Skip to content

cobigen openapiplugin

jdiazgon edited this page Mar 15, 2018 · 23 revisions

OpenApi Plug-in

The OpenApi Plug-in enables the support for Swagger files that follows the OpenApi 3.0 standard as input for CobiGen. Until now, CobiGen was though to follow a "code first" generation, with this plugin, now can follow too the "contract first" strategy

  • Code First

    • Generating from a file with code (Java/XML code in our case)

  • Contract First

    • Generation from a full definition file (Swagger in this case). This file contains all the information about entities, operations, etc…​

Trigger Extensions

The OpenApi Plug-in provides a new trigger for Swagger OpenApi 3.0 related inputs. It accepts different representations as inputs (see OpenApi input reader) and provides additional matching and variable assignment mechanisms. The configuration in the context.xml for this trigger looks like this:

  • type 'openapi'

    Example of a openapi trigger definition
    <trigger id="..." type="openapi" templateFolder="...">
        ...
    </trigger>

    This trigger type enables OpenApi elements as inputs.

Matcher type

With the trigger you might define matchers, which restrict the input upon specific aspects:

  • type 'element' → An object

This trigger will be enabled if the element (Java Object) of the input file is and EntityDef (value).

ContainerMatcher type

Additionally, the java plugin provides the ability to match packages (containers) as follows:

  • type 'element'

The container matcher matches elements as Java Objects, in this case will be always an OpenApiFile object. (See containerMatcher semantics to get more information about containerMatchers itself.)

VariableAssignment types

Furthermore, it provides the ability to extract information from each input for further processing in the templates. The values assigned by variable assignments will be made available in template and the destinationPath of context.xml through the namespace variables.<key>. The OpenApi Plug-in currently provides two different mechanisms:

  • type 'constant' → constant parameter

    <trigger id="..." type="openapi" templateFolder="...">
        <containerMatcher type="element" value="OpenApiFile"/>
        <matcher type="element" value="EntityDef">
            <variableAssignment type="constant" key="rootPackage" value="com.capgemini.demo" />
        </matcher>
    </trigger>

This variable assignment assigns the value of the given regular expression group number to the given key. In this case, the constant type variableAssignment is used to specify the root package where the generate will place the files generated.

  • type 'extension' → Extraction of the info extensions and the extensions of each entity. (the tags that start with "x-…​").

      <trigger id="..." type="openapi" templateFolder="...">
        <containerMatcher type="element" value="OpenAPIFile"/>
        <matcher type="element" value="EntityDef">
          <variableAssignment type="extension" key="testingAttribute" value="x-test"/>
          <variableAssignment type="extension" key="rootPackage" value="x-rootpackage"/>
          <variableAssignment type="extension" key="globalVariable" value="x-global"/>
        </matcher>
      </trigger>

The 'extension' variable assignment tries to find 'extensions' (tags that start with "x-…​") on the 'info' part of your file and on the extensions of each entity. value is the extension that our plug-in will try to find on your OpenAPI file. The result will be stored in the variable key.

As you will see on the figure below, there are two types of variables: The global ones, that are defined on the 'info' part of the file, and the local ones, that are defined inside each entity.

Therefore, if you want to define the root package, then you will have to declare it on the 'info' part. That way, all your entities will be generated under the same root package.

Swagger at OASP4J Project

  • type 'property' → property of the Java Object

    <trigger id="..." type="openapi" templateFolder="...">
        <containerMatcher type="element" value="OpenApiFile"/>
        <matcher type="element" value="EntityDef">
            <variableAssignment type="property" key="entityName" value="name" />
        </matcher>
    </trigger>

The 'property' variable assignment tries to find the property value of the entities defined on the schema. The value is assigned to the key. The current properties that you will able to get are:

  1. ComponentDef component: It is an object that stores the configuration of an oasp4j component. Its only property is List<PathDef> which contains the paths as the ones shown here.

  2. String componentName: Stores the name of the x-component tag for this entity.

  3. String name: Name of this entity (as shown on the example above).

  4. String description: Description of this entity.

  5. List<PropertyDef> properties: List containing all the properties of this entity. PropertyDef is an object that has the next properties:

    1. String name.

    2. String type.

    3. String format.

    4. String description.

    5. Boolean isCollection.

    6. Boolean isEntity.

    7. Boolean required.

    8. Map<String, Object> constraints

