• Stars
    star
    159
  • Rank 235,916 (Top 5 %)
  • Language
    Java
  • License
    Apache License 2.0
  • Created over 9 years ago
  • Updated 7 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Swagger Inflector

Build Status

Maven Central


NOTE: If you're looking for swagger-inflector 1.X and Swagger/OpenApi 2.0, please refer to v1 branch


This project uses the Swagger Specification to drive an API implementation. Rather than a typical top-down or bottom-up swagger integration, the Inflector uses the swagger specification as a DSL for the REST API. The spec drives the creation of routes and controllers automatically, matching methods and method signatures from the implementation. This brings a similar integration approach to the JVM as swagger-node brings to the javascript world.

To allow for an iterative development, the framework will mock responses for any unimplemented methods, based on the specification. That means you can ship your API to your consumers for review immediately as you build it out.

You have full control over the mapping of controllers to classes and methods as well as models.

Quick start!

Run this command to start in a hurry. It will create a project named my-project

curl -L https://raw.githubusercontent.com/swagger-api/swagger-inflector/master/setup.sh | project=my-project bash

This will download everything you need to start editing and running a swagger-inflector based project. See the output of the command for instructions.

Components

Inflector uses the following libraries:

  • swagger models for the swagger definition
  • Jackson for JSON processing
  • Jersey 2.39 for REST
  • Minimum Java 8

Integration

Inflector will create routes and add them to Jersey. You simply need to register the Inflector application in your webapp and it should be compatible with your existing deployment, whether with web.xml, spring, dropwizard, etc.

To add inflector via web.xml:

<servlet>
  <servlet-name>swagger-inflector</servlet-name>
  <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>javax.ws.rs.Application</param-name>
    <param-value>io.swagger.oas.inflector.OpenAPIInflector</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>swagger-inflector</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>

This simply adds the SwaggerInflector application to Jersey.

Configuration

Inflector uses a single yaml file for configuration. The default file is inflector.yaml but it can be overridden by setting a system property when starting the JVM:

-Dconfig=/path/to/config

The configuration supports the following:

# mode (development | staging | production).  Default is development, and this value will be overridden by a system property
# -Denvironment=production for example
environment: development

# configure your default controller package for method discovery
controllerPackage: io.swagger.oas.sample.controllers

# configure the default model package for model discovery
modelPackage: io.swagger.oas.sample.models

# the path to the swagger definition (Note! this can be overridden with -DswaggerUrl as a system property
swaggerUrl: openapi.yaml

# specific mappings for models, used to locate models in the `#/definitions/${model}`
modelMappings:
  User: io.swagger.oas.sample.models.User

# HTTP response code when required parameters are missing
invalidRequestCode: 400

#Allows to configure the exposed spec (values in example are the defaults)
exposedSpecOptions:
  parseOptions:
    resolve: false
    resolveFully: false
  useOriginalNotParsed: false
  hideInflectorExtensions: true
  mergeRootPath: true

Locating the controller class

The actual controller class for each method is located via the first of the following mechanisms:

  • a x-swagger-router-controller extension at the method level can specify the specific controller class
  • each tag associated with the method is assembled into the classnames "<controllerPackage>.<Tag>" or "<controllerPackage>.<Tag>Controller", the first of these classes that is found by Class.forName(...) will be used
  • an optional <controllerClass> configuration parameter is appended to <controllerPackage>
  • as a last resort a class named <controllerPackage>.Default is used

By default the class is loaded directly with Class.forName(...).newInstance() - but you can override class creation by providing a custom ControllerFactory to the inflector configuration (for example if you want your controllers to be loaded by a DI framework).

Locating the target method

When locating methods, the operationId is used as the method name for lookup via reflection. If not specified, there is logic for generation of a method name.

Once a method is matched via name, the parameter types will be compared to ensure we have the right model. In all methods, only java objects are supported--primitives currently will not match (this allows for proper nulls).

You can override a model mapping by setting a vendor extension in the swagger yaml:

# uses method name, look for controllerPackage in the configuration
paths:
  /test1:
    get:
      x-swagger-router-controller: SampleController
      operationId: getTest1
      parameters:
        - name: name
          in: query
          type: string
      responses:
        200:
          description: Success!

From the configuration example above, this will look for the following class:

class: io.swagger.sample.controllers.SampleController

with the following method:

