Kora фреймворк для написания Java / Kotlin приложений с упором на производительность, эффективность, прозрачность сделанный разработчиками Т-Банк / Тинькофф

Kora is a framework for writing Java / Kotlin applications with a focus on performance, efficiency, transparency made by T-Bank / Tinkoff developers

Skip to content

OpenAPI codegen

Module for creating declarative HTTP handlers HTTP server or create declarative HTTP clients from OpenAPI contracts using OpenAPI Generator plugin.

Dependency

Dependency build.gradle:

buildscript {
    dependencies {
        classpath("ru.tinkoff.kora:openapi-generator:1.1.15")
    }
}

Plugin dependency build.gradle:

plugins {
    id "org.openapi.generator" version "7.4.0"
}

Dependency build.gradle.kts:

buildscript {
    dependencies {
        classpath("ru.tinkoff.kora:openapi-generator:1.1.15")
    }
}

Plugin dependency build.gradle.kts:

plugins {
    id("org.openapi.generator") version("7.4.0")
}

Requires HTTP server or HTTP client module.

Configuration

Configuration is required for OpenAPI Generator plugin parameters:

Client

A minimal example of configuring a plugin to create a declarative HTTP client:

Kora's available plugin options:

  • clientConfigPrefix - configuration prefix of created HTTP clients
  • tags - possibility to put additional tags on created HTTP-clients
  • interceptors - ability to specify interceptors for HTTP clients
  • primaryAuth - specify which authorization mechanism to use as the primary one if several securitySchemes are specified in OpenAPI
  • securityConfigPrefix - prefix of authorization mechanism configuration Basic/ApiKey (configuration path will be specified prefix + name securitySchemes in OpenAPI, or just name in OpenAPI if prefix is not specified).
  • authAsMethodArgument - ability to specify authorization as an argument of an HTTP client method rather than through an interceptor
  • additionalContractAnnotations - ability to specify additional annotations over HTTP client methods
  • enableJsonNullable - Treat nullable=true and required=false schema fields as a JsonNullable wrapper
  • mode in which mode the generator should operate, available values:
openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml" //(1)!
    outputDir = "$buildDir/generated/openapi" //(2)!  
    apiPackage = "ru.tinkoff.kora.example.openapi.api" //(3)!
    modelPackage = "ru.tinkoff.kora.example.openapi.model" //(4)!
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker" //(5)!
    openapiNormalizer = [
        DISABLE_ALL: "true"
    ]
    configOptions = [
        mode: "java-client", //(6)!
        clientConfigPrefix: "httpClient.myclient" //(7)!
    ]
}
  1. Path to OpenAPI file from which classes will be created
  2. Directory where the files will be created
  3. Package from classes of delegates, controllers, converters, etc.
  4. Package from classes of models, DTOs, etc.
  5. Package from calling classes
  6. Mode of plugin operation (creating Java client / Kotlin / Java server, etc.)
  7. Prefix path to client configuration file

Kora's available plugin options:

  • clientConfigPrefix - configuration prefix of created HTTP clients
  • tags - possibility to put additional tags on created HTTP-clients
  • interceptors - ability to specify interceptors for HTTP clients
  • primaryAuth - specify which authorization mechanism to use as the primary one if several securitySchemes are specified in OpenAPI
  • securityConfigPrefix - prefix of authorization mechanism configuration Basic/ApiKey (configuration path will be specified prefix + name securitySchemes in OpenAPI, or just name in OpenAPI if prefix is not specified).
  • authAsMethodArgument - ability to specify authorization as an argument of an HTTP client method rather than through an interceptor
  • additionalContractAnnotations - ability to specify additional annotations over HTTP client methods
  • enableJsonNullable - Treat nullable=true and required=false schema fields as a JsonNullable wrapper
  • mode in which mode the generator should operate, available values:
    • kotlin-client - create synchronous client
    • kotlin-suspend-client - create suspend client
openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml" //(1)!
    outputDir = "$buildDir/generated/openapi" //(2)!
    apiPackage = "ru.tinkoff.kora.example.openapi.api" //(3)!
    modelPackage = "ru.tinkoff.kora.example.openapi.model" //(4)!
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker" //(5)!
    openapiNormalizer = mapOf(
        "DISABLE_ALL" to "true"
    )
    configOptions = mapOf(
        "mode" to "kotlin-client", //(6)!
        "clientConfigPrefix" to "httpClient.myclient" //(7)!
    )
}
  1. Path to OpenAPI file from which classes will be created
  2. Directory where the files will be created
  3. Package from classes of delegates, controllers, converters, etc.
  4. Package from classes of models, DTOs, etc.
  5. Package from calling classes
  6. Mode of plugin operation (creating Java client / Kotlin / Java server, etc.)
  7. Prefix path to client configuration file

Once created, the HTTP client will be available for deployment as a dependency on the created interface.

Interceptors

It is possible to put interceptors on created clients with @HttpClient annotation.

The value is a Json object whose key is the api tag from the contract, and the value is an object with type and tag fields, it is possible to specify both fields at the same time, or optionally one of them:

  • type - the implementation class of a particular interceptor
  • tag - tags of the interceptor (can be specified as an array of strings).

In order to do this, set the configOptions.interceptors parameter:

openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml"
    outputDir = "$buildDir/generated/openapi"
    apiPackage = "ru.tinkoff.kora.example.openapi.api"
    modelPackage = "ru.tinkoff.kora.example.openapi.model"
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker"
    openapiNormalizer = [
        DISABLE_ALL: "true"
    ]
    configOptions = [
        mode: "java-client",
        interceptors: """
                {
                  "*": [
                    {
                      "tag": "ru.tinkoff.example.MyTag"
                    }
                  ],
                  "pet": [
                    {
                      "type": "ru.tinkoff.example.MyInterceptor"
                    }
                  ],
                  "shop": [
                    {
                      "type": "ru.tinkoff.example.MyInterceptor",
                      "tag": "ru.tinkoff.example.MyTag"
                    }
                  ]
                }
                """
    ]
}
openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml"
    outputDir = "$buildDir/generated/openapi"
    apiPackage = "ru.tinkoff.kora.example.openapi.api"
    modelPackage = "ru.tinkoff.kora.example.openapi.model"
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker"
    openapiNormalizer = mapOf(
        "DISABLE_ALL" to "true"
    )
    configOptions = mapOf(
        "mode" to "kotlin-client",
        "interceptors" to """{
                  "*": [
                    {
                      "tag": "ru.tinkoff.example.MyTag"
                    }
                  ],
                  "pet": [
                    {
                      "type": "ru.tinkoff.example.MyInterceptor"
                    }
                  ],
                  "shop": [
                    {
                      "type": "ru.tinkoff.example.MyInterceptor",
                      "tag": "ru.tinkoff.example.MyTag"
                    }
                  ]
                }
                """
    )
}

Tags

It is possible to put parameters httpClientTag and telemetryTag on created clients with @HttpClient annotation. The value is a Json object, the key of which is the api tag from the contract, and the value is the object with the fields httpClientTag and telemetryTag.

For this purpose it is necessary to set the configOptions.tags parameter:

openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml"
    outputDir = "$buildDir/generated/openapi"
    apiPackage = "ru.tinkoff.kora.example.openapi.api"
    modelPackage = "ru.tinkoff.kora.example.openapi.model"
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker"
    openapiNormalizer = [
        DISABLE_ALL: "true"
    ]
    configOptions = [
        mode: "java-client",
        clientConfigPrefix: "httpClient.myclient",
        tags: """
              {
                "*": { // applies to all tags except those explicitly specified (in this case, instrument)
                  "httpClientTag": "some.tag.Common.class",
                  "telemetryTag": "some.tag.Common.class"
                },
                "instrument": { // apply to instrument
                  "httpClientTag": "some.tag.Instrument.class",
                  "telemetryTag": "some.tag.Instrument.class"
                }
              }
              """
    ]
}
openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml"
    outputDir = "$buildDir/generated/openapi"
    apiPackage = "ru.tinkoff.kora.example.openapi.api"
    modelPackage = "ru.tinkoff.kora.example.openapi.model"
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker"
    openapiNormalizer = mapOf(
        "DISABLE_ALL" to "true"
    )
    configOptions = mapOf(
        "mode" to "kotlin-client",
        "clientConfigPrefix" to "httpClient.myclient",
        "tags" to """{
                        "*": { // applies to all tags except those explicitly specified (in this case, instrument)
                          "httpClientTag": "some.tag.Common.class",
                          "telemetryTag": "some.tag.Common.class"
                        },
                        "instrument": { // apply to instrument
                          "httpClientTag": "some.tag.Instrument.class",
                          "telemetryTag": "some.tag.Instrument.class"
                        }
                     }
                     """
    )
}

Server

A minimal example of configuring a plugin to create HTTP server handlers:

Available Kora plugin parameters:

  • enableServerValidation - whether to create validators according to the OpenAPI secification description for the server and whether to enable validation on HTTP handlers: true, false.
  • requestInDelegateParams - whether to expected HttpServerRequest as a method argument: true, false
  • interceptors - ability to specify interceptors for HTTP controllers
  • additionalContractAnnotations - ability to specify additional annotations for controller methods
  • enableJsonNullable - Treat nullable=true and required=false schema fields as a JsonNullable wrapper
  • mode in which mode the generator should operate, available values:
    • java-server - create a synchronous server
    • java-async-server - create a CompletionStage server
    • java-reactive-server - create a reactive server, you need to connect Project Reactor yourself.
openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml" //(1)!
    outputDir = "$buildDir/generated/openapi" //(2)!  
    apiPackage = "ru.tinkoff.kora.example.openapi.api" //(3)!
    modelPackage = "ru.tinkoff.kora.example.openapi.model" //(4)!
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker" //(5)!
    openapiNormalizer = [
        DISABLE_ALL: "true"
    ]
    configOptions = [
        mode: "java-server" //(6)!
    ]
}
  1. Path to OpenAPI file from which classes will be created
  2. Directory where the files will be created
  3. Package from classes of delegates, controllers, converters, etc.
  4. Package from classes of models, DTOs, etc.
  5. Package from calling classes
  6. Mode of plugin operation (creating Java client / Kotlin / Java server, etc.)

Available Kora plugin parameters:

  • enableServerValidation - whether to create validators according to the OpenAPI secification description for the server and whether to enable validation on HTTP handlers: true, false.
  • requestInDelegateParams - whether to expected HttpServerRequest as a method argument: true, false
  • interceptors - ability to specify interceptors for HTTP controllers
  • additionalContractAnnotations - ability to specify additional annotations for controller methods
  • enableJsonNullable - Treat nullable=true and required=false schema fields as a JsonNullable wrapper
  • mode in which mode the generator should operate, available values:
    • kotlin-server - create synchronous server
    • kotlin-suspend-server - create suspend server
openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml" //(1)!
    outputDir = "$buildDir/generated/openapi" //(2)!
    apiPackage = "ru.tinkoff.kora.example.openapi.api" //(3)!
    modelPackage = "ru.tinkoff.kora.example.openapi.model" //(4)!
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker" //(5)!
    openapiNormalizer = mapOf(
        "DISABLE_ALL" to "true"
    )
    configOptions = mapOf(
        "mode" to "kotlin-server" //(6)!
    )
}
  1. Path to OpenAPI file from which classes will be created
  2. Directory where the files will be created
  3. Package from classes of delegates, controllers, converters, etc.
  4. Package from classes of models, DTOs, etc.
  5. Package from calling classes
  6. Mode of plugin operation (creating Java client / Kotlin / Java server, etc.)

