:toc:
toc::[]

= 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 thought to follow a "code first" generation, with this plugin, now it can also follow 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...

NOTE: If you are not a CobiGen developer, you will be more interested in <<Usage, usage>>.

== 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,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
[source,xml]
----
<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`). 

=== Container Matcher 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 link:cobigen-core_configuration#containermatcher-node[`containerMatcher` semantics] to get more information about `containerMatchers` itself.)

[[openapi-variable-assignment]]
=== Variable Assignment 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
+
[source,xml]
----
<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-..."`).
+
[source,xml]
----
  <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 (e.g. com.devonfw.project).

image:images/howtos/openapi-gen/extensionPropertyFile.png[Swagger at devon4j Project, height="520",link="images/howtos/openapi-gen/extensionPropertyFile.png"]

If no extension with that name was found, then an empty string will be assigned. In the case of not defining the root package, then the code will be generated into `src/main/java`.

* type 'property' -> property of the Java Object
+
[source,xml]
----
<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:

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

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

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

.   `String *description*`: Description of this entity.

.   `List<PropertyDef> *properties*`: List containing all the properties of this entity. `PropertyDef` is an object that has the next properties:
    .. String name.
    .. String type.
    .. String format.
    .. String description.
    .. Boolean `isCollection`.
    .. Boolean `isEntity`.
    .. Boolean required.
    .. Map<String, Object> constraints

If no property with that name was found, then it will be set to `null`.

=== Full trigger configuration

[source,xml]
----
<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>
----

[[OpenAPI-input-reader]]
== 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)
** *header* (`HeaderDef` :: Definition of the header found at the top of the file)
** *name* ('String' :: Name of the current Entity)
** *`componentName`* ('String' :: name of the component the entity belongs to)
** *component* (`ComponentDef` :: Full definition of the component that entity belongs to)
** *description* ('String' :: Description of the Entity)
** *properties* (`List<PropertyDef>` :: List of properties the entity has)
** *`relationShips`* (`List<RelationShip>` :: List of Relationships the entity has)

* *`HeaderDef`* ('Map<String, Object>' :: common element structure)
** *info* (`InfoDef` :: Definition of the info found in the header)
** *servers* (`List<ServerDef>` :: List of servers the specification uses)

* *`InfoDef`* ('Map<String, Object>' :: common element structure)
** *title* ('String' :: The title of the specification)
** *description* ('String' :: The description of the specification)

* *`ServerDef`* ('Map<String, Object>' :: common element structure)
** *`URI`* ('String' :: String representation of the Server location)
** *description* ('String' :: description of the server)

* *`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 refers to another entity, false by default)
** *`sameComponent`* (`boolean` :: *true* if the entity that the property refers to belongs to the same component, false by default)
** *description* ('String' :: Description of the property)
** *required* (`boolean` :: *true* if the property 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)
** *`rootComponent`* ('String' :: the first segment of the path)
** *version* ('String' :: version of the service)
** *`pathURI`* ('String' :: URI of the path, the segment after the version)
** *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 different tags)
** *responses* (`List<ResponseDef>` :: Responses of the operation)

* *`ParameterDef`* ('Map<String, Object>' :: common element structure)
** *`isSearchCriteria`* (`boolean` :: *true* if the response is an `SearchCriteria` object)
** *`inPath`* (`boolean` :: *true* if this parameter is contained in the request path)
** *`inQuery`* (`boolean` :: *true* if this parameter is contained in a query)
** *`isBody`* (`boolean` :: *true* if this parameter is a response body)
** *`inHeader`* (`boolean` :: *true* if this parameter is contained in a header)
** *`mediaType`* ('String' :: String representation of the media type of the parameter)

* *`ResponseDef`* ('Map<String, Object>' :: common element structure)
** *`isArray`* (`boolean` :: *true* if the type of the response is an Array)
** *`isPaginated`* (`boolean` :: *true* if the type of the response is paginated)
** *`isVoid`* (`boolean` :: *true* if there is no type/an empty type)
** *`isEntity`* (`boolean` :: *true* if the type of the response is an Entity)
** *`entityRef`* (`EntityDef` :: Incomplete `EntityDef` containing the name and properties of the referenced Entity)
** *type* ('String' :: String representation of the attribute's value)
** *code* ('String' :: String representation of the HTTP status code)
** *`mediaTypes`* ('List<String>' :: List of media types that can be returned)
** *description* ('String' :: Description of the response)


== 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 `mergeStrategy` for the specific language (`javamerge`, `javamerge_override`, `jsonmerge`, etc...)

[source,xml]
----
<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 link:https://swagger.io/docs/specification/about/[Swagger3 Docs].

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

image:images/howtos/openapi-gen/openapi_howto1.png[Swagger at devon4j Project, width="450",link="images/howtos/openapi-gen/openapi_howto1.png"]

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

[[paths]]
=== 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 devon4j `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, and define an `x-component` tag with the component name. That way this service will be included into the component services list.

