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