Elide supports the generation of OpenAPI documentation from Elide annotated beans. Specifically, it generates a JSON or YAML document conforming to the OpenAPI 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.
Schema
annotations.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-jakarta</artifactId>
</dependency>
If you are using Elide Spring Autoconfigure, you can customize the OpenAPI
document by using a OpenApiDocumentCustomizer
bean:
@Bean
public OpenApiDocumentCustomizer openApiDocumentCustomizer() {
return openApi -> {
Info info = new Info()
.title("My Title");
openApi.setInfo(info);
};
}
The application yaml file has settings:
elide:
api-docs:
path: /doc
enabled: true
version: openapi_3_0
If you want Swagger UI to acquire & use a bearer token from an OAuth identity provider, you can configure the OpenAPI document by using annotations:
@SpringBootApplication
@OpenAPIDefinition(info = @Info(title = "My Title"), security = @SecurityRequirement(name = "bearerAuth"))
@SecurityScheme(
name = "bearerAuth",
type = SecuritySchemeType.HTTP,
bearerFormat = "JWT",
scheme = "bearer"
)
public class App {
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
}
Elide contributes to Springdoc’s OpenAPI document by exposing a Springdoc OpenApiCustomizer
bean. The default implementation is implemented in DefaultElideOpenApiCustomizer
. To override the behavior a ElideOpenApiCustomizer
bean can be created which will cause the DefaultElideOpenApiCustomizer
not to be configured.
@Bean
public ElideOpenApiCustomizer elideOpenApiCustomizer() {
return new MyCustomElideOpenApiCustomizer();
}
If you are using Elide Standalone, you can extend ElideStandaloneSettings
to:
/**
* Enable OpenAPI documentation.
* @return whether OpenAPI is enabled;
*/
@Override
default boolean enableApiDocs() {
return false;
}
/**
* API root path specification for the OpenAPI endpoint. Namely, this is the root uri for OpenAPI docs.
*
* @return Default: /api-docs/*
*/
@Override
default String getApiDocsPathSpec() {
return "/api-docs/*";
}
/**
* OpenAPI documentation requires an API name.
* @return open api service name;
*/
@Override
default String getApiTitle() {
return "Elide Service";
}
/**
* The OpenAPI Specification Version to generate.
* @return the OpenAPI Specification Version to generate
*/
@Override
default OpenApiVersion getOpenApiVersion() {
return OpenApiVersion.OPENAPI_3_0;
}
/**
* Creates a singular OpenAPI document for JSON-API.
* @param dictionary Contains the static metadata about Elide models. .
* @return list of OpenAPI registration objects.
*/
@Override
default List<ApiDocsEndpoint.ApiDocsRegistration> buildApiDocs(EntityDictionary dictionary) {
List<ApiDocsEndpoint.ApiDocsRegistration> docs = new ArrayList<>();
dictionary.getApiVersions().stream().forEach(apiVersion -> {
Info info = new Info()
.title(getApiTitle())
.version(apiVersion);
OpenApiBuilder builder = new OpenApiBuilder(dictionary).apiVersion(apiVersion);
String moduleBasePath = getJsonApiPathSpec().replace("/*", "");
OpenAPI openApi = builder.build().info(info).addServersItem(new Server().url(moduleBasePath));
docs.add(new ApiDocsEndpoint.ApiDocsRegistration("test", () -> openApi, getOpenApiVersion().getValue(),
apiVersion));
});
return docs;
}
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 Info object.
Info info = new Info().title("My Service").version("1.0");
Initialize a OpenAPI builder.
OpenApiBuilder builder = new OpenApiBuilder(dictionary);
Build the OpenAPI document
OpenAPI document = builder.build().info(info);
You can directly convert to JSON:
OpenApiDocument openApiDocument = new OpenApiDocument(document, OpenApiDocument.Version.from("3.0"));
String jsonOutput = openApiDocument.of(OpenApiDocument.MediaType.APPLICATION_JSON);
Or you can use the OpenAPI document directly to configure the provided JAX-RS Endpoint:
List<ApiDocsEndpoint.ApiDocsRegistration> apiDocs = new ArrayList<>();
OpenAPI openApi = new OpenAPI();
apiDocs.add(new ApiDocsEndpoint.ApiDocsRegistration("test", () -> openApi, "3.0", ""));
//Dependency Inject the ApiDocsEndpoint JAX-RS resource
bind(apiDocs).named("apiDocs").to(new TypeLiteral<List<ApiDocsEndpoint.ApiDocsRegistration>>() {
});
If you want Swagger UI to acquire & use a bearer token from an OAuth identity provider, you can configure the OpenAPI document similar to:
SecurityScheme oauthDef = new SecurityScheme()
.name("bearerAuth")
.type(SecurityScheme.Type.HTTP)
.bearerFormat("JWT")
.scheme("bearer");
SecurityRequirement oauthReq = new SecurityRequirement().addList("bearerAuth");
OpenApiBuilder builder = new OpenApiBuilder(entityDictionary);
OpenAPI document = builder.build();
document.addSecurityItem(oauthReq);
document.getComponents().addSecuritySchemes("bearerAuth", oauthDef);
A query or header parameter can be added globally to all Elide API endpoints:
Parameter oauthParam = new Parameter()
.in("Header")
.name("Authorization")
.schema(new StringSchema())
.description("OAuth bearer token")
.required(false);
OpenApiBuilder builder = new OpenApiBuilder(dictionary)
.globalParameter(oauthParam);
An HTTP response can be added globally to all Elide API endpoints:
ApiResponse rateLimitedResponse = new ApiResponse().description("Too Many Requests");
OpenApiBuilder builder = new OpenApiBuilder(dictionary)
.globalResponse(429, rateLimitedResponse);
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 OpenAPI documents for subgraphs of the model.
Set<Type<?>> entities = Sets.newHashSet(
new ClassType(Book.class),
new ClassType(Author.class),
new ClassType(Publisher.clas)s
);
OpenApiBuilder builder = new OpenApiBuilder(dictionary)
.explicitClassList(entities);
In the above example, the OpenApiBuilder
will only generate paths that exclusively traverse the provided set of entities.
The size of the OpenAPI document can be reduced significantly by limiting the number of filter operators that are used to generate query parameter documentation.
OpenApiBuilder builder = new OpenApiBuilder(dictionary)
.filterOperators(Sets.newHashSet(Operator.IN));
In the above example, filter query parameters are only generated for the IN operator.
Elide extracts the model description and title from the Schema
and Include
annotations and adds them to the OpenAPI documentation. Schema
has precedence over Include
if both are present.
@Schema(description = "A book model description", title = "Book")
class Book {
}
For Schema
only the description and title property is extracted. For the Include
annotation, the friendlyName is used as the title.
Elide extracts properties from the Schema
annotation and adds them to the OpenAPI documentation.
class Book {
@Schema(requiredMode = RequiredMode.REQUIRED)
public String title;
}
Only the required, value, example, readOnly, writeOnly, requiredProperties, requiredMode and accessMode properties are extracted. This is currently only supported for attributes on Elide models.
OpenAPI documents are tied to an explicit API version. When constructing a OpenAPI document, the API version must be set to match the API version of the models it will describe:
OpenApiBuilder builder = new OpenApiBuilder(dictionary).apiVersion("1.0");
OpenAPI openApi = builder.build();