public interface OpenapiSupport
RESTHandler servlet
, the Smart GWT server can
also generate
a standard OpenAPI
specification
of the REST interface supported by RestHandler
. This allows any client system
that supports OpenAPI to access the operations supported by your DataSources. Because details
like field types
and validators
) are automatically
translated to OpenAPI, the OpenAPI specification (OAS) of your DataSource operations is much
more specific and detailed than the general RestDataSource
protocol spec, and
can allow automatically generated UIs or automatically generated communication stubs to be
much richer and easier to use.
Very often, a reasonably simple DataSource expresses more than can be easily translated to
the current OpenAPI specification. In such cases, efforts are made to use the OpenAPI
extensions
mechanism to provide that level of detail. Validators are one common area of interest.
It is worthwhile to inspect the raw YAML output at least once before relying solely on
visual tooling, unless said tooling
supports vendor extensions. ReDoc,
for example, is able to render the generated spec well, and in fact powers the example
specification (see link below), but leaves out important details found in the extensions,
like the RESTHandler's JSON prefix
and
suffix
.
<filter> <filter-name>cross-origin</filter-name> <filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class> </filter> <filter-mapping> <filter-name>cross-origin</filter-name> <url-pattern>/restapi/*</url-pattern> </filter-mapping>allowing the Swagger Editor to access the YAML generated for you by Smart GWT, through the use of its
url
query parameter. e.g.,
http://editor.swagger.io/?url=http://localhost:8080/api/Customer.yaml
openapi.yaml
resource.
For example, if your servlet is configured to respond to requests at /restapi
<servlet-mapping> <servlet-name>RESTHandler</servlet-name> <url-pattern>/restapi/</url-pattern> </servlet-mapping>then a GET request to
/restapi/openapi.yaml
will yield the automatically
generated documentation, based on your application's DataSource (ds.xml) configurations.
This specification is generated dynamically, so that each new request for the specification
includes any changes made to new or updated DataSources since the last request.
Alternatively, save the file to disk, with modifications if desired, and serve it statically
from another location if that's more in line with your requirement.
openapi.yaml
by default includes a reference to every path exposed
by every DataSource.ds.xml file found in your project. The paths exposed by your DataSource
are determined by the rules documented in the
RESTHandler servlet's
SimplifiedREST protocol.
For an Order
DataSource with only default operationBindings, these references would look something like
the following:
paths: /: post: summary: DataSource-agnostic POSTMessage protocol # and so on... remainder clipped for brevity /RESTDataSource/Order/fetch: $ref: Order.yaml#/paths/~1RESTDataSource~1Order~1fetch /RESTDataSource/Order/add: $ref: Order.yaml#/paths/~1RESTDataSource~1Order~1add /RESTDataSource/Order/update: $ref: Order.yaml#/paths/~1RESTDataSource~1Order~1update /RESTDataSource/Order/remove: $ref: Order.yaml#/paths/~1RESTDataSource~1Order~1remove /RESTDataSource/Order/batch: $ref: Order.yaml#/paths/~1RESTDataSource~1Order~1batch /Order: $ref: Order.yaml#/paths/~1Order /Order/{orderId}: $ref: Order.yaml#/paths/~1Order~1%7BorderId%7DThe first path in the above listing documents RESTHandler's singular AdvancedREST endpoint, described by the
RESTDataSource's
postMessage
protocol and found in
our example configuration at /restapi/
. As documented elsewhere, this should
normally be the endpoint preferred by all but the simplest of integrations.
On the other hand, it is also the endpoint most difficult to document effectively,
due in part to a handful of restrictions found in the OAS 3.x specification. One such
restriction is documented in an open issue around
request/response correlation,
which in short points out that there is no good way to correlate a variation of some request
to the matching variation on the response. In the case of the AdvancedREST endpoint,
of course, request and response formats both depend entirely on the values provided in
dataSource
, operationType
, and operationId
arguments.
Ideally, we could document the inputs and outputs of a resource like
/restapi/?dataSource=Order&operationType=fetch&operationId=fetchByCustomer
separately from one like
/restapi/?dataSource=Order&operationType=fetch&operationId=fetchByUser
,
but this is expressly
disallowed.
In the absence of any spec-compliant mechanism like
supporting interdependencies between query parameters
then, we also provide simplified variations of the AdvancedREST endpoint on each
DataSource's specification, seen alongside the other SimplifiedREST endpoints with the
RESTDataSource
path. These 'SimplifiedPOST' endpoints do allow for Criteria
& AdvancedCriteria, as well as batching of multiple operations against the same
DataSource.
You can easily view all of the operations for a single DataSource by making the request to
{id}.yaml
instead of openapi.yaml, where {id}
is the ID of any
DataSource in your project. Building on the examples above, a request for
/restapi/Order.yaml
would include every path except '/'. Again, prefer
the RestHandler's AdvancedREST endpoint to SimplifiedREST, with the caveat that
SimplifiedREST documentation my be more explicit until a future version of the OAS spec
addresses some of the issues discussed here.
A full example specification is too lengthy to include in documentation, so it is left to
the reader to experiment with their own DataSources (sample DataSources are included with
the SDK and in Maven archetypes
or with the
example specification
bundled in the SDK (link below), using this documentation as guidance. Most of what is
generated for you can be pretty easily traced back to your DataSource -
description
, field names,
required/optional attributes, and
type mappings are all pretty straightforward, and the rest of it really should work the
way you might expect it to. A few specific examples include:
description
, complete with
i18n translations
as applicable, when a
locale
can be derived from the servlet request or is specified explicitly with a 'locale'
query parameter (e.g., ?locale=es).
Declarative
Security
rules are found
(rules themselves are not disclosed, by design).
criteria
,
description
,
outputs
, etc. are respected.
<DataSource serverType="sql" schema="PUBLIC" dbName="ClassicModels" ID="Order" tableName="orders"> <serverObject className="com.example.classicmodels.OrderOperations" /> <fields> <field name="orderNumber" type="sequence" primaryKey="true" /> <field name="orderDate" type="date" required="true" /> <field name="requiredDate" type="date" required="true" /> <field name="shippedDate" type="date" /> <field name="status" type="enum" length="15" required="true"> <valueMap> <value>In Process</value> <value>Shipped</value> <value>Cancelled</value> <value>Disputed</value> <value>Resolved</value> <value>On Hold</value> </valueMap> </field> <field name="comments" type="text" length="16777216" /> <field name="customerNumber" title="Customer" type="integer" required="true" foreignKey="Customer.customerNumber" displayField="customerName" /> <field name="customerName" includeFrom="Customer.customerName" hidden="true" /> </fields> <operationBindings> <binding operationType="add" operationId="addWithDiscountCode" serverMethod="addWithDiscountCode" /> <binding operationType="add" operationId="addWithManualAdjustment" serverMethod="addWithManualAdjustment" /> </operationBindings> </DataSource>
/Order: $ref: Order.yaml#/paths/~1Order /Order/{orderId}: $ref: Order.yaml#/paths/~1Order~1%7BorderId%7D /Order/add/addWithDiscountCode: $ref: Order.yaml#/paths/~1Order~1add~1addWithDiscountCode /Order/add/addWithManualAdjustment: $ref: Order.yaml#/paths/~1Order~1add~1addWithManualAdjustment
The simplest means for this kind of minimal customization is through
server.properties
configuration. Example
values for supported properties include:
openapi.info.version: 1.0.0 openapi.info.title: New Application openapi.info.description: A short description of New Application ## default value derived from servlet context & httpRequest openapi.servers.servletUrl: http://localhost:8080/restapiYou may also use configuration to control which DataSources are exposed to your specification. Three strategies are supported:
<DataSource ID="Order" tableName="orders" apidoc="false">
openapi.ds.blacklisted: Order, OrderDetail
openapi.ds.whitelisted: Order, OrderDetail
openapi.ds.dynamics: DYN_Environment, DYN_Status
request.setAttribute("openapi.ds.dynamics", "DynamicOrder$Foo_0123, DynamicOrderItem$Foo_0123,");Finally, a DataSource is automatically excluded from the specification under the following circumstances:
<binding apidoc="false" operationType="add" operationId="addWithDiscountCode" serverMethod="addWithDiscountCode" />
allowMultiUpdate
com.isomorphic.openapi
package of the isomorphic-core-rpc module. Any of these
templates may be overridden by placing a copy in a location known to the RESTHandler servlet
(again, refer to server javadoc), but this kind of thing should normally be considered the
last course of action.