REST Components
Components providing a REST API are marked using the Component-level option enableJaxrs
. An application can contain more than one Component which exposes a REST API, but each Component will represent a separate API.
Component
This option enables the expansion of the REST API code using JAX-RS in the Component. This is required to generate all files required to create a REST API at the Component level. To generate APIs themselves for specific DataElements, those should be marked with the includeJaxrsConnector
option.
The value of the option represents the base path for the Component's API. The default path is /v1
, but by specifying a value in the option, a different path can be used. For example, /api/v2
could be used instead.
<options>
<enableJaxrs/>
</options>
Connectors
The REST API itself for a DataElement is defined in a connector class. This class implements methods for each individual REST endpoint which is available for the DataElement. The class is generated in the package api
, under the DataElement's package and named DataElementConnector
. Where DataElement
is the name of the DateElement.
The connector class itself is annotated with a @Path
annotation, which defines the relative path of the connector. This path is parsed from the option value supplied in includeJaxrsConnector
. Each endpoint method in the connector class is annotated with an annotation which indicates the HTTP method used to address the endpoint, as well as a relative path for the endpoint using the @Path
annotation (if any). The path of the endpoints is also parsed out of the includeJaxrsConnector
option value, where everything is ignored except for the trailing resource identifier in the value, which is added to the appropriate endpoints (GET for single resource, PUT, PATCH and DELETE). The value given in the individual endpoint options such as exposeGetEndpoint
is appended to this path as well, but typically this will rarely be needed.
DataElement
This option is added to a DataElement to indicate that a REST connector class should be expanded for it. The value of the option should be the path to the path for the DataElement. Common practice is to use the name of the element in its plural form.
DataElement TypeAttribute
is typically assigned the path /typeattributes
.
When this option is added to a DataElement, there must also be exactly one QuerySearch element linked to that DataElement, which has the option jaxrs.querySearch
.
Resource path definition
Each individual endpoint option can define its own path relative to the path of the connector. Some endpoints require a target resource, such as the GET
endpoint to retrieve a resource, but also the PATCH
, PUT
and DELETE
endpoints. The name of this resource identifier is typically part of the endpoint's own path. It is possible to add this identifier into the path of the connector. Doing so will indicate to the expanders that this identifier should be used for the path of all previously mentioned endpoints. This also indicates that regardless of its name, this identifier always represents the same thing in each of the respective endpoints.
DataElement TypeAttribute
is typically defined with includeJaxrsConnector: /typeattributes
. Its PUT
endpoint have the option exposePutEndpoint: /{typeAttributeId}
and its DELETE
endpoint the option exposeDeleteEndpoint: /{typeAttributeId}
. Though the name is the same, the expanders can not be sure that typeAttributeId
represents the same thing in both the PUT
and DELETE
endpoints. To indicate this, we define the options as follows: includeJaxrsConnector: /typeattributes/{typeAttributeId}
, exposePutEndpoint
without a value and exposeDeleteEndpoint
without a value.
When the resource path is defined as part of the connector, the connector's path will still be the path without the part containing the tailing resource identifier. What will additionally be generated includes the URL validation implementation and the query filter for the resource identifier.
Multiple path parameters
A path can contain multiple parameters. Excluding for a trailing identifier representing the resource path identifier, all parts of the path, including path variabels will be part of the connector path. These variables will also be added to all of the generated endpoints.
includeJaxrsConnector: /attributes/{attributeId}/typeattributes/{typeAttributeId}
will reserve /{typeAttributeId}
as the path for the resource endpoints and use /attributes/{attributeId}/typeattributes
as the path for the connector, while adding attributeId
as a parameter to all expanded endpoint methods.
<options>
<includeJaxrsConnector>/cities/{cityId}</includeJaxrsConnector>
</options>
<options>
<includeJaxrsConnector>/countries/{countryId}/cities/{cityId}</includeJaxrsConnector>
</options>
QuerySearch
This option marks a QuerySearch model element as the one that will be used by the REST implementation for the DataElement it is linked to.
<options>
<jaxrs.querySearch/>
</options>
DataElement
This option is added to a DataElement to define a name for the element in areas where the name is exposed to the outside world. This means in any externally exposed model and all API documentation.
The value for the option is formatted as Name_PluralName
. If the underscore and PluralName
is omitted, the Name
with a trialing s
is used as the plural.
The default when this option is not used is the name of the element for the singular form and the name with a trailing s
for the plural form.
<options>
<externalName>Starship</externalName>
</options>
<options>
<externalName>City_Cities</externalName>
</options>
Field
The goal of this option is to provide a default implementation when one wishes to expose a field in a DataElement directly as-is to through the REST API. This provides an implementation, which is fairly inflexible, as it is just a default implementation, but it does cover the majority of use-cases where one wishes to simple expose a field.
It is possible to pass the endpoints on which you wish this to apply if it should not be all of them as a comma-separated list.
<options>
<exposeRestField/>
</options>
<options>
<exposeRestField>GET,PATCH</exposeRestField>
</options>
GET List Endpoint
The GET
endpoint to retrieve a list is used to retrieve one or more records from the database. This endpoint supports pagination modeled after to the HAL specification. The data records themselves are retrieved and mapped to a DataElementOutputModel
class in the control
layer. These objects are then stored in a list in the embedded
field (_embedded
in JSON) of the PaginatedResponse<T>
class. This class also has a links
object and page
object. The links
object contains links dependent on the pagination model, such as a first
, next
, prev
, last
and self
. The page
object contains pagination data, such as the total number of elements (totalElements
), the number of pages (totalPages
), the current page (number
) and number of elements in the page (size
).
These endpoints can provide additional search functionality by adding query parameters, which should be passed into the DataElementQueryFilter
class, as it should affect the query in the DataElementQuerySearch
class.
DataElement
This option generates a GET
endpoint to retrieve multiple resource in the Connector class for the DataElement, as a
method with name get<DataElement>List()
.
The Option can be supplied with a subpath, that will be appended to the path of the includeJaxrsConnector
option.
<options>
<exposeGetListEndpoint/>
</options>
<options>
<exposeGetListEndpoint>/somesubpath</exposeGetListEndpoint>
</options>
Responses
If the request was successful, 200 OK
is returned along with a paginated response containing resources as an
application/hal+json
object. When something goes wrong, a 4xx
status is returned in case of a handled error or a
500
status is returned in case of a technical or uncaught error in the application. More information can be found on
the error handling page.
Query parameters
To filter elements, a parameter must be added to the custom-getList-parameters
anchor. This can be filtered on by
adding it to the QueryFilter object that is constructed to perform the query through the QuerySearch system. This can
also be automated by adding the option jaxrs.querySearch.exposeFilter to a field
in the Querysearch object used for the REST API on the element, which is marked with the option
jaxrs.querySearch.
The constraint that applies the filter to the database query must be manually added in the relevant QuerySearch class as to finish the implementation.
QueryFilterField
This option will add a parameter for the field to the GET
endpoint used to retrieve a list of resources, that is
generated using the exposeGetListEndpoint
option. This supports all basic types
with the exception of the DataRef
type added by the expanders for QuerySearch.
<options>
<jaxrs.querySearch.exposeFilter/>
</options>
To filter elements by name, add an optional query parameter in the custom-getList-parameters
anchor in the connector's getDateElementDetailsList()
method. It should be defined as @QueryParam("name") String name,
. In the DataElementQueryFilter
class, add the field private String name
with both accessor methods.
In the DataElementQuerySearch
class, add a constraint to the custom-constraints
anchor as:
, when(filter.getName() != null, () ->
equal("o.name", filter.getName(), SPLIT, CONTAINS, CASE_INSENSITIVE)
)
When the name
parameter is given, it will be used as a comma-separated list of string values that match the name
with case-insensitive contains logic.
Sorting
By default the GETlist endpoint has support for sorting results through the sortby
query parameter. The value for the parameter is sorted as follows:
field1[:order],field2[:order],...,fieldN[:order]
fieldX
is the name of the field, which is mapped in theSortFieldMapper
class for the DataElement. The mapping maps the given name to the name of the actual field in the DataElement. The name accepted by the API should typically match the name of a field that is returned in the data for clarity. If the optionexposeRestField
is used to expose a field, the mapping will be generated automatically.order
is an optional value that indicates how the data should be ordered. If provided, it should beasc
for ascending ordering ordesc
for descending ordering. The default if not provided is ascending.
sortby=name,type:asc,index:desc
Component
DataElement
CascadingThis option disables sorting for the GETlist endpoints if it is defined for the DataElement, as well as generation of the infrastructure for it on the DataElement level.
<options>
<jaxrs.endpoint.getList.sorting.disable/>
</options>
Default order
The QuerySearch instance which is used to retrieve values for the GET call will be assigned the option useDefaultOrder
implicitly. This option ensures that results are always sorted by the database identifier field to ensure consistent ordering across all GET operations. If other sorting is defined in the QuerySearch class itself or through the sortby
parameter, the ordering by the database identifier is applied after all other ordering is applied.
Additional configuration
dataElement
Sets the default page for GETlist requests. This marks the first page in the numbering of the pages. If set to 1 (default), the page numbering will start at 1, but if set to 0, the numbering will start at 0. This is also the number of the default apge that will be returned if no page number was specified.
<options>
<jaxrs.endpoint.getList.defaultPage>1</jaxrs.endpoint.getList.defaultPage>
</options>
dataElement
Sets the default page size for GETlist requests. This is the number of elements that will be returned if no size was specified in the call. By default this is set to 10.
<options>
<jaxrs.endpoint.getList.defaultPageSize>10</jaxrs.endpoint.getList.defaultPageSize>
</options>
dataElement
Sets the maximum page size for GETlist requests. This is the maximum number of elements that will be returned, regardless of the size was specified in the call. By default there is no maximum.
<options>
<jaxrs.endpoint.getList.maxPageSize>100</jaxrs.endpoint.getList.maxPageSize>
</options>
GET Resource Endpoint
The GET
endpoint to retrieve a resource based on a given valid unique identifier for this resource. Typically this identifier is a UUID. A resource is represented by a DataElementOutputModel
instance, which is mapped from an internal projection using a DataElementOutputMapper
. The mapper maps all fields from the internal representation onto fields in the output model. To represent DataRef
objects, every REST element also has a DataElementReferenceModel
and to map it a DataElementReferenceMapper
.
DataElement
This option generates a GET
endpoint to retrieve a resource in the Connector class for the DataElement, as a method
with name get<DataElement>Details()
.
The Option can be supplied with a subpath, that will be appended to the path of the includeJaxrsConnector
option.
Ideally the includeJaxrsConnector
option will supply the resource identifier for the path, so it can be matched with other endpoints.
This means that the preferred usage is without an option value and /{resourceId}
being part of the path defined in includeJaxrsConnector
.
<options>
<exposeGetEndpoint/>
</options>
<options>
<exposeGetEndpoint>/{resourceId}</exposeGetEndpoint>
</options>
Responses
If the request was successful, 200 OK
is returned along with the resource is returned as an application/json
object.
When something goes wrong, a 4xx
status is returned in case of a handled error or a 500
status is returned in case
of a technical or uncaught error in the application. More information can be found on the
error handling page.
Standard implementation
The REST expanders can generate a default implementation for the GET endpoint. Contrary to the regular implementation, this couples the expansion to the data model, when no custom implementation is needed. The first step to generate this implementation is by adding the resource identifier for the path to the includeJaxrsConnector
option. This means the option will have a value such as /dataelements/{dataElementId}
, rather than the /{dataElementId}
part of the path being provided in the exposeGetEndpoint
option. This indicates to the expanders that this identifier represents the same thing in every endpoint that takes a resource identifier. Also that this identifier uniquely represents a resource for this DataElement.
Every field that should be exposed as-is through the GET endpoint, should have the option exposeRestField
. This indicates to the expanders that this field does not require any custom mapping and can just be exposed directly. This works for both valuefields and linkfields. In case of a linkfield, the output of the endpoint will contain a reference object that represents the linked resource.
Additional configuration
DataElement
This option can be used to specify what projection should be returned from the data
layer to the GET
endpoints. The option defaults to jaxrs.querySearch.projection: details
as the projection used should contain at least all fields which are being exposed through the REST API.
<options>
<jaxrs.querySearch.projection/>
</options>
POST Endpoint
The POST
endpoint is used to create a new resource. When the endpoint is called, input data should be provided in JSON format, which is serialized to the DataElementPostInputModel
class. This in turn is mapped by the DataElementPostInputMapper
to the DataElementCommand.CreateDataElement
class.
To correctly generate this endpoint, you should always defined a command with the option jaxrs.command.create
, as this will be used to both transport the input data and execute the creation of the resource.
DataElement
This option generates a POST
endpoint in the Connector class for the DataElement, as a method with name create<DataElement>()
.
If a DataCommand with the option jaxrs.command.create
is defined, it will use that command to transport the data from the endpoint and execute the creation of resources.
The Option can be supplied with a subpath, that will be appended to the path of the includeJaxrsConnector
option.
<options>
<exposePostEndpoint/>
</options>
<options>
<exposePostEndpoint>/somesubpath</exposePostEndpoint>
</options>
Responses
If the request was successful, 201 Created
is returned along with a application/json
object containing the
identifier which represents the resource and a Location
header, containing the url to the resource. When something
goes wrong, a 4xx
status is returned in case of a handled error or a 500
status is returned in case of
a technical or uncaught error in the application. More information can be found on the
error handling page.
{
"id": "55889f12-5488-480e-a967-c8aaf395f4f5"
}
Standard implementation
The REST expanders can generate a default implementation for the POST endpoint. Most of the code for this endpoint is always generated, but given that fields have the option exposeRestField
and a field with the same name and type is present in the command, it will add these fields to the input model for the endpoint. One exception is that when the field is a linkfield, the field in the command should be of type String
, so a reference which should be the functional key, can be passed in the model. Code will be generated that will automatically resolve this reference.
When the enableValidation
option is active on the component, the REST expanders will also generate a DataElementPostInputValidator
class in the control
layer, which verifies the data given in the input model. These checks include checking that required data is present. It will also generate a DataElementCreateDataValidator
class in the logic
layer, which will check the data against the database, for checks such as exists and unique.
PUT Endpoint
The PUT
endpoint is used to update an existing resource. It should overwrite all fields of a resource if they are specified in the endpoint, even if they are not filled out in the request. When the endpoint is called, input data should be provided in JSON format, which is serialized to the DataElementPutInputModel
class. This in turn is mapped by the DataElementPutInputMapper
to the DataElementCommand.ModifyDataElement
class.
To correctly generate this endpoint, you should always defined a command with the option jaxrs.command.modify
, as this will be used to both transport the input data and execute the modification of the resource.
DataElement
This option generates a PUT
endpoint in the Connector class for the DataElement, as a method with name put<DataElement>()
.
If a DataCommand with the option jaxrs.command.modify
is defined, it will use that command to transport the data from the endpoint and execute the modification of resources.
The Option can be supplied with a subpath, that will be appended to the path of the includeJaxrsConnector
option.
Ideally the includeJaxrsConnector
option will supply the resource identifier for the path, so it can be matched with other endpoints.
This means that the preferred usage is without an option value and /{resourceId}
being part of the path defined in includeJaxrsConnector
.
<options>
<exposePutEndpoint/>
</options>
<options>
<exposePutEndpoint>/{resourceId}</exposePutEndpoint>
</options>
Responses
If the request was successful, 204 No Context
is returned and the resource will be modified. When something goes
wrong, a 4xx
status is returned in case of a handled error or a 500
status is returned in case of a technical or
uncaught error in the application. More information can be found on the error handling page.
Standard implementation
The REST expanders can generate a default implementation for the PUT endpoint. Most of the code for this endpoint is always generated, but given that fields have the option exposeRestField
and a field with the same name and type is present in the command, it will add these fields to the input model for the endpoint. One exception is that when the field is a linkfield, the field in the command should be of type String
, so a reference which should be the functional key, can be passed in the model. Code will be generated that will automatically resolve this reference.
When the enableValidation
option is active on the component, the REST expanders will also generate a DataElementPutInputValidator
class in the control
layer, which verifies the data given in the input model. These checks include checking that required data is present. It will also generate a DataElementModifyDataValidator
class in the logic
layer, which will check the data against the database, for checks such as exists and unique.
PATCH Endpoint
The PATCH
endpoint is used to update an existing resource. It will update all fields of a resource that are specified in the payload sent along with a request to the endpoint. When the endpoint is called, input data should be provided in JSON format, which is serialized to the DataElementPatchInputModel
class. This in turn is mapped by the DataElementPatchInputMapper
to the DataElementCommand.UpdateDataElement
class.
To correctly generate this endpoint, you should always defined a command with the option jaxrs.command.update
, as this will be used to both transport the input data and execute the update of the resource.
DataElement
This option generates a PATCH
endpoint in the Connector class for the DataElement, as a method with name patch<DataElement>()
.
If a DataCommand with the option jaxrs.command.update
is defined, it will use that command to transport the data from the endpoint and execute the updating of resources.
The Option can be supplied with a subpath, that will be appended to the path of the includeJaxrsConnector
option.
Ideally the includeJaxrsConnector
option will supply the resource identifier for the path, so it can be matched with other endpoints.
This means that the preferred usage is without an option value and /{resourceId}
being part of the path defined in includeJaxrsConnector
.
<options>
<exposePatchEndpoint/>
</options>
<options>
<exposePatchEndpoint>/{resourceId}</exposePatchEndpoint>
</options>
Responses
If the request was successful, 204 No Context
is returned and the resource will be updated. When something goes
wrong, a 4xx
status is returned in case of a handled error or a 500
status is returned in case of a technical or
uncaught error in the application. More information can be found on the error handling page.
Standard implementation
The REST expanders can generate a default implementation for the PATCH endpoint. Most of the code for this endpoint is always generated, but given that fields have the option exposeRestField
and a field with the same name and type is present in the command, it will add these fields to the input model for the endpoint. One exception is that when the field is a linkfield, the field in the command should be of type String
, so a reference which should be the functional key, can be passed in the model. Code will be generated that will automatically resolve this reference.
When the enableValidation
option is active on the component, the REST expanders will also generate a DataElementPatchInputValidator
class in the control
layer, which verifies the data given in the input model. These checks include checking that required data is present. It will also generate a DataElementUpdateDataValidator
class in the logic
layer, which will check the data against the database, for checks such as exists and unique.
DELETE Endpoint
The DELETE
endpoint is used to remove an existing resource. After successfully calling this endpoint with a valid resource identifier, the resource it represents will no longer exist.
To correctly generate this endpoint, you should always defined a command with the option jaxrs.command.remove
, as this will be used to both transport the resource identifier and execute the removal of the resource.
DataElement
This option generates a DELETE
endpoint in the Connector class for the DataElement, as a method with name delete<DataElement>()
.
If a DataCommand with the option jaxrs.command.remove
is defined, it will use that command to transport the data from the endpoint and execute the creation of resources.
The Option can be supplied with a subpath, that will be appended to the path of the includeJaxrsConnector
option.
Ideally the includeJaxrsConnector
option will supply the resource identifier for the path, so it can be matched with other endpoints.
This means that the preferred usage is without an option value and /{resourceId}
being part of the path defined in includeJaxrsConnector
.
<options>
<exposeDeleteEndpoint/>
</options>
<options>
<exposeDeleteEndpoint>/{resourceId}</exposeDeleteEndpoint>
</options>
Responses
If the request was successful, 204 No Context
is returned and the resource will be deleted. When something goes wrong,
a 4xx
status is returned in case of a handled error or a 500
status is returned in case of a technical or uncaught
error in the application. More information can be found on the error handling page.
Standard implementation
The REST expanders can generate a default implementation for the DELETE endpoint which will perform the delete operation when given a valid resource identifier.
File IO Endpoints
It is possible to expand endpoints for file IO operations (since rest-expanders
version 4.24.0
). This refers to file
uploads and downloads.
In this implementation we consider file resources to be a special case of a resource that can be linked to a regular api-accessible resource on the server. These links to files are represented by fields in a rest resource. It is possible to use both value- and linkfields to represent files.
As files are considered to be individual resources, they must be uploaded independently from the rest resources and then linked by reference on create or modify.
The file APIs take into account three different concepts that can be mixed freely:
- The endpoints to upload and/or download a file.
- The link between a regular resource and a file.
- The default implementation of the two previous items for specific fields.
Upload endpoints
Field
This option generates a POST
endpoint in the Connector class for the DataElement to upload a file in function of the
field it is applied to. The field will be marked with the tag #svcapi.endpoint.io.upload
to indicate that it has an
upload endpoint. This also implies that this field represents a file resource, for which the tag #svcapi.field.io
will be applied.
<options>
<svcapi.endpoint.upload/>
</options>
The option svcapi.endpoint.upload
is used to add an upload endpoint to a Field. The upload endpoint will accept the
incoming file and write it to a temporary location on the server. It is then read from that location to store it in its
final location. Apache Tika is used to try to identify the type of the file and the size is
also automatically determined. This information can then be used by a further implementation to store the file.
The path of an upload endpoint for a field is currently defined as ../<connectorBasePath>/upload/<fieldName>
.
The connectorBasePath
in this example is the connector path without the trailing path parameter.
When a file is successfully uploaded, a unique identifier is returned in the NS-File-Upload-Id
header. This identifier
can be used to link the file resource to a resource of the DataElement the Field belongs to.
Standard implementation
Currently a default implementation is available for links to assets:Asset
with the rest-assets-expanders
resource.
This will store the file as a new Asset and the functional key (file identifier) of that Asset is returned in the
NS-File-Upload-Id
header.
To activate the default implementation, the Field must have the option exposeRestField
without value or containing the value POST_FILE
alongside any other endpoints defined in the value.
Download endpoints
Field
This option generates a GET
endpoint in the Connector class for the DataElement to download a file linked to a field
in a specific DataElement resource. The field will be marked with the tag #svcapi.endpoint.io.download
to indicate
that it has a download endpoint. This also implies that this field represents a file resource, for which the tag
#svcapi.field.io
will be applied.
<options>
<svcapi.endpoint.download/>
</options>
The option svcapi.endpoint.download
is used to add a download endpoint to a Field. The download endpoint will transfer
a file linked to the field depending on the endpoint implementation. If no file is linked to the field, a
404 Not Found
error is returned by the API.
The path of an upload endpoint for a field is currently defined as ../<connectorPath>/download/<fieldName>
.
The connectorPath
in this example is the connector path without the trailing path parameter.
Standard implementation
Currently a default implementation is available for links to assets:Asset
with the rest-assets-expanders
resource.
This will retrieve the file from a linked asset and provide it as a download of the relevant content type as it was
stored for that asset.
To activate the default implementation, the Field must have the option exposeRestField
without value or containing the value GET_FILE
alongside any other endpoints defined in the value.
File resource links
Files can be represented by a ValueField or a LinkField, depending on the implementation. When a field represents a
file, the tag #svcapi.field.io
will be applied to the field. This can be provided by a specific implementation and it
is also added when an upload or download endpoint is added to the field.
A file reference is a JSON object containing up to three properties:
resourceUri
: An optional property that is only present when a download endpoint exists for the resource, with the specific uri to the endpoint that downloads the references resource.identifier
: A unique identifier that can be used to identify the file in (at minimum) the context of this field.fileName
: An optional property that contains the name of the file if provided by the implementation.
In the following example resource, birthCertificate
represents a file resource.
{
"name": "Snickers",
"uuid": "f9020d14-b1ab-4de3-99a2-f0a2e5a8c71d",
"birthCertificate": {
"resourceUri": "http://localhost:8080/restexample/v1/cats/f9020d14-b1ab-4de3-99a2-f0a2e5a8c71d/download/birthCertificate",
"identifier": "d8227d40-4e8b-4b7f-baf4-52bb8a051e26",
"fileName": "snickers-haz-cheeseburger.jpeg"
},
"breed": {
"resourceUri": "http://localhost:8080/restexample/v1/catbreeds/Maine%20Coon",
"name": "Maine Coon"
}
}