package com.mgreau.gae.booking;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.annotation.Nullable;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.response.CollectionResponse;
import com.google.appengine.api.datastore.Cursor;
import com.google.appengine.api.search.Document;
import com.google.appengine.api.search.Field;
import com.google.appengine.api.search.Index;
import com.google.appengine.api.search.IndexSpec;
import com.google.appengine.api.search.Results;
import com.google.appengine.api.search.ScoredDocument;
import com.google.appengine.api.search.SearchServiceFactory;
import com.google.appengine.datanucleus.query.JPACursorHelper;
@Api(name = "bookingendpoint", clientIds = { Ids.CLIENT_ID }, audiences = { Ids.AUDIENCE })
public class HotelEndpoint {
static final Index INDEX = getIndex();
private static Index getIndex() {
IndexSpec indexSpec = IndexSpec.newBuilder().setName("hotelindex")
.build();
return SearchServiceFactory.getSearchService().getIndex(indexSpec);
}
/**
* This method lists all the entities inserted in datastore. It uses HTTP
* GET method.
*
* @return List of all entities persisted.
*/
@SuppressWarnings({ "cast", "unchecked" })
@ApiMethod(name = "hotels.list")
public CollectionResponse<Hotel> listHotel(
@Nullable @Named("cursor") String cursorString,
@Nullable @Named("limit") Integer limit) {
EntityManager mgr = getEntityManager();
List<Hotel> result = null;
Cursor cursor = null;
// To check if its the latest cursor
String backupCursor = cursorString;
try {
Query query = mgr.createQuery("select from Hotel as Hotel");
if (cursorString != null && cursorString != "") {
cursor = Cursor.fromWebSafeString(cursorString);
query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
if (limit != null) {
query.setMaxResults(limit);
}
result = (List<Hotel>) query.getResultList();
cursor = JPACursorHelper.getCursor(result);
if (cursor != null && result != null && limit != null
&& result.size() == limit)
cursorString = cursor.toWebSafeString();
else
cursorString = "";
if (cursorString != null && backupCursor != null
&& backupCursor.equals(cursorString)) {
cursorString = "";
}
// Tight loop for fetching all entities from datastore and
// accomodate
// for lazy fetch.
for (@SuppressWarnings("unused")
Hotel h : result)
;
} finally {
mgr.close();
}
return CollectionResponse.<Hotel> builder().setItems(result)
.setNextPageToken(cursorString).build();
}
/**
* This method gets the entity having primary key id. It uses HTTP GET
* method.
*
* @param id
* the primary key of the java bean.
* @return The entity with primary key id.
*/
@ApiMethod(name = "hotels.get")
public Hotel getHotel(@Named("id") Long id) {
EntityManager mgr = getEntityManager();
Hotel hotel = null;
try {
hotel = mgr.find(Hotel.class, id);
} finally {
mgr.close();
}
return hotel;
}
/**
* This inserts the entity into App Engine datastore. It uses HTTP POST
* method.
*
* @param hotel
* the entity to be inserted.
* @return The inserted entity.
*/
@ApiMethod(name = "hotels.insert")
public Hotel insertHotel(Hotel hotel) {
EntityManager mgr = getEntityManager();
try {
mgr.persist(hotel);
} finally {
mgr.close();
}
addHotelToSearchIndex(hotel);
return hotel;
}
/**
* This method is used for updating a entity. It uses HTTP PUT method.
*
* @param hotel
* the entity to be updated.
* @return The updated entity.
*/
@ApiMethod(name = "hotels.update")
public Hotel updateHotel(Hotel hotel) {
EntityManager mgr = getEntityManager();
try {
mgr.persist(hotel);
} finally {
mgr.close();
}
addHotelToSearchIndex(hotel);
return hotel;
}
/**
* This method removes the entity with primary key id. It uses HTTP DELETE
* method.
*
* @param id
* the primary key of the entity to be deleted.
* @return The deleted entity.
*/
@ApiMethod(name = "hotels.delete")
public Hotel removeHotel(@Named("id") Long id) {
EntityManager mgr = getEntityManager();
Hotel hotel = null;
try {
hotel = mgr.find(Hotel.class, id);
mgr.remove(hotel);
} finally {
mgr.close();
}
return hotel;
}
private static EntityManager getEntityManager() {
return EMF.get().createEntityManager();
}
@ApiMethod(httpMethod = "GET", name = "hotels.search")
public List<Hotel> searchHotel(@Named("term") String queryString) {
List<Hotel> hotelList = new ArrayList<Hotel>();
Results<ScoredDocument> results = INDEX.search(queryString);
for (ScoredDocument scoredDoc : results) {
Field f = scoredDoc.getOnlyField("id");
if (f == null || f.getText() == null)
continue;
long hotelId = Long.parseLong(f.getText());
if (hotelId != -1) {
Hotel b = getHotel(hotelId);
hotelList.add(b);
}
}
return hotelList;
}
private static void addHotelToSearchIndex(Hotel h) {
Document.Builder docBuilder = Document
.newBuilder()
.addField(
Field.newBuilder()
.setName("name")
.setText(
h.getHotelName() != null ? h
.getHotelName() : ""))
.addField(
Field.newBuilder().setName("id")
.setText(Long.toString(h.getId())))
.addField(
Field.newBuilder()
.setName("country")
.setText(
h.getCountry() != null ? h.getCountry()
: ""))
.addField(
Field.newBuilder()
.setName("price")
.setNumber(
h.getPrice() != null ? h.getPrice() : 0))
.addField(
Field.newBuilder()
.setName("city")
.setText(h.getCity() != null ? h.getCity() : ""))
.addField(
Field.newBuilder()
.setName("numberOfStars")
.setNumber(
h.getNumberOfStars() != null ? h
.getNumberOfStars() : 0))
.addField(
Field.newBuilder().setName("published")
.setDate(Field.date(new Date())));
docBuilder.setId(Long.toString(h.getId()));
Document doc = docBuilder.build();
INDEX.put(doc);
}
}