method: public Object getTest1(
    RequestContext,
    java.lang.String name)

Complex inputs

When there are complex inputs, such as the example below:

paths:
  /test2:
    post:
      x-swagger-router-controller: SampleController
      operationId: addUser
      requestBody:
          content:
            "application/json":
              schema:
                $ref: '#/definitions/User'
      parameters:
        - name: name
          in: query
          type: string
      responses:
        200:
          description: Success!

the Inflector will do the following:

  • Look in vendor extensions for the models to see if a mapping exists. If so, it will attempt to load it via the classloader
  Address:
   x-swagger-router-model: io.swagger.test.models.Address
   properties:
     street:
       type: string
       example: 12345 El Monte Road
     city:
       type: string
       example: Los Altos Hills
     state:
       type: string
       example: CA
     zip:
       type: string
       example: '94022'
  • Look in the configuration for a mapping between User and a concrete class definition. If the definition exists AND the class can be loaded, the method will look like such:
public ResponseContext addUser (
   RequestContext context,             // request context
   io.swagger.sample.models.User user, // user being added
   java.lang.String name)              // the `name` query param
  • If the definition does not exist, the modelPackage from the configuration will be used to attempt to load the class:


If the definition can be loaded it will be used as the method signature

  • If no model can be loaded, it is the developer's job to unwrap the input and parse it on their own. This requires Content-Type-specific processing. Inflector will then look for the following method:
public ResponseContext addUser (
   RequestContext context,             // request context
   JsonNode user,                      // a Json tree representing the user
   java.lang.String name)              // the `name` query param
  • If no method can be found, a mock response will be returned based on the swagger definition. For complex objects, if an example exists, we will use that. Otherwise, it will be constructed.

The RequestWrapper and ResponseContext contain information about headers (in and outbound), content-type and acceptable response types.

Outputs

Your controllers can return null (void response), an object (entity), or a ResponseContext, which allows you to send specific error codes, headers, and an optional entity.

For example, if you want to return a Pet from a controller:

    public ResponseContext getPet(RequestContext request, java.lang.Integer petId) {
        // do your magic to fetch a pet...
        Pet pet = complexBusinessLogic.getPetById(petId);

        return new ResponseContext()
                .status(Status.OK)
                .entity(pet);
    }

and the Inflector will return a 200 response code, marshalling the Pet object into the appropriate content type.

If you do not implement your controller, the Inflector will generate sample data based on your model definitions. It will honor any examples that you have in the definitions, assuming they are compatible with the schema you declared. For example, this definition:

properties:
  street:
    type: "string"
    example: "12345 El Monte Blvd"
  city:
    type: "string"
    example: "Los Altos Hills"
  state:
    type: "string"
    example: "CA"
    minLength: 2
    maxLength: 2
  zip:
    type: "string"
    example: "94022"
xml:
  name: "address"

Will produce this example for a Accept:application/json:

{
  "street" : "12345 El Monte Blvd",
  "city" : "Los Altos Hills",
  "state" : "CA",
  "zip" : "94022"
}

and application/yaml:

street: "12345 El Monte Blvd"
city: "Los Altos Hills"
state: "CA"
zip: "94022"

and application/xml:

<address>
  <street>12345 El Monte Blvd</street>
  <city>Los Altos Hills</city>
  <state>CA</state>
  <zip>94022</zip>
</address>

Payload validation

Since your inbound and outbound payloads are defined with the Swagger schema, Inflector can validate them at runtime. Just enable payload validations in your inflector config:

validatePayloads: true

And at start-up, Inflector will read the schema and attach the relevant section of it to the operation. For example, a post operation that has this as the schema definition:

{
  "Category": {
    "required": [
      "id"
    ],
    "properties": {
      "id": {
        "type": "integer",
        "format": "int64"
      },
      "name": {
        "type": "string"
      }
    },
    "xml": {
      "name": "Category"
    }
  }
}

Will fail if the incoming body looks like this:

{
  "name": "Tony"
}

because the required field id is missing.

The same goes for responses generated by the server. Any response code that you send will be validated against it's corresponding schema.

You can choose to enable this in development, staging, or production.

Content type negotiation

There is a pluggable framework for handling different content types. You can register any processor by the following:

EntityProcessor myProcessor = new MyEntityProcessor();  // implements EntityProcessor
EntityProcessorFactory.addProcessor(myProcessor);

Development Lifecycle

