-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
474 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
root: ./docs/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Summary | ||
|
||
* [Introduction](index.md) | ||
* [Plugins](plugins/index.md) | ||
* [Notarized Application](plugins/notarized_application.md) | ||
* [Notarized Route](plugins/notarized_route.md) | ||
* [Notarized Locations](plugins/notarized_locations.md) | ||
* [The Playground](playground.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,101 @@ | ||
# Kompendium | ||
Kompendium is intended to be a non-invasive OpenAPI spec generator for [Ktor](https://ktor.io) APIs. By operating | ||
entirely through Ktor's plugin architecture, Kompendium allows you to incrementally document your API without requiring | ||
you to rip out and replace the amazing code you have already written. | ||
|
||
Hi :) | ||
# Compatibility | ||
|
||
| Kompendium | Ktor | OpenAPI | | ||
|------------|------|---------| | ||
| 1.X | 1 | 3.0 | | ||
| 2.X | 1 | 3.0 | | ||
| 3.X | 2 | 3.1 | | ||
|
||
> These docs are focused solely on Kompendium 3, previous versions should be considered deprecated and no longer | ||
> maintained | ||
# Getting Started | ||
|
||
## Adding the Artifact | ||
|
||
All Kompendium artifacts are published to Maven Central. Most Kompendium users will only need to import the core | ||
dependency | ||
|
||
```kotlin | ||
dependencies { | ||
// other dependencies... | ||
implementation("io.bkbn:kompendium-core:latest.release") | ||
} | ||
``` | ||
|
||
## Notarizing a Ktor Application | ||
|
||
Once we have added the dependencies, installed the `NotarizedApplication` plugin. This is an application-level | ||
Ktor plugin that is used to instantiate and configure Kompendium. Your OpenAPI spec metadata will go here, along with | ||
custom type overrides (typically useful for custom scalars such as dates and times), along with other configurations. | ||
|
||
```kotlin | ||
private fun Application.mainModule() { | ||
install(NotarizedApplication()) { | ||
spec = OpenApiSpec( | ||
// ... | ||
) | ||
} | ||
} | ||
``` | ||
|
||
At this point, you will have a valid OpenAPI specification generated at runtime, which can be accessed by default | ||
at the `/openapi.json` path of your api. | ||
|
||
For more detail on the `NotarizedApplication` plugin, please see the [docs](./plugins/notarized_application.md) | ||
|
||
## Notarizing a Ktor Route | ||
|
||
Once you have notarized your application, you can begin to notarize individual routes using the `NotarizedRoute` plugin. | ||
This is a route-level Ktor plugin that is used to configure the documentation for a specific endpoint of your API. The | ||
route documentation will be piped back to the application-level plugin, and will be automatically injected into the | ||
OpenApi specification. | ||
|
||
Setting up documentation on a route is easiest achieved by creating an extension function on the Ktor `Route` class | ||
|
||
```kotlin | ||
private fun Route.documentation() { | ||
install(NotarizedRoute()) { | ||
parameters = listOf( | ||
Parameter( | ||
name = "id", | ||
`in` = Parameter.Location.path, | ||
schema = TypeDefinition.STRING | ||
) | ||
) | ||
get = GetInfo.builder { | ||
summary("Get user by id") | ||
description("A very neat endpoint!") | ||
response { | ||
responseCode(HttpStatusCode.OK) | ||
responseType<ExampleResponse>() | ||
description("Will return whether or not the user is real 😱") | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Once you have created your documentation function, it can be attached to the route simply by calling it at the desired | ||
path. | ||
|
||
```kotlin | ||
private fun Application.mainModule() { | ||
// ... | ||
routing { | ||
// ... | ||
route("/{id}") { | ||
documentation() | ||
get { | ||
// ... | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
For more details on the `NotarizedRoute` plugin, please see the [docs](./plugins/notarized_route.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
The Playground is a module inside the Kompendium repository that provides out of the box examples for a variety of | ||
Kompendium features. | ||
|
||
At the moment, the following playground applications are | ||
|
||
| Example | Description | | ||
|--------------|------------------------------------------------------------| | ||
| Basic | A minimally viable Kompendium application | | ||
| Auth | Documenting authenticated routes | | ||
| Custom Types | Documenting custom scalars to be used by Kompendium | | ||
| Exceptions | Documenting exception responses | | ||
| Gson | Serialization using Gson instead of the default Kotlinx | | ||
| Hidden Docs | Place your generated documentation behind authorization | | ||
| Jackson | Serialization using Jackson instead of the default KotlinX | | ||
| Locations | Using the Ktor Locations API to define routes | | ||
|
||
You can find all of the playground | ||
examples [here](https://github.com/bkbnio/kompendium/tree/main/playground/src/main/kotlin/io/bkbn/kompendium/playground) | ||
in the Kompendium repository |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
Plugins are the lifeblood of Kompendium. | ||
|
||
It starts with the `NotarizedApplication`, where Kompendium is instantiated and attached to the API. This is where spec | ||
metadata is defined, custom types are defined, and more. | ||
|
||
From there, a `NotarizedRoute` plugin is attached to each route you wish to document. This allows API documentation to | ||
be an iterative process. Each route you notarize will be picked up and injected into the OpenAPI spec that Kompendium | ||
generates for you. | ||
|
||
Finally, there is the `NotarizedLocations` plugin that allows you to leverage and document your usage of the | ||
Ktor [Locations](https://ktor.io/docs/locations.html) API. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
The `NotarizedApplication` plugin sits at the center of the entire Kompendium setup. It is a pre-requisite to | ||
installing any other Kompendium plugins. | ||
|
||
# Configuration | ||
|
||
Very little configuration is needed for a basic documentation setup, but | ||
several configuration options are available that allow you to modify Kompendium to fit your needs. | ||
|
||
## Spec | ||
|
||
This is where you will define the server metadata that lives outside the scope of any specific route. For | ||
full information, you can inspect the `OpenApiSpec` data class, and of course | ||
reference [OpenAPI spec](https://spec.openapis.org/oas/v3.1.0) itself. | ||
|
||
> ⚠️ Please note, the `path` field of the `OpenApiSpec` is intended to be filled in by `NotarizedRoute` plugin | ||
> definitions. Writing custom paths manually could lead to unexpected behavior | ||
## Custom Routing | ||
|
||
For public facing APIs, having the default endpoint exposed at `/openapi.json` is totally fine. However, if you need | ||
more granular control over the route that exposes the generated schema, you can modify the `openApiJson` config value. | ||
|
||
For example, if we want to hide our schema behind a basic auth check, we could do the following | ||
|
||
```kotlin | ||
private fun Application.mainModule() { | ||
// Install content negotiation, auth, etc... | ||
install(NotarizedApplication()) { | ||
// ... | ||
openApiJson = { | ||
authenticate("basic") { | ||
route("/openapi.json") { | ||
get { | ||
call.respond( | ||
HttpStatusCode.OK, | ||
this@route.application.attributes[KompendiumAttributes.openApiSpec] | ||
) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Custom Types | ||
|
||
Kompendium is _really_ good at converting simple scalar and complex objects into JsonSchema compliant specs. However, | ||
there is a subset of values that cause it trouble. These are most commonly classes that produce "complex scalars", | ||
such as dates and times, along with object representations of scalars such as `BigInteger`. | ||
|
||
In situations like this, you will need to define a map of custom types to JsonSchema definitions that Kompendium can use | ||
to short-circuit its type analysis. | ||
|
||
For example, say we would like to serialize `kotlinx.datetime.Instant` entities as a field in our response objects. We | ||
would need to add it as a custom type. | ||
|
||
```kotlin | ||
private fun Application.mainModule() { | ||
// ... | ||
install(NotarizedApplication()) { | ||
spec = baseSpec | ||
customTypes = mapOf( | ||
typeOf<Instant>() to TypeDefinition(type = "string", format = "date-time") | ||
) | ||
} | ||
} | ||
``` | ||
|
||
Doing this will save it in a cache that our `NotarizedRoute` plugin definitions will check from prior to attempting to | ||
perform type inspection. | ||
|
||
This means that we only need to define our custom type once, and then Kompendium will reuse it across the entire | ||
application. | ||
|
||
> While intended for custom scalars, there is nothing stopping you from leveraging custom types to circumvent type | ||
> analysis | ||
> on any class you choose. If you have an alternative method of generating JsonSchema definitions, you could put them | ||
> all | ||
> in this map and effectively prevent Kompendium from having to do any reflection | ||
## Schema Configurator | ||
|
||
The `SchemaConfigurator` is an interface that allows users to bridge the gap between Kompendium serialization and custom | ||
serialization strategies that the serializer they are using for their API. For example, if you are using KotlinX | ||
serialization in order to convert kotlin fields from camel case to snake case, you could leverage | ||
the `KotlinXSchemaConfigurator` in order to instruct Kompendium on how to serialize values properly. | ||
|
||
```kotlin | ||
private fun Application.mainModule() { | ||
install(ContentNegotiation) { | ||
json(Json { | ||
serializersModule = KompendiumSerializersModule.module | ||
encodeDefaults = true | ||
explicitNulls = false | ||
}) | ||
} | ||
install(NotarizedApplication()) { | ||
spec = baseSpec | ||
// Adds support for @Transient and @SerialName | ||
// If you are not using them this is not required. | ||
schemaConfigurator = KotlinXSchemaConfigurator() | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
The Ktor Locations API is an experimental API that allows users to add increased type safety to their defined routes. | ||
|
||
You can read more about it [here](https://ktor.io/docs/locations.html). | ||
|
||
Kompendium supports Locations through an ancillary module `kompendium-locations` | ||
|
||
## Adding the Artifact | ||
|
||
Prior to documenting your locations, you will need to add the artifact to your gradle build file. | ||
|
||
```kotlin | ||
dependencies { | ||
implementation("io.bkbn:kompendium-locations:latest.release") | ||
} | ||
``` | ||
|
||
## Installing Plugin | ||
|
||
Once you have installed the dependency, you can install the plugin. The `NotarizedLocations` plugin is an _application_ | ||
level plugin, and **must** be install after both the `NotarizedApplication` plugin and the Ktor `Locations` plugin. | ||
|
||
```kotlin | ||
private fun Application.mainModule() { | ||
install(Locations) | ||
install(NotarizedApplication()) { | ||
spec = baseSpec | ||
} | ||
install(NotarizedLocations()) { | ||
locations = mapOf( | ||
Listing::class to NotarizedLocations.LocationMetadata( | ||
parameters = listOf( | ||
Parameter( | ||
name = "name", | ||
`in` = Parameter.Location.path, | ||
schema = TypeDefinition.STRING | ||
), | ||
Parameter( | ||
name = "page", | ||
`in` = Parameter.Location.path, | ||
schema = TypeDefinition.INT | ||
) | ||
), | ||
get = GetInfo.builder { | ||
summary("Get user by id") | ||
description("A very neat endpoint!") | ||
response { | ||
responseCode(HttpStatusCode.OK) | ||
responseType<ExampleResponse>() | ||
description("Will return whether or not the user is real 😱") | ||
} | ||
} | ||
), | ||
) | ||
} | ||
} | ||
``` | ||
|
||
Here, the `locations` property is a map of `KClass<*>` to metadata describing that locations metadata. This | ||
metadata is functionally identical to how a standard `NotarizedRoute` is defined. | ||
|
||
> ⚠️ If you try to map a class that is not annotated with the ktor `@Location` annotation, you will get a runtime | ||
> exception! |
Oops, something went wrong.