Full trigger configuration

<trigger id="..." type="openapi" templateFolder="...">
    <containerMatcher type="element" value="OpenApiFile">
    <matcher type="element" value="EntityDef">
        <variableAssignment type="constant" key="rootPackage" value="com.capgemini.demo" />
        <variableAssignment type="property" key="component" value="componentName" />
        <variableAssignment type="property" key="entityName" value="name" />
    </matcher>
</trigger>

Input reader

The Cobigen OpenApi Plug-in implements an input reader for OpenApi 3.0 files. The XML input reader will create the following object model for template creation:

  • model ('Map<String, Object>' :: common element structure)

    • name ('String' :: Name of the current Entity)

    • componentName ('String' :: name oif the component that entity belongs to)

    • component ('ComponentDef' :: Full definition of the component that entity belongs to)

    • description ('String' :: Description of the Entity)

    • properties ('List<PropertyDef>' :: String representation of the attribute’s value)

    • relationShips ('List<RelationShip' :: String representation of the attribute’s value)

  • ComponentDef ('Map<String, Object>' :: common element structure)

    • paths ('List<PathDef>' :: List of services for this component)

  • PropertyDef ('Map<String, Object>' :: common element structure)

    • name ('String' :: Name of the property)

    • type ('String' :: type of the property)

    • format ('String' :: format of the property (i.e. int64))

    • isCollection ('boolean' :: true if the property is a collection, false by default)

    • isEntity ('boolean' :: true if the property referes to another entity, false by default)

    • sameComponent ('boolean' :: true if the entity that the property refers to belonmgs to the same component, false by default)

    • description ('String' :: Description of the property)

    • required ('boolean' :: true if the proiperty is set as required)

    • constraints ('Map<String, Object>')

  • RelationShip ('Map<String, Object>' :: common element structure)

    • type ('String' :: type of the relationship (OneToOne, ManyToMany, etc…​))

    • entity ('String' :: destination entity name)

    • sameComponent ('boolean' :: true if the destination entity belongs to the same component of the source entity, false by default)

    • unidirectional ('boolean' :: true if the relationship is unidirectional, false by default)

  • PathDef ('Map<String, Object>' :: common element structure)

    • pathURI ('String' :: Uri of the path)

    • version ('String' :: version of the service)

    • operations ('List<OperationDef>' :: List of operations for this path)

  • OperationDef ('Map<String, Object>' :: common element structure)

    • type ('String' :: type of the operation (GET, PUT, etc…​))

    • parameters ('List<ParameterDef>' :: List of parameters)

    • operationId ('String' :: name of the operation prototype)

    • description ('String' :: JavaDoc Description of the operation)

    • summary ('List<PropertyDef>' :: JavaDoc operation Summary)

    • tags ('List<String>' :: List of diferent tags)

    • response ('ResponseDef' :: Response of the operation)

 — TODO

  • ParameterDef ('Map<String, Object>' :: common element structure)

    • isSearchCriteria ('boolean' :: true if the response is an SearchCriteria object)

    • inPath ('boolean' :: name oif the component that entity belongs to)

    • inQuery ('boolean' :: Full definition of the component that entity belongs to)

    • inHeader ('boolean' :: Description of the Entity)

    • mediaType ('String' :: String representation of the attribute’s value)

  • ResponseDef ('Map<String, Object>' :: common element structure)

    • isArray ('boolean' :: Name of the current Entity)

    • isPaginated ('boolean' :: name oif the component that entity belongs to)

    • isEntity ('boolean' :: Full definition of the component that entity belongs to)

    • isVoid ('boolean' :: Description of the Entity)

    • type ('String' :: String representation of the attribute’s value)

    • format ('String' :: String representation of the attribute’s value)

    • mediaType ('String' :: String representation of the attribute’s value)

Merger extensions