```yaml
  /componentnamemanagement/v1/entityname/customOperation/:
    x-component: componentnamemanagement
    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
```

That way, CobiGen will be able to generate the endpoint (REST service) `customOperation` on `componentmanagement`. If you do not specify the component to generate to (the `x-component` tag) then this service will not be taken into account for generation.

[[service]]
=== Service based generation

In previous CobiGen versions, we were able to generate code from a contract-first OpenAPI specification only when we defined components like the following:

```yaml
components:
    schemas:
        Shop:
          x-component: shopmanagement
          description: Entity definition of Shop
          type: object
          properties:
            shopExample:
              type: string
              maxLength: 100
              minLength: 5
              uniqueItems: true
```

We could not generate services without the definition of those components. 

In our current version, we have overcome it, so that now we are able to generate all the services independently. You just need to add an `x-component` tag with the name of the component that will make use of that service. See <<paths, here>>.

An small OpenAPI example defining only services can be found below:

```yaml
openapi: 3.0.0
servers:
  - url: 'https://localhost:8081/server/services/rest'
    description: Just some data
info:
  title: Devon Example
  description: Example of a API definition
  version: 1.0.0
  x-rootpackage: com.capgemini.spoc.openapi
paths:
  /salemanagement/v1/sale/{saleId}:
    x-component: salemanagement
    get:
      operationId: findSale
      parameters:
        - name: saleId
          in: path
          required: true
          description: The id of the pet to retrieve
          schema:
            type: string
      responses:
        '200':
          description: Any
  /salemanagement/v1/sale/{bla}:
    x-component: salemanagement
    get:
      operationId: findSaleBla
      parameters:
        - name: bla
          in: path
          required: true
          schema:
            type: integer
            format: int64
            minimum: 10
            maximum: 200
      responses:
        '200':
          description: Any

```

Then, the increment that you need to select for generating those services is _Crud devon4ng Service based Angular_:

image:images/howtos/openapi-gen/service_based.png[Service based generation, width="450",link="images/howtos/openapi-gen/service_based.png"]



=== Full example

This example yaml file can be download from link:files/devonfw.yml[here].

[WARNING] 
As you will see on the file, "x-component" tags are obligatory if you want to generate components (entities). They have to be defined for each one.
In addition, you will find the global variable "x-rootpackage" that are explained <<,here>>.


```yaml
openapi: 3.0.0
servers:
  - url: 'https://localhost:8081/server/services/rest'
    description: Just some data
info:
  title: Devon Example
  description: Example of a API definition
  version: 1.0.0
  x-rootpackage: com.devonfw.angular.test
paths:
  /shopmanagement/v1/shop/{shopId}:
    x-component: shopmanagement
    get:
      operationId: findShop
      parameters:
        - name: shopId
          in: path
          required: true
          schema:
            type: integer
            format: int64
            minimum: 0
            maximum: 50
      responses:
        '200':
          description: Any
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shop'
            text/plain:
              schema:
                type: string
        '404':
          description: Not found
  /salemanagement/v1/sale/{saleId}:
    x-component: salemanagement
    get:
      operationId: findSale
      parameters:
        - name: saleId
          in: path
          required: true
          description: The id of the pet to retrieve
          schema:
            type: string
      responses:
        '200':
          description: Any
  /salemanagement/v1/sale/:
    x-component: salemanagement
    post:
      responses:
        '200':
          description: Any
      requestBody:
        $ref: '#/components/requestBodies/SaleData'
      tags:
       - searchCriteria
  /shopmanagement/v1/shop/new:
    x-component: shopmanagement
    post:
      responses:
       '200':
          description: Any
      requestBody:
        $ref: '#/components/requestBodies/ShopData'
components:
    schemas:
        Shop:
          x-component: shopmanagement
          description: Entity definition of Shop
          type: object
          properties:
            shopExample:
              type: string
              maxLength: 100
              minLength: 5
              uniqueItems: true
            sales:
              type: array # Many to One relationship
              items:
                $ref: '#/components/schemas/Sale'
        Sale:
          x-component: salemanagement
          description: Entity definition of Shop
          type: object
          properties:
            saleExample:
              type: number
              format: int64
              maximum: 100
              minimum: 0
          required:
            - saleExample

    requestBodies:
        ShopData:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Shop'
          required: true
        SaleData:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Sale'
          required: true
 
 
 
```




