URLs

Do you want to improve this page? Please edit it on GitHub.

Description

Eclipse MicroProfile OpenAPI is a standardisation effort around OpenAPI. In particular, it offers an API to model OpenAPI specification (interfaces in the org.eclipse.microprofile.openapi.models package and sub-packages).

This repository contains different projects (implementations and utilities) for this API. Providing a complete vendor implementation for the Eclipse MicroProfile OpenAPI is out-of-scope.

Listing 1. A minimal OpenAPI specification (Yaml format)
---
openapi: 3.0.1
info:
  title: Ping Specification
  version: "1.0"
servers:
- url: http://localhost:8000/
paths:
  /ping:
    get:
      operationId: pingGet
      responses:
        "200":
          description: OK

A typical OpenAPI specification as presented in Listing 1 can be created in Java using Eclipse MicroProfile OpenAPI project with the code presented in Listing 2.

Listing 2. An OpenAPI created with the Eclipse MicroProfile OpenAPI
import static org.eclipse.microprofile.openapi.OASFactory.*;

import org.eclipse.microprofile.openapi.models.OpenAPI;

public final class PingSpec {
    public static OpenAPI create() {
        return createOpenAPI()
            .openapi("3.0.1")
            .info(
                createInfo()
                    .title("Ping Specification")
                    .version("1.0")
            )
            .addServer(
                createServer()
                    .url("http://localhost:8000/")
            )
            .paths(
                createPaths()
                    .addPathItem(
                        "/ping", createPathItem()
                            .GET(
                                createOperation()
                                    .operationId("pingGet")
                                    .responses(
                                        createAPIResponses()
                                            .addAPIResponse(
                                                "200", createAPIResponse()
                                                    .description("OK")
                                            )
                                    )
                            )
                    )
            );
    }
}

The code in Listing 2 requires only microprofile-openapi-api to be on the classpath (see Listing 3 for the complete maven coordinates).

Listing 3. Maven coordinates of the 'Eclipse MicroProfile OpenAPI' api project
<dependency>
  <groupId>org.eclipse.microprofile.openapi</groupId>
  <artifactId>microprofile-openapi-api</artifactId>
  <version>2.0</version>
</dependency>

All EMPOA projects of this repository (EMPOA stands for Eclipse MicroProfile OpenAPI) are based on the interfaces representing OpenAPI specifications from the Eclipse MicroProfile OpenAPI project. Either by implementing them (see Implementation projects) or by providing additional features (see Serializer projects and Other projects).

Implementation projects

Without any implementation on the classpath, the code presented in Listing 2 will throw an exception IllegalStateException (see Listing 4 for the stacktrace).

Listing 4. Stacktrace when working with the Eclipse MicroProfile OpenAPI project without any implementation
java.lang.IllegalStateException: No OASFactoryResolver implementation found!
        at org.eclipse.microprofile.openapi.spi.OASFactoryResolver.instance(OASFactoryResolver.java:77)
        at org.eclipse.microprofile.openapi.OASFactory.createObject(OASFactory.java:49)
        at org.openapitools.empoa.extended.tck.specs.PingSpec.create(PingSpec.java:16)
        at ...

This is why you need to provide one implementation on your classpath.

EMPOA Simple models

Listing 5. Maven coordinates of the 'EMPOA Simple models' project
<dependency>
  <groupId>org.openapitools.empoa</groupId>
  <artifactId>empoa-simple-models-impl</artifactId>
  <version>2.1.0</version>
</dependency>

This project provides a simple implementation for the interfaces defined in the org.eclipse.microprofile.openapi.models package and sub-packages. The implementation are simple POJO. There is no dependency to any other frameworks.

EMPOA Swagger-Core

Listing 6. Maven coordinates of the 'EMPOA Swagger-Core' project
<dependency>
  <groupId>org.openapitools.empoa</groupId>
  <artifactId>empoa-swagger-core</artifactId>
  <version>2.1.0</version>
</dependency>

Swagger-Core is a well known library to handle OpenAPI specification in Java. Swagger-Parser is using this representation when serializing and deserializing JSON or YAML files.

This project provides a simple implementation for the interfaces defined in the org.eclipse.microprofile.openapi.models package and sub-packages by delegating all methods to Swagger-Core object. It can be used as a bridge between both libraries.

One usage could be a project programmed on top of the Eclipse MicroProfile APIs but using Swagger-Parser to parse OpenAPI Specifications,

As for any other implementation project of the Eclipse MicroProfile APIs, the org.eclipse.microprofile.openapi.OASFactory can be used to create OpenAPI instance programmatically (making usage of Swagger-Core invisible).

An other approach, if you have an existing instance of Swagger-Core models (for example the output of Swagger-Parser), org.openapitools.empoa.swagger.core.internal.SwAdapter can be used as proposed in Listing 7.

