Saturday, September 28, 2013

Dynamic REST Client & Controller Generation


Here is the example from SBE REST Modules on GitHub.

It let's you create an interface and put your Spring MVC REST annotations on it if you follow certain patterns. The REST client and controller can be generated using Spring beans. It's expected that your controllers would just delegate to a service layer, so the interface to that service can be registered using @RestResource on the class. The base URI and default response class are also defined here. By default, a method with @RequestMapping will expect the same method and method signature to be available to delegate to in the service class.

Another feature is to turn off creating a relative URI using the class' path, is to specify @RestRequestResource on a method and setting 'relative' to false. If the interface overloads a method to perform further conversion on the results, the method name can be specified. The framework still expects the method signatures to match. A converter can be specified to change the result before it's sent to the client. A good example of this is creating smaller models to match different needs without doing further customizations on the backend services or queries.

This all needs more work, but all of the basics are working now. I'll write this up soon on Spring by Example.



public interface PersistenceFindMarshallingService<R extends EntityResponseResult, FR extends EntityFindResponseResult> {

     public final static String PATH_DELIM = "/";
     public final static String PARAM_DELIM = "?";
     public final static String PARAM_VALUE_DELIM = "&";

     public final static String ID_VAR = "id";
     public final static String PAGE_VAR = "page";
     public final static String PAGE_SIZE_VAR = "page-size";

     public final static String PAGE_PATH = PATH_DELIM + PAGE_VAR;
     public final static String PAGE_SIZE_PATH = PATH_DELIM + PAGE_SIZE_VAR;
     public final static String PAGINATED = PAGE_PATH + PATH_DELIM + "{" + PAGE_VAR + "}" + PAGE_SIZE_PATH + PATH_DELIM + "{" + PAGE_SIZE_VAR + "}";

     public final static String ROOT_URI = PATH_DELIM;
     public final static String FIND_BY_ID_URI = PATH_DELIM + "{" + ID_VAR + "}";

     /**
      * Find by primary key.
      */
     @RequestMapping(value = FIND_BY_ID_URI, method = RequestMethod.GET)
     public R findById(@PathVariable(ID_VAR) Integer id);

     /**
      * Find a paginated record set.
      */
     @RequestMapping(value = PAGINATED, method = RequestMethod.GET)
     public FR find(@PathVariable(PAGE_VAR) int page, @PathVariable(PAGE_SIZE_VAR) int pageSize);

     /**
      * Find all records.
      */
     @RequestMapping(value = ROOT_URI, method = RequestMethod.GET)
     public FR find();

}


public interface PersistenceMarshallingService<R extends EntityResponseResult, FR extends EntityFindResponseResult, S extends PkEntityBase>
         extends PersistenceFindMarshallingService<R, FR> {

     public final static String DELETE_URI = ROOT_URI + "remove";

     /**
      * Save record.
      */
     @RequestMapping(value = ROOT_URI, method = RequestMethod.POST)
     public R create(@RequestBody S request);

     /**
      * Update record.
      */
     @RequestMapping(value = ROOT_URI, method = RequestMethod.PUT)
     public R update(@RequestBody S request);

     /**
      * Delete record.
      */
     // FIXME: server has marshalling error if DELETE
     @RequestMapping(value = DELETE_URI, method = RequestMethod.PUT)
     public R delete(@RequestBody S request);

}



@RestResource(service=ContactService.class, path=PATH, responseClass=PersonResponse.class)
public interface ContactMarshallingService extends PersistenceMarshallingService<PersonResponse, PersonFindResponse, Person> {

     final static String PATH = "/person-test";
     final static String SMALL_URI = "/small";
     final static String SMALL_PATH = "/small-person";

     public final static String SMALL_FIND_BY_ID_REQUEST = SMALL_PATH + PATH_DELIM + "{" + ID_VAR + "}";

     public final static String SMALL_FIND_PAGINATED_REQUEST = SMALL_PATH + PAGINATED;

     public final static String LAST_NAME_VAR = "lastName";
     public final static String LAST_NAME_PARAMS = PARAM_DELIM + LAST_NAME_VAR + "={" + LAST_NAME_VAR + "}";
     public final static String FIND_BY_LAST_NAME_CLIENT_REQUEST = PATH + LAST_NAME_PARAMS;
     public final static String SMALL_FIND_BY_LAST_NAME_CLIENT_REQUEST = PATH + SMALL_URI + LAST_NAME_PARAMS;

     @RequestMapping(value = SMALL_FIND_BY_ID_REQUEST, method = RequestMethod.GET)
     @RestRequestResource(relative=false, methodName="findById", converter=SmallContactConverter.class)
     public PersonResponse smallFindById(@PathVariable(ID_VAR) Integer id);

     @RequestMapping(value=SMALL_FIND_PAGINATED_REQUEST, method = RequestMethod.GET)
     @RestRequestResource(relative=false, methodName="find", converter=SmallContactConverter.class)
     public PersonFindResponse smallFind(@PathVariable(PAGE_VAR) int page, @PathVariable(PAGE_SIZE_VAR) int pageSize);

     @RequestMapping(value = PATH_DELIM, method = RequestMethod.GET, params= { LAST_NAME_VAR })
     public PersonFindResponse findByLastName(@RequestParam(LAST_NAME_VAR) String lastName);

     @RequestMapping(value = SMALL_URI, method = RequestMethod.GET, params= { LAST_NAME_VAR })
     @RestRequestResource(methodName="findByLastName", converter=SmallContactConverter.class)
     public PersonFindResponse smallFindByLastName(@RequestParam(LAST_NAME_VAR) String lastName);

}


No comments: