cube108
cube108

Reputation: 121

Generate Client Libraries fails if ClassnameEndpoint.java has 'Multiple methods with same rest path "GET classname": etc

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

Answers (2)

Sanket Berde
Sanket Berde

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

willlma
willlma

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

Related Questions