There are three modes that the Inflector supports, as configured by the environment attribute in the inflector config:

  • development. In this mode, mock responses will be sent for controllers which are not implemented. The intention is to allow you to quickly iterate on the implementation of the design. In addition, missing model implementations are tolerated and supported.

  • staging. Warning messages will be logged when starting the service for any missing controller, method, or model.

  • production. The expectation is all methods and declared (manually mapped) models exist. If they don't, it'll throw nasty errors and the server will not start.

In development mode, there is a /debug.json page which shows implementation details of the inflector service.

If your Swagger Description is unparsable, the server will throw ugly errors on startup and the debug.json page will give indications as to why.

Samples

The samples are a being refactor to support the new inflector.

You will soon find samples for the inflector project in the Swagger-Samples repository. The inflector projects start with inflector-

Running tests

If running Java 8, you will need to run a variant that has backported fix 8157236. Azul Zulu is confirmed to work (jmockit/jmockit1#710).

If running with Java 9 or later, you will need to either:

  • Pass -Djdk.attach.allowAttachSelf=true to the VM.
  • Configure the test execution JVM to start with the "-javaagent:/jmockit.1.x.jar" initialization parameter. It can be specified in the build script file for tools such as Maven or Gradle, or in a "Run/Debug Configuration" for IntelliJ IDEA or Eclipse.

Security contact

Please disclose any security-related issues or vulnerabilities by emailing [email protected], instead of using the public issue tracker.

More Repositories

1

swagger-ui

Swagger UI is a collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.
JavaScript
26,260
star
2

swagger-codegen

swagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.
Mustache
16,866
star
3

swagger-editor

Swagger Editor
JavaScript
8,842
star
4

swagger-core

Examples and server integrations for generating the Swagger API Specification, which enables easy access to your REST API
Java
7,372
star
5

swagger-node

Swagger module for node.js
JavaScript
3,969
star
6

swagger-js

Javascript library to connect to swagger-enabled APIs via browser or nodejs
JavaScript
2,607
star
7

swagger.io-docs

The content of swagger.io
Astro
1,560
star
8

swagger-parser

Swagger Spec to Java POJOs
Java
777
star
9

swagger-samples

Samples for the various Swagger projects under swagger-api
JavaScript
537
star
10

swagger-socket

Swagger Socket: A REST over WebSocket
Java
383
star
11

swagger-play

Java
330
star
12

swagger-codegen-generators

Mustache
284
star
13

swagger-petstore

Java
245
star
14

validator-badge

Validate your Swagger JSON/YAML today!
Java
210
star
15

swagger-converter

OpenAPI/Swagger 2.0 to OpenAPI 3.0 Converter WebService
Shell
114
star
16

swagger-scala-module

Swagger support for scala
Scala
103
star
17

apidom

Semantic parser for API specifications
TypeScript
68
star
18

rails-petstore

Ruby
29
star
19

swaggerhub-maven-plugin

A simple maven plugin to access SwaggerHub hosting of OpenAPI/Swagger from a maven build process.
Java
29
star
20

swagger-play-sample-app

A sample play app which uses swagger plugin to make the age old pet store swagger compliant.
JavaScript
29
star
21

swagger2

For working out the Swagger 2 working group page
CSS
21
star
22

scalatra-sample-app

Shell
20
star
23

swagger-scala-sample-app

A fully-functioning, stand-alone Swagger server written in scala which demonstrates how to enable Swagger in your API.
Scala
20
star
24

swagger-form-editor

JavaScript
19
star
25

swaggerhub-gradle-plugin

Gradle plugin for SwaggerHub
Java
19
star
26

petstore-kafka

A demo site built on top of Kafka topics
JavaScript
13
star
27

swagger-editor-cra

This is forked Create React App that builds SwaggerEditor@5
JavaScript
10
star
28

swagger-async-httpclient

Scala
10
star
29

swagger-play-1.2

Scala
9
star
30

swagger-scala

Scala
8
star
31

swagger-schema-packaging

6
star
32

apidom-lsp-vscode

ApiDOM VS Code Extension
TypeScript
5
star
33

sway-worker

4
star
34

apidom-ls-client

Demo client for apidom-ls OpenAPI / AsyncAPI ApiDOM validation service
TypeScript
3
star
35

.github

Common Github files for the Swagger projects
3
star
36

swagger-codegen-test

1
star