Reputation: 121
after generating Endpoint from a model class I edit the ClassnameEndpoint file to make a method that will return a list of Entities using a modified SELECT string.
Somehow this makes a clash and Generate Client Libraries fails like this:
INFO: Successfully processed C:\Users\1177\AndroidStudioProjects\N5\appN5-AppEngine\target/generated-sources/appengine-endpoints\WEB-INF/appengine-web.xml [ERROR] com.google.api.server.spi.config.validation.ApiConfigInvalidException: Multiple methods with same rest path "GET eventdata": "listUserEventData" and "listEventData"
here is the ClassnameEndpoint.java, which is edited by hand after being generated:
note listEventData and listUserEventData are identical except for SELECT string...
So how can I create an endpoint method to get ALL the entities and another method to get SOME entities without the clash?
package sic.example.appn5;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiNamespace;
import com.google.api.server.spi.response.CollectionResponse;
import com.google.appengine.api.datastore.Cursor;
import com.google.appengine.datanucleus.query.JPACursorHelper;
import java.util.List;
import javax.annotation.Nullable;
import javax.inject.Named;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityNotFoundException;
import javax.persistence.EntityManager;
import javax.persistence.Query;
@Api(name = "eventdataendpoint", namespace = @ApiNamespace(ownerDomain = "example.sic", ownerName = "example.sic", packagePath = "appn5"))
public class EventDataEndpoint {
/**
* This method lists all the entities inserted in datastore.
* It uses HTTP GET method and paging support.
*
* @return A CollectionResponse class containing the list of all entities
* persisted and a cursor to the next page.
*/
@SuppressWarnings({"unchecked", "unused"})
@ApiMethod(name = "listEventData")
public CollectionResponse<EventData> listEventData(
@Nullable @Named("cursor") String cursorString,
@Nullable @Named("limit") Integer limit) {
EntityManager mgr = null;
List<EventData> execute = null;
try {
mgr = getEntityManager();
Query query = mgr.createQuery("select from EventData as EventData");
Cursor cursor;
if (cursorString != null && cursorString.trim().length() > 0) {
cursor = Cursor.fromWebSafeString(cursorString);
query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
if (limit != null) {
query.setFirstResult(0);
query.setMaxResults(limit);
}
execute = (List<EventData>) query.getResultList();
cursor = JPACursorHelper.getCursor(execute);
if (cursor != null) cursorString = cursor.toWebSafeString();
// Tight loop for fetching all entities from datastore and accomodate
// for lazy fetch.
for (EventData obj : execute) ;
} finally {
if (mgr != null) {
mgr.close();
}
}
return CollectionResponse.<EventData>builder()
.setItems(execute)
.setNextPageToken(cursorString)
.build();
}
@SuppressWarnings({"unchecked", "unused"})
@ApiMethod(name = "listUserEventData")
public CollectionResponse<EventData> listUserEventData(
@Nullable @Named("username") String username,
@Nullable @Named("cursor") String cursorString,
@Nullable @Named("limit") Integer limit) {
EntityManager mgr = null;
List<EventData> execute = null;
try {
mgr = getEntityManager();
Query query = mgr.createQuery(String.format("select from EventData as EventData where BelongsTo = '%s'", username));
Cursor cursor;
if (cursorString != null && cursorString.trim().length() > 0) {
cursor = Cursor.fromWebSafeString(cursorString);
query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
if (limit != null) {
query.setFirstResult(0);
query.setMaxResults(limit);
}
execute = (List<EventData>) query.getResultList();
cursor = JPACursorHelper.getCursor(execute);
if (cursor != null) cursorString = cursor.toWebSafeString();
// Tight loop for fetching all entities from datastore and accomodate
// for lazy fetch.
for (EventData obj : execute) ;
} finally {
if (mgr != null) {
mgr.close();
}
}
return CollectionResponse.<EventData>builder()
.setItems(execute)
.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 = "getEventData")
public EventData getEventData(@Named("id") String id) {
EntityManager mgr = getEntityManager();
EventData eventData = null;
try {
eventData = mgr.find(EventData.class, id);
} finally {
mgr.close();
}
return eventData;
}
/**
* This inserts a new entity into App Engine datastore. If the entity already
* exists in the datastore, an exception is thrown.
* It uses HTTP POST method.
*
* @param eventData the entity to be inserted.
* @return The inserted entity.
*/
@ApiMethod(name = "insertEventData")
public EventData insertEventData(EventData eventData) {
EntityManager mgr = getEntityManager();
try {
if (containsEventData(eventData)) {
throw new EntityExistsException("Object already exists");
}
mgr.persist(eventData);
} finally {
mgr.close();
}
return eventData;
}
/**
* This method is used for updating an existing entity. If the entity does not
* exist in the datastore, an exception is thrown.
* It uses HTTP PUT method.
*
* @param eventData the entity to be updated.
* @return The updated entity.
*/
@ApiMethod(name = "updateEventData")
public EventData updateEventData(EventData eventData) {
EntityManager mgr = getEntityManager();
try {
if (!containsEventData(eventData)) {
throw new EntityNotFoundException("Object does not exist");
}
mgr.persist(eventData);
} finally {
mgr.close();
}
return eventData;
}
/**
* 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 = "removeEventData")
public EventData removeEventData(@Named("id") String id) {
EntityManager mgr = getEntityManager();
EventData eventData = null;
try {
eventData = mgr.find(EventData.class, id);
mgr.remove(eventData);
} finally {
mgr.close();
}
return eventData;
}
private boolean containsEventData(EventData eventData) {
EntityManager mgr = getEntityManager();
boolean contains = true;
try {
EventData item = mgr.find(EventData.class, eventData.getEventKey());
if (item == null) {
contains = false;
}
} finally {
mgr.close();
}
return contains;
}
private static EntityManager getEntityManager() {
return EMF.get().createEntityManager();
}
}
Upvotes: 0
Views: 1632
Reputation: 6895
The Answer given by @willlma is definately right ! But a very simple way of fixing this temporarily and avoid a lot of code change is to add another named parameter to be passed in the method.
public Event getFirstEvent(@Named("mainEventId") Long mainEventId,
@Named("useless") Boolean useless ,
User auth) throws UnauthorizedException {
if (auth!=null){
...
return event
} else throw new UnauthorizedException("Please authenticate first.");
}
public Event getEvent(@Named("eventID") Long eventID, User auth) throws UnauthorizedException {
if (auth != null) {
...
return event;
} else throw new UnauthorizedException("Please authenticate first.");
}
Upvotes: 0
Reputation: 7543
The end of the automatically generated path looks like this: _ah/api/endpoint_name/version_name/return_type/{named_parameter_1}/{named_parameter_2}/…
The problem you're facing is that both methods are in the same endpoint class, of the same version, have the same return type, and use GET
. Therefore, the URLs will be identical and conflicting. In this case, they're both _ah/api/eventdataendpoint/v1/eventdata
.
The solution is to add the path
attribute to one of the classes, like so:
@ApiMethod(name = "listUserEventData", path="eventdata/user")
public CollectionResponse<EventData> listUserEventData(…
Now, the first method with have URL _ah/api/eventdataendpoint/v1/eventdata
and the second one will have path _ah/api/eventdataendpoint/v1/eventdata/user
.
As you increase the number of methods in your endpoint, you will come across conflicts like this a lot, so if you plan on making many new methods, it's a good idea to use the path
attribute every time rather than rely on CE to auto-generate a path.
Edit: you'll find a lot of info pertaining to your API and each method's path at YOUR_APP_ID.appspot.com/_ah/api/discovery/v1/apis/ENDPOINT_NAME/v1/rest
Upvotes: 8