Open API 0.0.1

Open API is an standard to define an API as a contractual agreement. It is used for documentation, server and client.

This is an initial preview release, with the primary focus being models. With a server preview. The implementation is not tied to a specific server, and most code is common.

The generation and parsing utilizes the corresponding JSON Schema module. Which parses the upstream schema into kotlin code. As this is automatic it is relatively easy to keep up to date with the specification.

Features

  • Multi platform generation of open api models.
  • Cursory Server support
  • Gradle plugin to generate models at build time

Gradle Plugin

Generation At this time is done through a gradle plugin

import design.animus.kotlin.mp.schemas.json.generator.GeneratorConfig
import design.animus.kotlin.mp.schemas.openapi.OpenAPIConfig

plugins {
  id("design.animus.kotlin.mp.schemas.open_api.generator")
  kotlin("multiplatform")
}

OpenAPIGeneratorConfig {
  schemas = listOf(
    OpenAPIConfig(
      GeneratorConfig(
        packageBase = "design.animus.kotlin.mp.schemas.openapi.tests.petstore",
        outputPath = File("$projectDir/generated"),
        schemaFile = File("$projectDir/main/resources/petstore.api.json"),
        schemaName = "PetStore",
        createBaseObject = false
      ),
      createServer = true,
      createModel = false,
      createClient = false
    )
  )
}

Using the gradle wrapper is done as follows. From either the command line, or gradle panel in Intellij.

./gradlew :tests:ktor:openAPIGenerate 

I usually approach this that the generated code goes into a directory generated which is not committed.

Serialization

This follows the same approach as json-schema Where there is a Component and ComponentSerializer.

If you were to serialize Pet to json it would follow

        Json.encodeToString(PetSerializer, Pet(...))

This is due to the additionalProperties and patternProperties, which are dynamic keys. That may, or may not be present in the final object. This allows serializaition with the more advanced feature of json schema and open api.

Initial Server Support

This is very early, doesn't really work. It has a single unit test, so not fleshed out.

The server component is taken as extension function approach. Types are generated around the expected path and HTTP verb. The HTTP server (ktor, vertx, etc.), can then call the extension function to create, or register a route.

Why?

API versioning says that a version should stay constant, and resources or parameters only change when a new version occurs. But this provides compile type safety.

If we had the following path of /pet/{petId} and petId was a UUID of required. Then it was updated to an optional Int. This compilation system would ensure at compile time that we account for the change of the route. Let's look at an example.

Ktor

Here we have an extension method that takes in the Open API operation. Generating the boiler pate for the route.

The actual type generated for the operation is above. At this time only url/path parameters are supported. It will cast the path parameters to a known data class. Allowing for type safety, and automatic attempted casting, as well as de-structuring.

Of note due to reflection limitations of multi platform. Like in kotlin-frm the type seeds information regarding position, and corresponding type. These are utilized to ensure that the parameters match the desired types. You will receive a runtime error if the use passes an incorrect type. But the compiler will help to ensure handling any changes in the parameters.

The route function is relatively simple. Given the operation we can automatically determine a lot of information to seed ktor. The resulting for the end user just has extra information already cast to known types, then provides the standard routing context.

Of note in the above the generics in the function are boxed based on interfaces. So if a client doesn't have a path parameters they will not be presented a data class.

Artifacts

Artifacts are available on GitL