This plugin only provides an input reader, there is no support for OpenApi merging. Nevertheless, the files generated from an OpenApi file will be Java, XML, JSON, TS, etc…​ so, for each file to be generated defined at templates.xml, must set the mergeStartegy for the specific language (javamerge, javamerge_override, jsonmerge, etc…​)

<templates>
    ...
    <templateExtension ref="${variables.entityName}.java" mergeStrategy="javamerge"/>
    ...
    <templateExtension ref="${variables.entityName}dataGrid.component.ts" mergeStrategy="tsmerge"/>
    ...
    <templateExtension ref="en.json" mergeStrategy="jsonmerge"/>
</templates>

Usage

Writing OpenApi 3.0 contract file

The Swagger file must follow the OpenApi 3.0 standard to be readable by CobiGen, otherwise and error will be thrown. A full documentation about how to follow this standard can be found Swagger3 Docs.

The Swagger file must be at the core folder of your OASP4J project, like shown below:

Swagger at OASP4J Project

To be compatible with CobiGen and OASP4J, it must follow some specific configurations. This configurations also allows to avoid redundant definitions as SearchCriterias and PaginatedList objects are used at the services definitions.

Paths

  • Just adding the tags property at the end of the service definitions with the items SearchCriteria and/or paginated put into CobiGen knowledge that an standard OASP4J SearchCriteria and/or PaginateListTo object must be generated. That way, the Swagger file will be easier to write and even more understandable.

  • The path must start with the component name, that way this operation would be included into the component services list.

/componentnamemanagement/v1/entityname/customOperation/:
  post:
    summary: 'Summary of the operation'
    description: Description of the operation.
    operationId: customOperation
    responses:
      '200':
        description: Description of the response.
        content:
          application/json:
            schema:
              type: array
              items:
                $ref: '#/components/schemas/EntityName'
    requestBody:
      $ref: '#/components/requestBodies/EntityName'
    tags:
      - searchCriteria
      - paginated

Full example

This example yaml file can be download from here.

Warning
As you will see on the file, "x-component" tags are obligatory. They have to be defined for each component (entity).
openapi: 3.0.0
servers:
  - url: 'https://localhost:8081/server/services/rest'
info:
  title: Devon Example
  description: Example of a API definition
  version: 1.0.0
  x-rootpackage: rootPackage
paths:
  /datamanagement/v1/someData/{id}:
    get:
      operationId: findSomeData
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
            format: int64
            minimum: 0
            maximum: 50
      responses:
        '200':
          description: Any
  /datamanagement/v1/moreData/{id}:
    get:
      operationId: findMoreData
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
            format: int64
            minimum: 10
            maximum: 200
      responses:
        '200':
          description: Any
  /datamanagement/v1/someData/:
    post:
      responses:
        '200':
          description: Any
      requestBody:
        $ref: '#/components/requestBodies/SomeData'
      tags:
       - searchCriteria
  /datamanagement/v1/moreData/validateMoreData:
    post:
      responses:
       '200':
          description: Any
      requestBody:
        $ref: '#/components/requestBodies/MoreData'
components:
    schemas:
        SomeData:
          x-component: DataManagement
          description: Entity definiton of SomeData
          type: object
          properties:
            AnyString:
              type: string
              maxLength: 100
              minLength: 5
              uniqueItems: true
            furtherData:
              type: array
              items:
                $ref: '#/components/schemas/FurtherData'
        MoreData:
          x-component: DataManagement
          description: Entity definiton of Moredata
          type: object
          properties:
            anyNumber:
              type: number
              format: int64
              maximum: 100
              minimum: 0
            someData:
              $ref: '#/components/schemas/SomeData'
            allSomeData:
              type: array
              description: 'All SomeData'
              items:
                $ref: '#/components/schemas/SomeData'
          required:
            - saleExample
        FurtherData:
          x-component: AnotherComponent
          type: object
          properties:
            parent:
              $ref: '#/components/schemas/SomeData'
            valid:
              type: boolean
            someList:
              type: array
              items:
                type: string
    requestBodies:
        SomeData:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SomeData'
          required: true
        MoreData:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MoreData'
          required: true
 
Clone this wiki locally