Swagger

Overview

Elide supports the generation of Swagger documentation from Elide annotated beans. Specifically, it generates a JSON document conforming to the swagger specification that can be used by tools like Swagger UI (among others) to explore, understand, and compose queries against your Elide API.

Only JSON-API endpoints are documented. The GraphQL API schema can be explored directly with tools like Graphiql.

Features Supported

  • JaxRS & Spring Endpoint - Elide ships with a customizable JaxRS and Spring endpoints that can publish one or more swagger documents.
  • Path Discovery - Given a set of entities to explore, Elide will generate the minimum, cycle-free, de-duplicated set of URL paths in the swagger document.
  • Filter by Primitive Attributes - All GET requests on entity collections include filter parameters for each primitive attribute.
  • Prune Fields - All GET requests support JSON-API sparse fields query parameter.
  • Include Top Level Relationships - All GET requests support the ability to include direct relationships.
  • Sort by Attribute - All GET requests support sort query parameters.
  • Pagination - All GET requests support pagination query parameters.
  • Permission Exposition - Elide permissions are exported as documentation for entity schemas.
  • Model & Attribute Properties - The required, readOnly, example and value fields are extracted from ApiModelProperty annotations. The description field can be extracted from the ApiModel annotation.
  • API Version Support - Elide can create separate documents for different API versions.

Getting Started

Maven

If you are not using Elide Spring Starter or Elide Standalone (which package swagger as a dependency), you will need to pull in the following elide dependencies :

<dependency>
  <groupId>com.yahoo.elide</groupId>
  <artifactId>elide-swagger</artifactId>
</dependency>

<dependency>
  <groupId>com.yahoo.elide</groupId>
  <artifactId>elide-core</artifactId>
</dependency>

Pull in swagger core :

<dependency>
  <groupId>io.swagger</groupId>
  <artifactId>swagger-core</artifactId>
</dependency>

Spring Boot Configuration

If you are using Elide Spring Autoconfigure, you can override the Swagger bean:

    @Bean
    public Swagger buildSwagger(EntityDictionary dictionary, ElideConfigProperties settings) {
        Info info = new Info()
                .title(settings.getSwagger().getName())
                .version(settings.getSwagger().getVersion());

        SwaggerBuilder builder = new SwaggerBuilder(dictionary, info);

        Swagger swagger = builder.build().basePath(settings.getJsonApi().getPath());

        return swagger;
    }

The application yaml file has settings:

  • to enable the swagger document endpoint
  • to set the URL path for the swagger document endpoint
  • to set the API version number
elide:
  swagger:
    path: /doc
    enabled: true
    version: "1.0"

Elide Standalone Configuration

If you are using Elide Standalone, you can extend ElideStandaloneSettings to:

  • Enable Swagger.
  • Configure the URL Path for the swagger document.
  • Configure the Swagger document version.
  • Configure the service name.
  • Construct swagger documents for your service.
    /**
     * Enable swagger documentation.
     * @return whether Swagger is enabled;
     */
    @Override
    public boolean enableSwagger() {
        return false;
    }

    /**
     * API root path specification for the Swagger endpoint. Namely, this is the root uri for Swagger docs.
     *
     * @return Default: /swagger/*
     */
    @Override
    public String getSwaggerPathSpec() {
        return "/swagger/*";
    }

    /**
     * Swagger documentation requires an API version.
     * The models with the same version are included.
     * @return swagger version;
     */
    @Override
    public String getSwaggerVersion() {
        return NO_VERSION;
    }

    /**
     * Swagger documentation requires an API name.
     * @return swagger service name;
     */
    @Override
    public String getSwaggerName() {
        return "Elide Service";
    }

    /**
     * Creates a singular swagger document for JSON-API.
     * @param dictionary Contains the static metadata about Elide models. .
     * @return list of swagger registration objects.
     */
    @Override
    public List<DocEndpoint.SwaggerRegistration> buildSwagger(EntityDictionary dictionary) {
        Info info = new Info()
                .title(getSwaggerName())
                .version(getSwaggerVersion());

        SwaggerBuilder builder = new SwaggerBuilder(dictionary, info);

        String moduleBasePath = getJsonApiPathSpec().replaceAll("/\\*", "");

        Swagger swagger = builder.build().basePath(moduleBasePath);

        List<DocEndpoint.SwaggerRegistration> docs = new ArrayList<>();
        docs.add(new DocEndpoint.SwaggerRegistration("test", swagger));

        return docs;
    }

