Skip to content

Programmatic use

Dave Reynolds edited this page Aug 31, 2016 · 9 revisions

Part of the design goal for Sapi is to allow APIs to be constructed programmatically, giving us complete freedom to customize the API implementation.

While sapi2 allow us to define endpoints via external configuration files it is possible to combine programmatic and configured endpoints in a single application.

Processing model

Step Description Data structure
Request Request arrives as a GET or POST either at a custom or generic JAX RS endpoint. The requested URI and any path or query parameters are encapsulated in ... Request
Request embellishment Programmatic code can add new parameters or mark others as "consumed" Request
Call set up Determine the EndpointSpec instance that defines the query to run. Encapsulate the request, the endpoint specification and, optionally, a Velocity rendering template as ... Call
Obtain query builder The EndpointSpec delivers the query builder customized by the Request. In the case of a Describe query this will be the raw query builder with the ?id variable bound to the requested URI. In the case of a List query all of the RequestProcessors registered in the API will be run (by default these inject sorting, offset/limits and filters). QueryBuilder
Obtain query Further bindings and manipulations can be made to the query by the caller, eventually calling .build() to create a query Query
Execute query The configured datasource is queried using the instantiated Query returning ... ResultOrStream
Render The returned result or result stream is passed back as a JAX RS entity, marshallers take care of rendering to RDF, JSON, CSV and HTML. In the case of HTML the velocity template to use is found from the Request embedded in the results. ResultOrStream

Code paths

Typically a JAX RS resource class will inherit from EndpointsBase which provides convenience methods.

Default endpoint

Default handler which will describe the URI request or run a list endpoint defined by an externally configured specification with an explicit uri:

@Path("/{__path:.*}")
public class DefaultHandler extends EndpointsBase {

    @GET
    public Response handleDefault() {
        return defaultResponse();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response handleDefault(String body) {
        return defaultResponse(body);
    }
}

The strange name for the path pattern variable (__path) is to hide it from the parameter checks.

Simple programmatic endpoint

    @GET
    public Response getList() {
        Call call = getCall("endpoint");
        // custom preparation 
        return respondWith( call.getResults() );
    }

The call contains the request, the named endpoint (or the dynamically configured one that matches this URL if you don't given a name). This is enough to support custom processing of the query parameters and customization of the query by using call.getQueryBuilder(), manipulating the query builder and then inserting back using call.setQueryBuilder().

For full control then the expanded version of the above method body is:

    Request request = getRequest();
    // embelish request
    Call call = new Call(getAPI(), endpointName, request);
    QueryBuilder builder = call.getQueryBuilder();
    // additional query preparation here
    builder = call.finalizeQueryBuilder( builder );
    Query query = builder.build();
    ResultOrStream result = null;
    if (query instanceof ListQuery) {
        result = getAPI().getSource().query((ListQuery)query, this);
    } else {
        result = getAPI().getSource().query((ItemQuery)query, this);
    }
    return respondWith(result);