Listing 7. Example usages of EMPOA Swagger-Core
io.swagger.v3.oas.models.OpenAPI swaggerOpenAPI = parserResult.getOpenAPI();
OpenAPI openAPI = SwAdapter.toOpenAPI(swaggerOpenAPI);

If you are looking for factory methods where no Swagger project elements are part of the method signature, you can use org.openapitools.empoa.swagger.core.SwOASFactory. Its createObject(Object) method expects the Swagger instance that will be wrapped in the corresponding object implementing the Eclipse MicroProfile interface.

The Swagger library (io.swagger.core.v3:swagger-models) is only an "implementation" dependency.

Serializer projects

Given an OpenAPI specification represented by instances of objects implementing the Eclipse MicroProfile OpenAPI interfaces (org.eclipse.microprofile.openapi.models package and sub-packages), you can use one of the following projects to serialize them to JSON or YAML. Those projects work only with the API interfaces and are not bound to a specific implementation.

EMPOA Gson Serializer

Listing 8. Maven coordinates of the 'EMPOA Gson Serializer' project
<dependency>
  <groupId>org.openapitools.empoa</groupId>
  <artifactId>empoa-gson-serializer</artifactId>
  <version>2.1.0</version>
</dependency>

This project is using Gson to serialize classes implementing the Eclipse MicroProfile OpenAPI interfaces (org.eclipse.microprofile.openapi.models package and sub-pacakges) to JSON.

The code presented in Listing 9 shows how the library can be used to serialize an org.eclipse.microprofile.openapi.models.OpenAPI to JSON

Listing 9. Example usages of EMPOA Gson Serializer
Gson gson = OASGsonSerializer.instance();
String json = gson.toJson(openAPI);

EMPOA Jackson Serializer

Listing 10. Maven coordinates of the 'EMPOA Jackson Serializer' project
<dependency>
  <groupId>org.openapitools.empoa</groupId>
  <artifactId>empoa-jackson-serializer</artifactId>
  <version>2.1.0</version>
</dependency>

This project is using Jackson to serialize classes implementing the Eclipse MicroProfile OpenAPI interfaces (org.eclipse.microprofile.openapi.models package and sub-pacakges) to YAML or JSON.

It is adapted from the SmallRye OpenAPI project.

The code presented in Listing 11 shows how the library can be used to serialize an org.eclipse.microprofile.openapi.models.OpenAPI to JSON.

Listing 11. Example usages of EMPOA Jackson Serializer
String json = OpenAPISerializer.serialize(openAPI, OpenAPISerializer.Format.JSON);

Other projects

EMPOA Javapoet

Listing 12. Maven coordinates of the 'EMPOA Javapoet' project
<dependency>
  <groupId>org.openapitools.empoa</groupId>
  <artifactId>empoa-javapoet</artifactId>
  <version>2.1.0</version>
</dependency>

Javapoet is a library that can be used to generate Java code.

Given an instance of org.eclipse.microprofile.openapi.models.OpenAPI (or of any other interface in the org.eclipse.microprofile.openapi.models package or sub-packages), this project is using Javapoet to create the corresponding java-code to recreate an other instance holding the same values.

The code presented in Listing 13 shows how code can be generated: a complete class holding a single static method create() is generated.

Listing 13. Example usages of EMPOA Javapoet
JavaFile javaFile = JavaFileConverter.createOpenAPI(openAPI, packageName, className);
String javaCode = javaFile.toString();

The methods in a second class org.openapitools.empoa.javapoet.CodeBlockConverter might be useful to create only the CodeBlock corresponding to the generated OASFactory.createObject(..) code.

EMPOA Util

Listing 14. Maven coordinates of the 'EMPOA Util' project
<dependency>
  <groupId>org.openapitools.empoa</groupId>
  <artifactId>empoa-util</artifactId>
  <version>2.1.0</version>
</dependency>

This project is adding some features to the Eclipse MicroProfile OpenAPI interfaces (org.eclipse.microprofile.openapi.models package and sub-pacakges):

Visitor pattern

If you need to apply a visitor pattern approach to an OpenAPI specification, you can implement the OASVisitor interface or extends the OASVisitorAdapter class. Then all the accept methods are defined in the OASAccept class.

When the visit(..) method is called for a member of the OpenAPI specification, in addition to the instance of the object, you also get the JSONPath value that you could use to navigate to this object when the specification is serialized to JSON.

Example:

If you want to collect all operationId of all operations, you can use a visitor as implemented in Listing 15.

Listing 15. An example visitor that collects all operations id with its associated JSONPath
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.microprofile.openapi.models.Components;
import org.eclipse.microprofile.openapi.models.Operation;

