j2r2b

My notes on GitHub pages

Follow me on GitHub

Parsing OpenAPI specifications with external refs

According to the specification (Reference Object in OAS3) it is allowed to define references (using $ref) to external files.

Consider an example where there are two files in the same folder:

Fist our main OAS3 file: main.yaml

openapi: 3.0.1
info:
  title: ping test
  version: '1.0'
servers:
  - url: 'http://localhost:8000/'
paths:
  /some/ping:
    get:
      operationId: pingGet
      responses:
        '201':
          description: OK
          content:
            application/json:
              schema:
                $ref: './external.yaml#/SomeObj'
components:
  schemas: {}

And a second file: other.yaml

SomeObj:
  properties:
    s1:
      type: string
    s3:
      type: string

A good OpenAPI Parser need to be able to solve those cases.

KaiZen OpenAPI Parser

The KaiZen parser hides the references for you.

import com.reprezen.kaizen.oasparser.OpenApi3Parser;
import com.reprezen.kaizen.oasparser.model3.OpenApi3;
import com.reprezen.kaizen.oasparser.model3.Schema;
import com.reprezen.kaizen.oasparser.val.ValidationResults.ValidationItem;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map.Entry;

public class KaiZenParserExternalRefMain {

	public static void main(String[] args) throws IOException {
		java.nio.file.Path oas3Path = Paths.get("_path_to_/main.yaml");
		if (Files.isRegularFile(oas3Path)) {
			processOAS3(oas3Path.toFile(), false);
		} else {
			System.err.println(oas3Path.toString() + " does not exist.");
		}
	}

	private static void processOAS3(File specFile, boolean validate) {
		OpenApi3 model = new OpenApi3Parser().parse(specFile, validate);
		System.out.printf("== Model %s\n", specFile);
		if (!validate || model.isValid()) {
			navigateInModel(model);
		} else {
			for (ValidationItem item : model.getValidationItems()) {
				System.out.println(item);
			}
		}
		System.out.printf("------\n\n");
	}

	private static void navigateInModel(OpenApi3 model) {
		Schema schema = model.getPath("/some/ping").getOperation("get").getResponse("201")
				.getContentMediaType("application/json").getSchema();
		for (Entry<String, Schema> e : schema.getProperties().entrySet()) {
			System.out.println(e.getKey() + ": " + e.getValue().getType());
		}
	}
}

Tested with this dependency on the Classpath:

<dependency>
  <groupId>com.reprezen.kaizen</groupId>
  <artifactId>openapi-parser</artifactId>
  <version>2.1.1-201803281732</version>
</dependency>

Swagger Parser

The swagger-parser also supports this feature, but you need to activate it in the parser options (eg ParseOptions using setResolve(true)).

import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.parser.core.models.ParseOptions;

import java.util.Map;
import java.util.Map.Entry;

public class SwaggerParserTest {

    public static void main(String[] args) {
        String inputSpec = "_path_to_/main.yaml";

        final OpenAPIParser openApiParser = new OpenAPIParser();
        final ParseOptions options = new ParseOptions();
        options.setResolve(true);
        final OpenAPI openAPI = openApiParser.readLocation(inputSpec, null, options).getOpenAPI();

        Schema schema = openAPI.getPaths().get("/some/ping").getGet().getResponses().get("201").getContent()
                .get("application/json").getSchema();
        System.out.println("ref: " + schema.get$ref());

        Map<String, Schema> properties = openAPI.getComponents().getSchemas().get("SomeObj").getProperties();
        for (Entry<String, Schema> e : properties.entrySet()) {
            System.out.println(e.getKey() + ": " + e.getValue().getType());
        }
    }
}

Tested with:

<dependency>
    <groupId>io.swagger.parser.v3</groupId>
    <artifactId>swagger-parser</artifactId>
    <version>2.0.0</version>
</dependency>