Once created, the handlers will be automatically registered.

Validation

In order to generate models and controllers with annotations from the validation module, the enableServerValidation option must be set:

openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml"
    outputDir = "$buildDir/generated/openapi"
    apiPackage = "ru.tinkoff.kora.example.openapi.api"
    modelPackage = "ru.tinkoff.kora.example.openapi.model"
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker"
    openapiNormalizer = [
        DISABLE_ALL: "true"
    ]
    configOptions = [
        mode: "java-server",
        enableServerValidation: "true"  //(1)!
    ]
}
  1. Enabling validation on the HTTP server controller side
openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml"
    outputDir = "$buildDir/generated/openapi"
    apiPackage = "ru.tinkoff.kora.example.openapi.api"
    modelPackage = "ru.tinkoff.kora.example.openapi.model"
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker"
    openapiNormalizer = mapOf(
        "DISABLE_ALL" to "true"
    )
    configOptions = mapOf(
        "mode" to "kotlin-server",
        "enableServerValidation" to "true" //(1)!
    )
}
  1. Enabling validation on the HTTP server controller side

Interceptors

It is possible to put interceptors on created controllers with @HttpController annotation.

The value is a Json object whose key is the api tag from the contract, and the value is an object with type and tag fields, it is possible to specify both fields at the same time, or optionally one of them:

  • type - the implementation class of a particular interceptor
  • tag - tags of the interceptor (can be specified as an array of strings).

In order to do this, set the configOptions.interceptors parameter:

openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml"
    outputDir = "$buildDir/generated/openapi"
    apiPackage = "ru.tinkoff.kora.example.openapi.api"
    modelPackage = "ru.tinkoff.kora.example.openapi.model"
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker"
    openapiNormalizer = [
        DISABLE_ALL: "true"
    ]
    configOptions = [
        mode: "java-client",
        interceptors: """
                {
                  "*": [
                    {
                      "tag": "ru.tinkoff.example.MyTag"
                    }
                  ],
                  "pet": [
                    {
                      "type": "ru.tinkoff.example.MyInterceptor"
                    }
                  ],
                  "shop": [
                    {
                      "type": "ru.tinkoff.example.MyInterceptor",
                      "tag": "ru.tinkoff.example.MyTag"
                    }
                  ]
                }
                """
    ]
}
openApiGenerate {
    generatorName = "kora"
    group = "openapi tools"
    inputSpec = "$projectDir/src/main/resources/openapi/openapi.yaml"
    outputDir = "$buildDir/generated/openapi"
    apiPackage = "ru.tinkoff.kora.example.openapi.api"
    modelPackage = "ru.tinkoff.kora.example.openapi.model"
    invokerPackage = "ru.tinkoff.kora.example.openapi.invoker"
    openapiNormalizer = mapOf(
        "DISABLE_ALL" to "true"
    )
    configOptions = mapOf(
        "mode" to "kotlin-client",
        "interceptors" to """{
                  "*": [
                    {
                      "tag": "ru.tinkoff.example.MyTag"
                    }
                  ],
                  "pet": [
                    {
                      "type": "ru.tinkoff.example.MyInterceptor"
                    }
                  ],
                  "shop": [
                    {
                      "type": "ru.tinkoff.example.MyInterceptor",
                      "tag": "ru.tinkoff.example.MyTag"
                    }
                  ]
                }
                """
    )
}

Recommendations

????+ warning "Advice"

In case you have something that is not created by the plugin, or the behavior is different from what you want or other versions,
you should carefully check the [plugin configuration](#configuration) settings and examine them, 
as they may affect the results of how classes are created.

Starting with `7.0.0` version of the plugin, the `SIMPLIFY_ONEOF_ANYOF` rule enabled by default at the `openapiNormalizer` parameter 
may lead to some not obvious generator results.