Elide Library Configuration

If you are using Elide directly as a library (and not using Elide Standalone), follow these instructions:

Create and initialize an entity dictionary.

EntityDictionary dictionary = new EntityDictionary(Maps.newHashMap());

dictionary.bindEntity(Book.class);
dictionary.bindEntity(Author.class);
dictionary.bindEntity(Publisher.class);

Create a swagger info object.

Info info = new Info().title("My Service").version("1.0");

Initialize a swagger builder.

SwaggerBuilder builder = new SwaggerBuilder(dictionary, info);

Build the swagger document

Swagger document = builder.build();

Convert Swagger to JSON

You can directly convert to JSON:

String jsonOutput = SwaggerBuilder.getDocument(document);

Configure JAX-RS Endpoint

Or you can use the Swagger document directly to configure the provided JAX-RS Endpoint:

Map<String, Swagger> swaggerDocs = new HashMap<>();
docs.put("publishingModels", document)

//Dependency Inject the DocEndpoint JAX-RS resource
bind(docs).named("swagger").to(new TypeLiteral<Map<String, Swagger>>() { });

Supporting OAuth

If you want swagger UI to acquire & use a bearer token from an OAuth identity provider, you can configure the swagger document similar to:

SecuritySchemeDefinition oauthDef = new OAuth2Definition().implicit(CONFIG_DATA.zuulAuthorizeUri());
SecurityRequirement oauthReq = new SecurityRequirement().requirement("myOauth");

SwaggerBuilder builder = new SwaggerBuilder(entityDictionary, info);
Swagger document = builder.build();
    .basePath("/my/url/path")
    .securityDefinition("myOauth", oauthDef)
    .security(oauthReq)
    .scheme(Scheme.HTTPS));

Adding a global parameter

A query or header parameter can be added globally to all Elide API endpoints:

HeaderParameter oauthParam = new HeaderParameter()
    .name("Authorization")
    .type("string")
    .description("OAuth bearer token")
    .required(false);

SwaggerBuilder crashBuilder = new SwaggerBuilder(dictionary, info)
    .withGlobalParameter(oauthParam);

Adding a global response code

An HTTP response can be added globally to all Elide API endpoints:

Response rateLimitedResponse = new Response().description("Too Many Requests");

SwaggerBuilder crashBuilder = new SwaggerBuilder(dictionary, info)
    .withGlobalResponse(429, rateLimitedResponse);

Performance

Path Generation

The Swagger UI is very slow when the number of generated URL paths exceeds a few dozen. For large, complex data models, it is recommended to generate separate swagger documents for subgraphs of the model.

Set<Type<?>> entities = Sets.newHashSet(
    new ClassType(Book.class),
    new ClassType(Author.class),
    new ClassType(Publisher.clas)s
);

SwaggerBuilder coreBuilder = new SwaggerBuilder(dictionary, info)
    .withExplicitClassList(entities);

In the above example, swagger will only generate paths that exclusively traverse the provided set of entities.

Document Size

The size of the swagger document can be reduced significantly by limiting the number of filter operators that are used to generate query parameter documentation.

SwaggerBuilder crashBuilder = new SwaggerBuilder(dictionary, info)
   .withFilterOps(Sets.newHashSet(Operator.IN));

In the above example, filter query parameters are only generated for the IN operator.

Model Properties

Elide extracts the model description from the ApiModel and Include annotations and adds them to the swagger documentation. ApiModel has precedence over Include if both are present.

@ApiModel(description = "A book model description")
class Book {

}

Only the description property is extracted.

Attribute Properties

Elide extracts properties from the ApiModelProperty annotation and adds them to the swagger documentation.

class Book {

    @ApiModelProperty(required = true)
    public String title;
}

Only the required, value, example, and readOnly properties are extracted. This is currently only supported for attributes on Elide models.

API Versions

Swagger documents are tied to an explicit API version. When constructing a Swagger document, the API version must be set to match the API version of the models it will describe:

Info info = new Info().title("Test Service").version("1.0");
SwaggerBuilder builder = new SwaggerBuilder(dictionary, info);
Swagger swagger = builder.build();