public class OperationIdVisitor extends OASVisitorAdapter {

    private Map<String, String> operationIds = new HashMap<>();

    public Map<String, String> getOperationIds() {
        return Collections.unmodifiableMap(operationIds);
    }

    @Override
    public OASVisitResult visit(Operation operation, String jsonPath) {
        operationIds.put(jsonPath + ".operationId", operation.getOperationId()); (1)
        return OASVisitResult.CONTINUE; (2)
    }

    @Override
    public OASVisitResult visit(Components components, String jsonPath) {
        return OASVisitResult.TERMINATE; (3)
    }
}
1 Store the jsonPath and the operationId value in the map.
2 Indicates to continue with with the traversal of the OpenAPI specification
3 Indicates to stop, because we do not need to check the entities inside the components section

For a given OpenAPI specification, this visitor can be used to collect all the values as presented in Listing 16.

Listing 16. Usage of a visitor on a given OpenAPI specification
OperationIdVisitor visitor = new OperationIdVisitor();
OASAccept.accept(visitor, openAPI); (1)

Map<String, String> operationIds = visitor.getOperationIds();
1 Start visiting the OpenAPI tree

After visiting the simple Ping specification (see the OpenAPI file: ping.json on GitHub), the operationIds map contains one single entry:

  • key: $.paths.['/ping'].get.operationId

  • value: pingGet

Equals utility

The OASEquals utility checks if two instances of an OpenAPI element are equals or not, from an OpenAPI point of view (regardless of the implementation of their equals methods). They are considered equals if they produce the same JSON or YAML tree when they are serialized. See Listing 17 for a usage example.

Listing 17. Comparison of two schemas
Schema firstSchema;
Schema secondSchema;
// ... instantiate the 'firstSchema' and 'secondSchema' variables
boolean equals = OASEquals.equals(firstSchema, secondSchema);
Copy utility

The OASCopy utility class creates a copy of a given OpenAPI element instance. See Listing 18 for a usage example.

Listing 18. Copy of a PathItem instance
PathItem original;
// ... instantiate the 'original' variable
PathItem copy = OASCopy.copy(original);
Merge utility

The OASMerge utility provides methods to merge the content of one element of an OpenAPI specification into an other element of the same type. Only non-null values are copied. Maps are merged (if two entry have the same key those items are merged). List are also merged: items of the list are added only if they are not already present. See Listing 19 for a usage example.

Listing 19. Merge one Operation object into an other one
Operation fromOperation;
Operation intoOperation;
// ... instantiate the 'fromOperation' and 'intoOperation' variables
OASMerge.merge(fromOperation, intoOperation);

Helper projects

EMPOA Extended TCK

Listing 20. Maven coordinates of the 'EMPOA Extended TCK' project
<dependency>
  <groupId>org.openapitools.empoa</groupId>
  <artifactId>empoa-extended-tck</artifactId>
  <version>2.1.0</version>
</dependency>

This project contains some abstract test classes that add additional TCK tests. Those tests ensure that the implementations are working properly. They are complementary (and sometime also redundant) with the official TCK tests provided in org.eclipse.microprofile.openapi.tck.ModelConstructionTest.

EMPOA Generator

This project is used to generate code of other projects of this repository.

EMPOA SmallRye

This project is used to validate the 'EMPOA Extended TCK' project with an independent implementation.

Download

The artifacts are hosted on maven central.

Snapshots version are hosted in the sonatype repository:

https://oss.sonatype.org/content/repositories/snapshots/

Build

This project is using gradle.

Command to build the sources locally:

./gradlew build

Command to deploy to your local maven repository:

./gradlew publishToMavenLocal

Command to build the documentation page:

./gradlew asciidoctor

The output of this command is an HTML page located at <git repo root>/build/docs/html5/index.html.

For project maintainers

signing.gnupg.keyName and signing.gnupg.passphrase are expected to be set in your local gradle.properties file to be able to sign. sonatypeUser and sonatypePassword are expected to be set in order to be able to publish to a distant repository.

Command to build and publish the result to maven central:

./gradlew publishToSonatype

Command to upload the documentation page on GitHub pages:

./gradlew gitPublishPush

Command to perform a release:

./gradlew release -Prelease.useAutomaticVersion=true

Using ssh-agent

Some tasks requires to push into the distant git repository (release task or updating the gh-pages branch). If they are failing with errors like this:

org.eclipse.jgit.api.errors.TransportException: ... Permission denied (publickey).

Then ssh-agent can be used.

eval `ssh-agent -s`
ssh-add ~/.ssh/id_rsa

(source for this approach)

Get in touch

Use the issue tracker on GitHub.

You can also contact me on Twitter: @j2r2b

License