Compare commits
148 Commits
v3.14.3
...
v4.0.0-tes
Author | SHA1 | Date | |
---|---|---|---|
c53c0b045d | |||
fb0d3bb1dd | |||
1580d679d0 | |||
540dad1f00 | |||
21f922a296 | |||
fa7b1e5833 | |||
35110e8ad4 | |||
18d64a2ed1 | |||
4ffb04976a | |||
76b6288216 | |||
80f6f28b57 | |||
bf8b2b40f6 | |||
7c6826b366 | |||
ee56c8023e | |||
01c4e0558d | |||
a760121af5 | |||
b80ff3a998 | |||
8141be4b21 | |||
7fbc2523e2 | |||
3349e9dab7 | |||
da8439e1b7 | |||
e2d7d16571 | |||
f348910b63 | |||
7ed4c8075b | |||
87ba80b6bb | |||
e10f3b4c47 | |||
a7853ce353 | |||
da654e5664 | |||
d21434fdd0 | |||
8a94f6e498 | |||
7a58914466 | |||
2da6088303 | |||
1ad82f7e8e | |||
6117443c8b | |||
f8e098eb07 | |||
e2affe3f4b | |||
a78b51be07 | |||
fa96923a5a | |||
464fa2d98a | |||
11e0d248b5 | |||
2612010a88 | |||
cb78d19b89 | |||
28bce9c443 | |||
d7dad48808 | |||
2ea97ba2c2 | |||
944c632a1d | |||
d1c52c010e | |||
d9f78b4e9f | |||
dcde50de0a | |||
889fdb41d7 | |||
837108bbef | |||
ee9e3b03b3 | |||
3cc6e9f2e7 | |||
60eb3b6474 | |||
65c0137a46 | |||
c277e559f4 | |||
8a161466d6 | |||
9a4ad2f737 | |||
eb4d3ca7de | |||
7a8bb43675 | |||
ac464ea9ca | |||
c7545b1072 | |||
353637bb94 | |||
3d1fc566d4 | |||
79ddb2e4ae | |||
d004166a2e | |||
dc6dbdf1c0 | |||
0b9b8325ce | |||
6d89773448 | |||
b236530991 | |||
8caee55f43 | |||
13042ba385 | |||
26bd7178c9 | |||
9bc39e9767 | |||
57555587e0 | |||
47a9f0a145 | |||
789b750994 | |||
65fb7fbd55 | |||
89bd9dadd0 | |||
6ca8261cd2 | |||
ae5ddc35e6 | |||
dde9e1c0a7 | |||
8328351227 | |||
36a09ef3ca | |||
261e91a750 | |||
701ab7670e | |||
62280435cd | |||
18ddb13625 | |||
b5b2e462ac | |||
1bb52347a0 | |||
80f8ce6e52 | |||
1f504e8850 | |||
18d545bbfe | |||
e03868c42e | |||
e888207773 | |||
66943ceb5a | |||
e9342b9354 | |||
480b1ccdff | |||
95009090ba | |||
10ce370c9e | |||
1814fe4eb2 | |||
b0387313fd | |||
86c4800748 | |||
01052268f8 | |||
5c6f2c93e8 | |||
ccd46b15e7 | |||
eab1bb4ce7 | |||
3fdfe875f4 | |||
1b3cf0dbeb | |||
2410c81797 | |||
833bf463e7 | |||
b7e96019be | |||
aa5eb39a30 | |||
d002ec7fd9 | |||
650eb0f0d3 | |||
1a4568a49b | |||
f2786fec08 | |||
904cc6816d | |||
a06821a910 | |||
a9dffcf8a5 | |||
548b1ff4bc | |||
6f85ee476a | |||
326bbb4419 | |||
438eb52dc8 | |||
1fd59db4e1 | |||
238e372460 | |||
473e2c22af | |||
7fb7e688ac | |||
9f2c27aa0f | |||
dc762d1d3e | |||
b039803246 | |||
6ec240d7cd | |||
dd57347b90 | |||
43c8fd1338 | |||
6c2725680e | |||
84dfe4e6fe | |||
a0c1abf011 | |||
25820d0920 | |||
7c18283ac8 | |||
c5f3b7fa8b | |||
8b81010d72 | |||
c07ffa6a14 | |||
7793908ab6 | |||
bf6d08c2bd | |||
68bae4918e | |||
592c116c3b | |||
c7fe5c288f | |||
e059633055 |
10
.envrc
Normal file
10
.envrc
Normal file
@ -0,0 +1,10 @@
|
||||
if ! has nix_direnv_version || ! nix_direnv_version 3.0.5; then
|
||||
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.5/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs="
|
||||
fi
|
||||
|
||||
watch_file flake.nix
|
||||
watch_file flake.lock
|
||||
if ! use flake . --impure
|
||||
then
|
||||
echo "Flake could not be used... something is broken :(" >&2
|
||||
fi
|
17
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Documentation
|
||||
about: Suggest an improvement to the documentation for Kompendium
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Please describe the problem with the current documentation.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Link any docs references**
|
||||
Link to existing docs if incorrect, or references from another project if you have an example.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
18
.github/workflows/pr_checks.yml
vendored
18
.github/workflows/pr_checks.yml
vendored
@ -4,12 +4,12 @@ jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '17'
|
||||
java-version: '21'
|
||||
- name: Lint
|
||||
uses: burrunan/gradle-cache-action@v1
|
||||
with:
|
||||
@ -18,14 +18,14 @@ jobs:
|
||||
unit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '17'
|
||||
java-version: '21'
|
||||
- name: Unit Tests
|
||||
uses: burrunan/gradle-cache-action@v1
|
||||
with:
|
||||
gradle-version: wrapper
|
||||
arguments: test koverMergedReport
|
||||
arguments: test koverHtmlReport
|
||||
|
25
.github/workflows/publish.yml
vendored
25
.github/workflows/publish.yml
vendored
@ -1,25 +0,0 @@
|
||||
name: Publish to GitHub Packages
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths-ignore:
|
||||
- docs/**
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SONATYPE_SIGNING_KEY }}
|
||||
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SONATYPE_SIGNING_PASSWORD }}
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '17'
|
||||
- name: Publish to GitHub Packages
|
||||
uses: burrunan/gradle-cache-action@v1
|
||||
with:
|
||||
gradle-version: wrapper
|
||||
arguments: publishAllPublicationsToGithubPackagesRepository
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
21
.github/workflows/release.yml
vendored
21
.github/workflows/release.yml
vendored
@ -4,25 +4,26 @@ on:
|
||||
types:
|
||||
- prereleased
|
||||
- released
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SONATYPE_SIGNING_KEY }}
|
||||
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SONATYPE_SIGNING_PASSWORD }}
|
||||
jobs:
|
||||
publish-to-nexus:
|
||||
publish-to-maven-central:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '17'
|
||||
java-version: '21'
|
||||
- name: Publish to Maven Central
|
||||
uses: burrunan/gradle-cache-action@v1
|
||||
with:
|
||||
gradle-version: wrapper
|
||||
arguments: publishToSonatype closeAndReleaseSonatypeStagingRepository
|
||||
arguments: publishAndReleaseToMavenCentral
|
||||
properties: |
|
||||
SONATYPE_AUTOMATIC_RELEASE=true
|
||||
release=true
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USER }}
|
||||
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
|
||||
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
|
||||
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
|
||||
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MAVEN_SIGNING_KEY }}
|
||||
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MAVEN_SIGNING_KEY_ID }}
|
||||
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MAVEN_SIGNING_KEY_PASSPHRASE }}
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.gradle
|
||||
build
|
||||
.idea
|
||||
.direnv
|
||||
|
39
CHANGELOG.md
39
CHANGELOG.md
@ -2,6 +2,9 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Updates for Ktor 3.x support
|
||||
- Remove deprecated `NotarizedLocations`
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
@ -12,6 +15,42 @@
|
||||
|
||||
## Released
|
||||
|
||||
## [4.0.0] - January 21st, 2024
|
||||
|
||||
### Added
|
||||
|
||||
- All changes from alpha release
|
||||
|
||||
### Changed
|
||||
|
||||
- Enrichments now mirror schema types
|
||||
|
||||
## [4.0.0-alpha] - September 3rd, 2023
|
||||
|
||||
### Added
|
||||
|
||||
- Support for `type` on sealed interfaces
|
||||
- Ability to provide custom serializers
|
||||
|
||||
### Fixed
|
||||
|
||||
- Exception thrown when inheriting variable
|
||||
- Notarized routes not discarded on test completion
|
||||
- Data classes with property members breaks schema generation
|
||||
- Security cannot be applied to individual path operations
|
||||
- Serialization fails on generic response
|
||||
- Parameter example descriptions not being applied
|
||||
|
||||
### Removed
|
||||
|
||||
- Out of the box support for Jackson and Gson (can still be implemented through custom schema configurators)
|
||||
|
||||
## [3.14.4] - June 5th, 2023
|
||||
|
||||
### Changed
|
||||
|
||||
- Components definitions were not in the proper schema section. Prefixed the path with component slug in `protobuf java converter`.
|
||||
|
||||
## [3.14.3] - May 22nd, 2023
|
||||
|
||||
### Added
|
||||
|
@ -1,21 +1,19 @@
|
||||
import com.vanniktech.maven.publish.SonatypeHost
|
||||
import io.bkbn.sourdough.gradle.library.jvm.LibraryJvmPlugin
|
||||
import io.bkbn.sourdough.gradle.library.jvm.LibraryJvmExtension
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.8.21" apply false
|
||||
kotlin("plugin.serialization") version "1.8.21" apply false
|
||||
id("io.bkbn.sourdough.library.jvm") version "0.12.0" apply false
|
||||
id("io.bkbn.sourdough.application.jvm") version "0.12.0" apply false
|
||||
id("io.bkbn.sourdough.root") version "0.12.0"
|
||||
id("com.github.jakemarsden.git-hooks") version "0.0.2"
|
||||
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
||||
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
|
||||
kotlin("jvm") version "2.0.21" apply false
|
||||
kotlin("plugin.serialization") version "2.0.21" apply false
|
||||
id("io.bkbn.sourdough.library.jvm") version "0.13.0" apply false
|
||||
id("io.bkbn.sourdough.application.jvm") version "0.13.0" apply false
|
||||
id("com.vanniktech.maven.publish") version "0.30.0" apply false
|
||||
id("io.bkbn.sourdough.root") version "0.13.0"
|
||||
id("org.jetbrains.kotlinx.kover") version "0.8.3"
|
||||
}
|
||||
|
||||
gitHooks {
|
||||
setHooks(
|
||||
mapOf(
|
||||
"pre-commit" to "detekt",
|
||||
"pre-push" to "test"
|
||||
)
|
||||
)
|
||||
dependencies {
|
||||
subprojects.forEach { kover(it) }
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@ -31,15 +29,16 @@ allprojects {
|
||||
}
|
||||
|
||||
subprojects {
|
||||
plugins.withType(io.bkbn.sourdough.gradle.library.jvm.LibraryJvmPlugin::class) {
|
||||
extensions.configure(io.bkbn.sourdough.gradle.library.jvm.LibraryJvmExtension::class) {
|
||||
plugins.withType(LibraryJvmPlugin::class) {
|
||||
extensions.configure(LibraryJvmExtension::class) {
|
||||
githubOrg.set("bkbnio")
|
||||
githubRepo.set("kompendium")
|
||||
licenseName.set("MIT License")
|
||||
licenseUrl.set("https://mit-license.org")
|
||||
developerId.set("unredundant")
|
||||
developerId.set("brizzbuzz")
|
||||
developerName.set("Ryan Brink")
|
||||
developerEmail.set("admin@bkbn.io")
|
||||
sonatypeHost.set(SonatypeHost.CENTRAL_PORTAL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ plugins {
|
||||
id("io.bkbn.sourdough.library.jvm")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
id("com.adarshr.test-logger")
|
||||
id("maven-publish")
|
||||
id("com.vanniktech.maven.publish")
|
||||
id("java-library")
|
||||
id("signing")
|
||||
id("java-test-fixtures")
|
||||
@ -20,6 +20,7 @@ sourdoughLibrary {
|
||||
dependencies {
|
||||
// VERSIONS
|
||||
val kotestVersion: String by project
|
||||
val kotlinSerializeVersion: String by project
|
||||
val ktorVersion: String by project
|
||||
val detektVersion: String by project
|
||||
|
||||
@ -43,13 +44,11 @@ dependencies {
|
||||
testFixturesApi("io.kotest:kotest-assertions-core-jvm:$kotestVersion")
|
||||
testFixturesApi("io.kotest:kotest-property-jvm:$kotestVersion")
|
||||
testFixturesApi("io.kotest:kotest-assertions-json-jvm:$kotestVersion")
|
||||
testFixturesApi("io.kotest:kotest-assertions-ktor-jvm:4.4.3")
|
||||
testFixturesApi("io.kotest.extensions:kotest-assertions-ktor:2.0.0")
|
||||
|
||||
testFixturesApi("io.ktor:ktor-server-core:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-server-test-host:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-serialization:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-serialization-jackson:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-serialization-gson:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-server-content-negotiation:$ktorVersion")
|
||||
testFixturesApi("io.ktor:ktor-server-auth:$ktorVersion")
|
||||
@ -59,7 +58,7 @@ dependencies {
|
||||
|
||||
testFixturesApi("dev.forst:ktor-api-key:2.2.4")
|
||||
|
||||
testFixturesApi("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||
testFixturesApi("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializeVersion")
|
||||
}
|
||||
|
||||
testing {
|
||||
|
@ -10,6 +10,7 @@ class DeleteInfo private constructor(
|
||||
override val summary: String,
|
||||
override val description: String,
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val security: Map<String, List<String>>?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
@ -33,7 +34,8 @@ class DeleteInfo private constructor(
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
parameters = parameters,
|
||||
security = security
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ class GetInfo private constructor(
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
override val parameters: List<Parameter>,
|
||||
override val security: Map<String, List<String>>?
|
||||
) : MethodInfo {
|
||||
|
||||
companion object {
|
||||
@ -33,7 +34,8 @@ class GetInfo private constructor(
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
parameters = parameters,
|
||||
security = security
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ class HeadInfo private constructor(
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
override val parameters: List<Parameter>,
|
||||
override val security: Map<String, List<String>>?,
|
||||
) : MethodInfo {
|
||||
|
||||
companion object {
|
||||
@ -33,7 +34,8 @@ class HeadInfo private constructor(
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
parameters = parameters,
|
||||
security = security,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,9 @@ sealed interface MethodInfo {
|
||||
val tags: Set<String>
|
||||
val summary: String
|
||||
val description: String
|
||||
|
||||
val security: Map<String, List<String>>?
|
||||
get() = null
|
||||
val externalDocumentation: ExternalDocumentation?
|
||||
get() = null
|
||||
val operationId: String?
|
||||
@ -28,6 +31,7 @@ sealed interface MethodInfo {
|
||||
internal var tags: Set<String> = emptySet()
|
||||
internal var parameters: List<Parameter> = emptyList()
|
||||
internal var errors: MutableList<ResponseInfo> = mutableListOf()
|
||||
internal var security: Map<String, List<String>>? = null
|
||||
|
||||
fun response(init: ResponseInfo.Builder.() -> Unit) = apply {
|
||||
val builder = ResponseInfo.Builder()
|
||||
@ -59,6 +63,8 @@ sealed interface MethodInfo {
|
||||
|
||||
fun parameters(vararg parameters: Parameter) = apply { this.parameters = parameters.toList() }
|
||||
|
||||
fun security(security: Map<String, List<String>>) = apply { this.security = security }
|
||||
|
||||
abstract fun build(): T
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ class OptionsInfo private constructor(
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
override val parameters: List<Parameter>,
|
||||
override val security: Map<String, List<String>>?,
|
||||
) : MethodInfo {
|
||||
|
||||
companion object {
|
||||
@ -33,7 +34,8 @@ class OptionsInfo private constructor(
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
parameters = parameters,
|
||||
security = security,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ class PatchInfo private constructor(
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
override val parameters: List<Parameter>,
|
||||
override val security: Map<String, List<String>>?,
|
||||
) : MethodInfoWithRequest {
|
||||
|
||||
companion object {
|
||||
@ -35,7 +36,8 @@ class PatchInfo private constructor(
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
parameters = parameters,
|
||||
security = security,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ class PostInfo private constructor(
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
override val parameters: List<Parameter>,
|
||||
override val security: Map<String, List<String>>?,
|
||||
) : MethodInfoWithRequest {
|
||||
|
||||
companion object {
|
||||
@ -35,7 +36,8 @@ class PostInfo private constructor(
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
parameters = parameters,
|
||||
security = security,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ class PutInfo private constructor(
|
||||
override val externalDocumentation: ExternalDocumentation?,
|
||||
override val operationId: String?,
|
||||
override val deprecated: Boolean,
|
||||
override val parameters: List<Parameter>
|
||||
override val parameters: List<Parameter>,
|
||||
override val security: Map<String, List<String>>?,
|
||||
) : MethodInfoWithRequest {
|
||||
|
||||
companion object {
|
||||
@ -35,7 +36,8 @@ class PutInfo private constructor(
|
||||
externalDocumentation = externalDocumentation,
|
||||
operationId = operationId,
|
||||
deprecated = deprecated,
|
||||
parameters = parameters
|
||||
parameters = parameters,
|
||||
security = security,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import kotlin.reflect.typeOf
|
||||
|
||||
class RequestInfo private constructor(
|
||||
val requestType: KType,
|
||||
val typeEnrichment: TypeEnrichment<*>?,
|
||||
val enrichment: TypeEnrichment<*>?,
|
||||
val description: String,
|
||||
val examples: Map<String, MediaType.Example>?,
|
||||
val mediaTypes: Set<String>,
|
||||
@ -49,8 +49,8 @@ class RequestInfo private constructor(
|
||||
|
||||
fun description(s: String) = apply { this.description = s }
|
||||
|
||||
fun examples(vararg e: Pair<String, Any>) = apply {
|
||||
this.examples = e.toMap().mapValues { (_, v) -> MediaType.Example(v) }
|
||||
fun examples(vararg e: Pair<String, MediaType.Example>) = apply {
|
||||
this.examples = e.toMap()
|
||||
}
|
||||
|
||||
fun mediaTypes(vararg m: String) = apply {
|
||||
@ -60,7 +60,7 @@ class RequestInfo private constructor(
|
||||
fun build() = RequestInfo(
|
||||
requestType = requestType ?: error("Request type must be present"),
|
||||
description = description ?: error("Description must be present"),
|
||||
typeEnrichment = typeEnrichment,
|
||||
enrichment = typeEnrichment,
|
||||
examples = examples,
|
||||
mediaTypes = mediaTypes ?: setOf("application/json"),
|
||||
required = required ?: true
|
||||
|
@ -10,7 +10,7 @@ import kotlin.reflect.typeOf
|
||||
class ResponseInfo private constructor(
|
||||
val responseCode: HttpStatusCode,
|
||||
val responseType: KType,
|
||||
val typeEnrichment: TypeEnrichment<*>?,
|
||||
val enrichment: TypeEnrichment<*>?,
|
||||
val description: String,
|
||||
val examples: Map<String, MediaType.Example>?,
|
||||
val mediaTypes: Set<String>,
|
||||
@ -57,8 +57,8 @@ class ResponseInfo private constructor(
|
||||
|
||||
fun description(s: String) = apply { this.description = s }
|
||||
|
||||
fun examples(vararg e: Pair<String, Any>) = apply {
|
||||
this.examples = e.toMap().mapValues { (_, v) -> MediaType.Example(v) }
|
||||
fun examples(vararg e: Pair<String, MediaType.Example>) = apply {
|
||||
this.examples = e.toMap()
|
||||
}
|
||||
|
||||
fun mediaTypes(vararg m: String) = apply {
|
||||
@ -69,7 +69,7 @@ class ResponseInfo private constructor(
|
||||
responseCode = responseCode ?: error("You must provide a response code in order to build a Response!"),
|
||||
responseType = responseType ?: error("You must provide a response type in order to build a Response!"),
|
||||
description = description ?: error("You must provide a description in order to build a Response!"),
|
||||
typeEnrichment = typeEnrichment,
|
||||
enrichment = typeEnrichment,
|
||||
examples = examples,
|
||||
mediaTypes = mediaTypes ?: setOf("application/json"),
|
||||
responseHeaders = responseHeaders
|
||||
|
@ -1,16 +1,14 @@
|
||||
package io.bkbn.kompendium.core.plugin
|
||||
|
||||
import io.bkbn.kompendium.core.attribute.KompendiumAttributes
|
||||
import io.bkbn.kompendium.json.schema.KotlinXSchemaConfigurator
|
||||
import io.bkbn.kompendium.json.schema.SchemaConfigurator
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.util.Helpers.getSimpleSlug
|
||||
import io.bkbn.kompendium.oas.OpenApiSpec
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.application.createApplicationPlugin
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.application
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.route
|
||||
import io.ktor.server.routing.routing
|
||||
@ -19,25 +17,26 @@ import kotlin.reflect.KType
|
||||
object NotarizedApplication {
|
||||
|
||||
class Config {
|
||||
lateinit var spec: OpenApiSpec
|
||||
var openApiJson: Routing.() -> Unit = {
|
||||
route("/openapi.json") {
|
||||
lateinit var spec: () -> OpenApiSpec
|
||||
var specRoute: (OpenApiSpec, Routing) -> Unit = { spec, routing ->
|
||||
routing.route("/openapi.json") {
|
||||
get {
|
||||
call.respond(HttpStatusCode.OK, this@route.application.attributes[KompendiumAttributes.openApiSpec])
|
||||
call.respond(spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
var customTypes: Map<KType, JsonSchema> = emptyMap()
|
||||
var schemaConfigurator: SchemaConfigurator = SchemaConfigurator.Default()
|
||||
var schemaConfigurator: SchemaConfigurator = KotlinXSchemaConfigurator()
|
||||
}
|
||||
|
||||
operator fun invoke() = createApplicationPlugin(
|
||||
name = "NotarizedApplication",
|
||||
createConfiguration = ::Config
|
||||
) {
|
||||
val spec = pluginConfig.spec
|
||||
val spec = pluginConfig.spec()
|
||||
val routing = application.routing {}
|
||||
pluginConfig.openApiJson(routing)
|
||||
this@createApplicationPlugin.pluginConfig.specRoute(spec, routing)
|
||||
// pluginConfig.openApiJson(routing)
|
||||
pluginConfig.customTypes.forEach { (type, schema) ->
|
||||
spec.components.schemas[type.getSimpleSlug()] = schema
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ object NotarizedRoute {
|
||||
|
||||
fun Route.calculateRoutePath() = toString()
|
||||
.let {
|
||||
application.environment.rootPath.takeIf { root -> root.isNotEmpty() }
|
||||
application.rootPath.takeIf { root -> root.isNotEmpty() }
|
||||
?.let { root ->
|
||||
val sanitizedRoute = if (root.startsWith("/")) root else "/$root"
|
||||
it.replace(sanitizedRoute, "")
|
||||
|
@ -42,6 +42,7 @@ object Helpers {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
fun MethodInfo.addToSpec(
|
||||
path: Path,
|
||||
spec: OpenApiSpec,
|
||||
@ -50,36 +51,45 @@ object Helpers {
|
||||
routePath: String,
|
||||
authMethods: List<String> = emptyList()
|
||||
) {
|
||||
val type = this.response.responseType
|
||||
val enrichment = this.response.enrichment
|
||||
SchemaGenerator.fromTypeOrUnit(
|
||||
type = this.response.responseType,
|
||||
type = type,
|
||||
cache = spec.components.schemas,
|
||||
schemaConfigurator = schemaConfigurator,
|
||||
enrichment = this.response.typeEnrichment,
|
||||
enrichment = enrichment,
|
||||
)?.let { schema ->
|
||||
spec.components.schemas[this.response.responseType.getSlug(this.response.typeEnrichment)] = schema
|
||||
val slug = type.getSlug(enrichment)
|
||||
spec.components.schemas[slug] = schema
|
||||
}
|
||||
|
||||
errors.forEach { error ->
|
||||
val errorEnrichment = error.enrichment
|
||||
val errorType = error.responseType
|
||||
SchemaGenerator.fromTypeOrUnit(
|
||||
type = error.responseType,
|
||||
type = errorType,
|
||||
cache = spec.components.schemas,
|
||||
schemaConfigurator = schemaConfigurator,
|
||||
enrichment = error.typeEnrichment,
|
||||
enrichment = errorEnrichment,
|
||||
)?.let { schema ->
|
||||
spec.components.schemas[error.responseType.getSlug(error.typeEnrichment)] = schema
|
||||
val slug = errorType.getSlug(errorEnrichment)
|
||||
spec.components.schemas[slug] = schema
|
||||
}
|
||||
}
|
||||
|
||||
when (this) {
|
||||
is MethodInfoWithRequest -> {
|
||||
this.request?.let { reqInfo ->
|
||||
val reqEnrichment = reqInfo.enrichment
|
||||
val reqType = reqInfo.requestType
|
||||
SchemaGenerator.fromTypeOrUnit(
|
||||
type = reqInfo.requestType,
|
||||
type = reqType,
|
||||
cache = spec.components.schemas,
|
||||
schemaConfigurator = schemaConfigurator,
|
||||
enrichment = reqInfo.typeEnrichment,
|
||||
enrichment = reqEnrichment,
|
||||
)?.let { schema ->
|
||||
spec.components.schemas[reqInfo.requestType.getSlug(reqInfo.typeEnrichment)] = schema
|
||||
val slug = reqType.getSlug(reqEnrichment)
|
||||
spec.components.schemas[slug] = schema
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,10 +128,7 @@ object Helpers {
|
||||
operationId = this.operationId,
|
||||
deprecated = this.deprecated,
|
||||
parameters = this.parameters,
|
||||
security = config.security
|
||||
?.map { (k, v) -> k to v }
|
||||
?.map { listOf(it).toMap() }
|
||||
?.toMutableList(),
|
||||
security = this.createCombinedSecurityContext(config),
|
||||
requestBody = when (this) {
|
||||
is MethodInfoWithRequest -> this.request?.let { reqInfo ->
|
||||
Request(
|
||||
@ -129,7 +136,7 @@ object Helpers {
|
||||
content = reqInfo.requestType.toReferenceContent(
|
||||
examples = reqInfo.examples,
|
||||
mediaTypes = reqInfo.mediaTypes,
|
||||
enrichment = reqInfo.typeEnrichment
|
||||
enrichment = reqInfo.enrichment
|
||||
),
|
||||
required = reqInfo.required
|
||||
)
|
||||
@ -144,12 +151,31 @@ object Helpers {
|
||||
content = this.response.responseType.toReferenceContent(
|
||||
examples = this.response.examples,
|
||||
mediaTypes = this.response.mediaTypes,
|
||||
enrichment = this.response.typeEnrichment
|
||||
enrichment = this.response.enrichment
|
||||
)
|
||||
)
|
||||
).plus(this.errors.toResponseMap())
|
||||
)
|
||||
|
||||
private fun MethodInfo.createCombinedSecurityContext(config: SpecConfig): MutableList<Map<String, List<String>>>? {
|
||||
val configSecurity = config.security
|
||||
?.map { (k, v) -> k to v }
|
||||
?.map { listOf(it).toMap() }
|
||||
?.toMutableList()
|
||||
|
||||
val methodSecurity = this.security
|
||||
?.map { (k, v) -> k to v }
|
||||
?.map { listOf(it).toMap() }
|
||||
?.toMutableList()
|
||||
|
||||
return when {
|
||||
configSecurity == null && methodSecurity == null -> null
|
||||
configSecurity == null -> methodSecurity
|
||||
methodSecurity == null -> configSecurity
|
||||
else -> configSecurity.plus(methodSecurity).toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<ResponseInfo>.toResponseMap(): Map<Int, Response> = associate { error ->
|
||||
error.responseCode.value to Response(
|
||||
description = error.description,
|
||||
@ -157,7 +183,7 @@ object Helpers {
|
||||
content = error.responseType.toReferenceContent(
|
||||
examples = error.examples,
|
||||
mediaTypes = error.mediaTypes,
|
||||
enrichment = error.typeEnrichment
|
||||
enrichment = error.enrichment
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,172 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import dev.forst.ktor.apikey.apiKey
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers
|
||||
import io.bkbn.kompendium.core.util.customAuthConfig
|
||||
import io.bkbn.kompendium.core.util.customScopesOnSiblingPathOperations
|
||||
import io.bkbn.kompendium.core.util.defaultAuthConfig
|
||||
import io.bkbn.kompendium.core.util.multipleAuthStrategies
|
||||
import io.bkbn.kompendium.oas.component.Components
|
||||
import io.bkbn.kompendium.oas.security.ApiKeyAuth
|
||||
import io.bkbn.kompendium.oas.security.BasicAuth
|
||||
import io.bkbn.kompendium.oas.security.BearerAuth
|
||||
import io.bkbn.kompendium.oas.security.OAuth
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.auth.Authentication
|
||||
import io.ktor.server.auth.OAuthServerSettings
|
||||
import io.ktor.server.auth.Principal
|
||||
import io.ktor.server.auth.UserIdPrincipal
|
||||
import io.ktor.server.auth.basic
|
||||
import io.ktor.server.auth.jwt.jwt
|
||||
import io.ktor.server.auth.oauth
|
||||
|
||||
class KompendiumAuthenticationTest : DescribeSpec({
|
||||
describe("Authentication") {
|
||||
it("Can add a default auth config by default") {
|
||||
TestHelpers.openApiTestAllSerializers(
|
||||
snapshotName = "T0045__default_auth_config.json",
|
||||
applicationSetup = {
|
||||
install(Authentication) {
|
||||
basic("basic") {
|
||||
realm = "Ktor Server"
|
||||
validate { UserIdPrincipal("Placeholder") }
|
||||
}
|
||||
}
|
||||
},
|
||||
specOverrides = {
|
||||
this.copy(
|
||||
components = Components(
|
||||
securitySchemes = mutableMapOf(
|
||||
"basic" to BasicAuth()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
) { defaultAuthConfig() }
|
||||
}
|
||||
it("Can provide custom auth config with proper scopes") {
|
||||
TestHelpers.openApiTestAllSerializers(
|
||||
snapshotName = "T0046__custom_auth_config.json",
|
||||
applicationSetup = {
|
||||
install(Authentication) {
|
||||
oauth("auth-oauth-google") {
|
||||
urlProvider = { "http://localhost:8080/callback" }
|
||||
providerLookup = {
|
||||
OAuthServerSettings.OAuth2ServerSettings(
|
||||
name = "google",
|
||||
authorizeUrl = "https://accounts.google.com/o/oauth2/auth",
|
||||
accessTokenUrl = "https://accounts.google.com/o/oauth2/token",
|
||||
requestMethod = HttpMethod.Post,
|
||||
clientId = "DUMMY_VAL",
|
||||
clientSecret = "DUMMY_VAL",
|
||||
defaultScopes = listOf("https://www.googleapis.com/auth/userinfo.profile"),
|
||||
extraTokenParameters = listOf("access_type" to "offline")
|
||||
)
|
||||
}
|
||||
client = HttpClient(CIO)
|
||||
}
|
||||
}
|
||||
},
|
||||
specOverrides = {
|
||||
this.copy(
|
||||
components = Components(
|
||||
securitySchemes = mutableMapOf(
|
||||
"auth-oauth-google" to OAuth(
|
||||
flows = OAuth.Flows(
|
||||
implicit = OAuth.Flows.Implicit(
|
||||
authorizationUrl = "https://accounts.google.com/o/oauth2/auth",
|
||||
scopes = mapOf(
|
||||
"write:pets" to "modify pets in your account",
|
||||
"read:pets" to "read your pets"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
) { customAuthConfig() }
|
||||
}
|
||||
it("Can provide multiple authentication strategies") {
|
||||
data class TestAppPrincipal(val key: String) : Principal
|
||||
TestHelpers.openApiTestAllSerializers(
|
||||
snapshotName = "T0047__multiple_auth_strategies.json",
|
||||
applicationSetup = {
|
||||
install(Authentication) {
|
||||
apiKey("api-key") {
|
||||
headerName = "X-API-KEY"
|
||||
validate { key ->
|
||||
// api key library (dev.forst.ktor.apikey) is using the deprecated `Principal` class
|
||||
key
|
||||
.takeIf { it == "api-key" }
|
||||
?.let { TestAppPrincipal(it) }
|
||||
}
|
||||
}
|
||||
jwt("jwt") {
|
||||
realm = "Server"
|
||||
}
|
||||
}
|
||||
},
|
||||
specOverrides = {
|
||||
this.copy(
|
||||
components = Components(
|
||||
securitySchemes = mutableMapOf(
|
||||
"jwt" to BearerAuth("JWT"),
|
||||
"api-key" to ApiKeyAuth(ApiKeyAuth.ApiKeyLocation.HEADER, "X-API-KEY")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
) { multipleAuthStrategies() }
|
||||
}
|
||||
it("Can provide different scopes on path operations in the same route") {
|
||||
TestHelpers.openApiTestAllSerializers(
|
||||
snapshotName = "T0074__auth_on_specific_path_operation.json",
|
||||
applicationSetup = {
|
||||
install(Authentication) {
|
||||
oauth("auth-oauth-google") {
|
||||
urlProvider = { "http://localhost:8080/callback" }
|
||||
providerLookup = {
|
||||
OAuthServerSettings.OAuth2ServerSettings(
|
||||
name = "google",
|
||||
authorizeUrl = "https://accounts.google.com/o/oauth2/auth",
|
||||
accessTokenUrl = "https://accounts.google.com/o/oauth2/token",
|
||||
requestMethod = HttpMethod.Post,
|
||||
clientId = "DUMMY_VAL",
|
||||
clientSecret = "DUMMY_VAL",
|
||||
defaultScopes = listOf("https://www.googleapis.com/auth/userinfo.profile"),
|
||||
extraTokenParameters = listOf("access_type" to "offline")
|
||||
)
|
||||
}
|
||||
client = HttpClient(CIO)
|
||||
}
|
||||
}
|
||||
},
|
||||
specOverrides = {
|
||||
this.copy(
|
||||
components = Components(
|
||||
securitySchemes = mutableMapOf(
|
||||
"auth-oauth-google" to OAuth(
|
||||
flows = OAuth.Flows(
|
||||
implicit = OAuth.Flows.Implicit(
|
||||
authorizationUrl = "https://accounts.google.com/o/oauth2/auth",
|
||||
scopes = mapOf(
|
||||
"write:pets" to "modify pets in your account",
|
||||
"read:pets" to "read your pets"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
) { customScopesOnSiblingPathOperations() }
|
||||
}
|
||||
}
|
||||
})
|
@ -0,0 +1,35 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers
|
||||
import io.bkbn.kompendium.core.util.arrayConstraints
|
||||
import io.bkbn.kompendium.core.util.doubleConstraints
|
||||
import io.bkbn.kompendium.core.util.intConstraints
|
||||
import io.bkbn.kompendium.core.util.stringConstraints
|
||||
import io.bkbn.kompendium.core.util.stringContentEncodingConstraints
|
||||
import io.bkbn.kompendium.core.util.stringPatternConstraints
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
class KompendiumConstraintsTest : DescribeSpec({
|
||||
describe("Constraints") {
|
||||
it("Can apply constraints to an int field") {
|
||||
TestHelpers.openApiTestAllSerializers("T0059__int_constraints.json") { intConstraints() }
|
||||
}
|
||||
it("Can apply constraints to a double field") {
|
||||
TestHelpers.openApiTestAllSerializers("T0060__double_constraints.json") { doubleConstraints() }
|
||||
}
|
||||
it("Can apply a min and max length to a string field") {
|
||||
TestHelpers.openApiTestAllSerializers("T0061__string_min_max_constraints.json") { stringConstraints() }
|
||||
}
|
||||
it("Can apply a pattern to a string field") {
|
||||
TestHelpers.openApiTestAllSerializers("T0062__string_pattern_constraints.json") { stringPatternConstraints() }
|
||||
}
|
||||
it("Can apply a content encoding and media type to a string field") {
|
||||
TestHelpers.openApiTestAllSerializers("T0063__string_content_encoding_constraints.json") {
|
||||
stringContentEncodingConstraints()
|
||||
}
|
||||
}
|
||||
it("Can apply constraints to an array field") {
|
||||
TestHelpers.openApiTestAllSerializers("T0064__array_constraints.json") { arrayConstraints() }
|
||||
}
|
||||
}
|
||||
})
|
@ -0,0 +1,13 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers
|
||||
import io.bkbn.kompendium.core.util.defaultParameter
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
class KompendiumDefaultsTest : DescribeSpec({
|
||||
describe("Defaults") {
|
||||
it("Can generate a default parameter value") {
|
||||
TestHelpers.openApiTestAllSerializers("T0022__query_with_default_parameter.json") { defaultParameter() }
|
||||
}
|
||||
}
|
||||
})
|
@ -0,0 +1,39 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
||||
import io.bkbn.kompendium.core.util.enrichedComplexGenericType
|
||||
import io.bkbn.kompendium.core.util.enrichedGenericResponse
|
||||
import io.bkbn.kompendium.core.util.enrichedMap
|
||||
import io.bkbn.kompendium.core.util.enrichedNestedCollection
|
||||
import io.bkbn.kompendium.core.util.enrichedSimpleRequest
|
||||
import io.bkbn.kompendium.core.util.enrichedSimpleResponse
|
||||
import io.bkbn.kompendium.core.util.enrichedTopLevelCollection
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
class KompendiumEnrichmentTest : DescribeSpec({
|
||||
describe("Enrichment") {
|
||||
it("Can enrich a simple request") {
|
||||
openApiTestAllSerializers("T0055__enriched_simple_request.json") { enrichedSimpleRequest() }
|
||||
}
|
||||
it("Can enrich a simple response") {
|
||||
openApiTestAllSerializers("T0058__enriched_simple_response.json") { enrichedSimpleResponse() }
|
||||
}
|
||||
it("Can enrich a nested collection") {
|
||||
openApiTestAllSerializers("T0056__enriched_nested_collection.json") { enrichedNestedCollection() }
|
||||
}
|
||||
it("Can enrich a complex generic type") {
|
||||
openApiTestAllSerializers(
|
||||
"T0057__enriched_complex_generic_type.json"
|
||||
) { enrichedComplexGenericType() }
|
||||
}
|
||||
it("Can enrich a generic object") {
|
||||
openApiTestAllSerializers("T0067__enriched_generic_object.json") { enrichedGenericResponse() }
|
||||
}
|
||||
it("Can enrich a top level list type") {
|
||||
openApiTestAllSerializers("T0077__enriched_top_level_list.json") { enrichedTopLevelCollection() }
|
||||
}
|
||||
it("can enrich a map type") {
|
||||
openApiTestAllSerializers("T0078__enriched_top_level_map.json") { enrichedMap() }
|
||||
}
|
||||
}
|
||||
})
|
@ -0,0 +1,45 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers
|
||||
import io.bkbn.kompendium.core.util.dateTimeString
|
||||
import io.bkbn.kompendium.core.util.samePathSameMethod
|
||||
import io.bkbn.kompendium.json.schema.exception.UnknownSchemaException
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
import io.kotest.matchers.should
|
||||
import io.kotest.matchers.string.startWith
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.auth.Authentication
|
||||
import io.ktor.server.auth.UserIdPrincipal
|
||||
import io.ktor.server.auth.basic
|
||||
|
||||
class KompendiumErrorHandlingTest : DescribeSpec({
|
||||
describe("Error Handling") {
|
||||
it("Throws a clear exception when an unidentified type is encountered") {
|
||||
val exception = shouldThrow<UnknownSchemaException> {
|
||||
TestHelpers.openApiTestAllSerializers(
|
||||
""
|
||||
) { dateTimeString() }
|
||||
}
|
||||
exception.message should startWith("An unknown type was encountered: class java.time.Instant")
|
||||
}
|
||||
it("Throws an exception when same method for same path has been previously registered") {
|
||||
val exception = shouldThrow<IllegalArgumentException> {
|
||||
TestHelpers.openApiTestAllSerializers(
|
||||
snapshotName = "",
|
||||
applicationSetup = {
|
||||
install(Authentication) {
|
||||
basic("basic") {
|
||||
realm = "Ktor Server"
|
||||
validate { UserIdPrincipal("Placeholder") }
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
samePathSameMethod()
|
||||
}
|
||||
}
|
||||
exception.message should startWith("A route has already been registered for path: /test/{a} and method: GET")
|
||||
}
|
||||
}
|
||||
})
|
@ -0,0 +1,27 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers
|
||||
import io.bkbn.kompendium.core.util.exampleParams
|
||||
import io.bkbn.kompendium.core.util.exampleSummaryAndDescription
|
||||
import io.bkbn.kompendium.core.util.optionalReqExample
|
||||
import io.bkbn.kompendium.core.util.reqRespExamples
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
class KompendiumExamplesTest : DescribeSpec({
|
||||
describe("Examples") {
|
||||
it("Can generate example response and request bodies") {
|
||||
TestHelpers.openApiTestAllSerializers("T0020__example_req_and_resp.json") { reqRespExamples() }
|
||||
}
|
||||
it("Can describe example parameters") {
|
||||
TestHelpers.openApiTestAllSerializers("T0021__example_parameters.json") { exampleParams() }
|
||||
}
|
||||
it("Can generate example optional request body") {
|
||||
TestHelpers.openApiTestAllSerializers("T0069__example_optional_req.json") { optionalReqExample() }
|
||||
}
|
||||
it("Can generate example summary and description") {
|
||||
TestHelpers.openApiTestAllSerializers(
|
||||
"T0075__example_summary_and_description.json"
|
||||
) { exampleSummaryAndDescription() }
|
||||
}
|
||||
}
|
||||
})
|
@ -0,0 +1,27 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers
|
||||
import io.bkbn.kompendium.core.util.genericException
|
||||
import io.bkbn.kompendium.core.util.multipleExceptions
|
||||
import io.bkbn.kompendium.core.util.polymorphicException
|
||||
import io.bkbn.kompendium.core.util.singleException
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
class KompendiumExceptionsTest : DescribeSpec({
|
||||
describe("Exceptions") {
|
||||
it("Can add an exception status code to a response") {
|
||||
TestHelpers.openApiTestAllSerializers("T0016__notarized_get_with_exception_response.json") { singleException() }
|
||||
}
|
||||
it("Can support multiple response codes") {
|
||||
TestHelpers.openApiTestAllSerializers("T0017__notarized_get_with_multiple_exception_responses.json") {
|
||||
multipleExceptions()
|
||||
}
|
||||
}
|
||||
it("Can add a polymorphic exception response") {
|
||||
TestHelpers.openApiTestAllSerializers("T0018__polymorphic_error_status_codes.json") { polymorphicException() }
|
||||
}
|
||||
it("Can add a generic exception response") {
|
||||
TestHelpers.openApiTestAllSerializers("T0019__generic_exception.json") { genericException() }
|
||||
}
|
||||
}
|
||||
})
|
@ -0,0 +1,7 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
class KompendiumFreeFormTest : DescribeSpec({
|
||||
// todo Assess strategies here
|
||||
})
|
@ -0,0 +1,67 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers
|
||||
import io.bkbn.kompendium.core.util.genericPolymorphicResponse
|
||||
import io.bkbn.kompendium.core.util.genericPolymorphicResponseMultipleImpls
|
||||
import io.bkbn.kompendium.core.util.gnarlyGenericResponse
|
||||
import io.bkbn.kompendium.core.util.nestedGenericCollection
|
||||
import io.bkbn.kompendium.core.util.nestedGenericMultipleParamsCollection
|
||||
import io.bkbn.kompendium.core.util.nestedGenericResponse
|
||||
import io.bkbn.kompendium.core.util.overrideSealedTypeIdentifier
|
||||
import io.bkbn.kompendium.core.util.polymorphicCollectionResponse
|
||||
import io.bkbn.kompendium.core.util.polymorphicMapResponse
|
||||
import io.bkbn.kompendium.core.util.polymorphicResponse
|
||||
import io.bkbn.kompendium.core.util.simpleGenericResponse
|
||||
import io.bkbn.kompendium.core.util.subtypeNotCompleteSetOfParentProperties
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
class KompendiumPolymorphismAndGenericsTest : DescribeSpec({
|
||||
describe("Polymorphism and Generics") {
|
||||
it("can generate a polymorphic response type") {
|
||||
TestHelpers.openApiTestAllSerializers("T0027__polymorphic_response.json") { polymorphicResponse() }
|
||||
}
|
||||
it("Can generate a collection with polymorphic response type") {
|
||||
TestHelpers.openApiTestAllSerializers("T0028__polymorphic_list_response.json") { polymorphicCollectionResponse() }
|
||||
}
|
||||
it("Can generate a map with a polymorphic response type") {
|
||||
TestHelpers.openApiTestAllSerializers("T0029__polymorphic_map_response.json") { polymorphicMapResponse() }
|
||||
}
|
||||
it("Can generate a response type with a generic type") {
|
||||
TestHelpers.openApiTestAllSerializers("T0030__simple_generic_response.json") { simpleGenericResponse() }
|
||||
}
|
||||
it("Can generate a response type with a nested generic type") {
|
||||
TestHelpers.openApiTestAllSerializers("T0031__nested_generic_response.json") { nestedGenericResponse() }
|
||||
}
|
||||
it("Can generate a polymorphic response type with generics") {
|
||||
TestHelpers.openApiTestAllSerializers(
|
||||
"T0032__polymorphic_response_with_generics.json"
|
||||
) { genericPolymorphicResponse() }
|
||||
}
|
||||
it("Can handle an absolutely psycho inheritance test") {
|
||||
TestHelpers.openApiTestAllSerializers("T0033__crazy_polymorphic_example.json") {
|
||||
genericPolymorphicResponseMultipleImpls()
|
||||
}
|
||||
}
|
||||
it("Can support nested generic collections") {
|
||||
TestHelpers.openApiTestAllSerializers("T0039__nested_generic_collection.json") { nestedGenericCollection() }
|
||||
}
|
||||
it("Can support nested generics with multiple type parameters") {
|
||||
TestHelpers.openApiTestAllSerializers("T0040__nested_generic_multiple_type_params.json") {
|
||||
nestedGenericMultipleParamsCollection()
|
||||
}
|
||||
}
|
||||
it("Can handle a really gnarly generic example") {
|
||||
TestHelpers.openApiTestAllSerializers("T0043__gnarly_generic_example.json") { gnarlyGenericResponse() }
|
||||
}
|
||||
it("Can override the type name for a sealed interface implementation") {
|
||||
TestHelpers.openApiTestAllSerializers("T0070__sealed_interface_type_name_override.json") {
|
||||
overrideSealedTypeIdentifier()
|
||||
}
|
||||
}
|
||||
it("Can serialize an object where the subtype is not a complete set of parent properties") {
|
||||
TestHelpers.openApiTestAllSerializers("T0071__subtype_not_complete_set_of_parent_properties.json") {
|
||||
subtypeNotCompleteSetOfParentProperties()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
@ -0,0 +1,25 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers
|
||||
import io.bkbn.kompendium.core.util.defaultField
|
||||
import io.bkbn.kompendium.core.util.nonRequiredParam
|
||||
import io.bkbn.kompendium.core.util.nullableField
|
||||
import io.bkbn.kompendium.core.util.requiredParams
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
class KompendiumRequiredFieldsTest : DescribeSpec({
|
||||
describe("Required Fields") {
|
||||
it("Marks a parameter as required if there is no default and it is not marked nullable") {
|
||||
TestHelpers.openApiTestAllSerializers("T0023__required_param.json") { requiredParams() }
|
||||
}
|
||||
it("Can mark a parameter as not required") {
|
||||
TestHelpers.openApiTestAllSerializers("T0024__non_required_param.json") { nonRequiredParam() }
|
||||
}
|
||||
it("Does not mark a field as required if a default value is provided") {
|
||||
TestHelpers.openApiTestAllSerializers("T0025__default_field.json") { defaultField() }
|
||||
}
|
||||
it("Does not mark a nullable field as required") {
|
||||
TestHelpers.openApiTestAllSerializers("T0026__nullable_field.json") { nullableField() }
|
||||
}
|
||||
}
|
||||
})
|
@ -0,0 +1,29 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers
|
||||
import io.bkbn.kompendium.core.util.nestedUnderRoot
|
||||
import io.bkbn.kompendium.core.util.paramWrapper
|
||||
import io.bkbn.kompendium.core.util.rootRoute
|
||||
import io.bkbn.kompendium.core.util.simplePathParsing
|
||||
import io.bkbn.kompendium.core.util.trailingSlash
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
class KompendiumRouteParsingTest : DescribeSpec({
|
||||
describe("Route Parsing") {
|
||||
it("Can parse a simple path and store it under the expected route") {
|
||||
TestHelpers.openApiTestAllSerializers("T0012__path_parser.json") { simplePathParsing() }
|
||||
}
|
||||
it("Can notarize the root route") {
|
||||
TestHelpers.openApiTestAllSerializers("T0013__root_route.json") { rootRoute() }
|
||||
}
|
||||
it("Can notarize a route under the root module without appending trailing slash") {
|
||||
TestHelpers.openApiTestAllSerializers("T0014__nested_under_root.json") { nestedUnderRoot() }
|
||||
}
|
||||
it("Can notarize a route with a trailing slash") {
|
||||
TestHelpers.openApiTestAllSerializers("T0015__trailing_slash.json") { trailingSlash() }
|
||||
}
|
||||
it("Can notarize a route with a parameter") {
|
||||
TestHelpers.openApiTestAllSerializers("T0068__param_wrapper.json") { paramWrapper() }
|
||||
}
|
||||
}
|
||||
})
|
@ -1,36 +1,13 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import dev.forst.ktor.apikey.apiKey
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
||||
import io.bkbn.kompendium.core.util.arrayConstraints
|
||||
import io.bkbn.kompendium.core.util.complexRequest
|
||||
import io.bkbn.kompendium.core.util.customAuthConfig
|
||||
import io.bkbn.kompendium.core.util.customFieldNameResponse
|
||||
import io.bkbn.kompendium.core.util.dateTimeString
|
||||
import io.bkbn.kompendium.core.util.defaultAuthConfig
|
||||
import io.bkbn.kompendium.core.util.defaultField
|
||||
import io.bkbn.kompendium.core.util.defaultParameter
|
||||
import io.bkbn.kompendium.core.util.doubleConstraints
|
||||
import io.bkbn.kompendium.core.util.enrichedGenericResponse
|
||||
import io.bkbn.kompendium.core.util.enrichedComplexGenericType
|
||||
import io.bkbn.kompendium.core.util.enrichedNestedCollection
|
||||
import io.bkbn.kompendium.core.util.enrichedSimpleRequest
|
||||
import io.bkbn.kompendium.core.util.enrichedSimpleResponse
|
||||
import io.bkbn.kompendium.core.util.exampleParams
|
||||
import io.bkbn.kompendium.core.util.genericException
|
||||
import io.bkbn.kompendium.core.util.genericPolymorphicResponse
|
||||
import io.bkbn.kompendium.core.util.genericPolymorphicResponseMultipleImpls
|
||||
import io.bkbn.kompendium.core.util.gnarlyGenericResponse
|
||||
import io.bkbn.kompendium.core.util.fieldOutsideConstructor
|
||||
import io.bkbn.kompendium.core.util.headerParameter
|
||||
import io.bkbn.kompendium.core.util.ignoredFieldsResponse
|
||||
import io.bkbn.kompendium.core.util.intConstraints
|
||||
import io.bkbn.kompendium.core.util.multipleAuthStrategies
|
||||
import io.bkbn.kompendium.core.util.multipleExceptions
|
||||
import io.bkbn.kompendium.core.util.nestedGenericCollection
|
||||
import io.bkbn.kompendium.core.util.nestedGenericMultipleParamsCollection
|
||||
import io.bkbn.kompendium.core.util.nestedGenericResponse
|
||||
import io.bkbn.kompendium.core.util.nestedTypeName
|
||||
import io.bkbn.kompendium.core.util.nestedUnderRoot
|
||||
import io.bkbn.kompendium.core.util.nonRequiredParam
|
||||
import io.bkbn.kompendium.core.util.nonRequiredParams
|
||||
import io.bkbn.kompendium.core.util.notarizedDelete
|
||||
@ -45,56 +22,32 @@ import io.bkbn.kompendium.core.util.nullableField
|
||||
import io.bkbn.kompendium.core.util.nullableNestedObject
|
||||
import io.bkbn.kompendium.core.util.nullableReference
|
||||
import io.bkbn.kompendium.core.util.overrideMediaTypes
|
||||
import io.bkbn.kompendium.core.util.polymorphicCollectionResponse
|
||||
import io.bkbn.kompendium.core.util.polymorphicException
|
||||
import io.bkbn.kompendium.core.util.polymorphicMapResponse
|
||||
import io.bkbn.kompendium.core.util.polymorphicResponse
|
||||
import io.bkbn.kompendium.core.util.postNoReqBody
|
||||
import io.bkbn.kompendium.core.util.primitives
|
||||
import io.bkbn.kompendium.core.util.reqRespExamples
|
||||
import io.bkbn.kompendium.core.util.optionalReqExample
|
||||
import io.bkbn.kompendium.core.util.requiredParams
|
||||
import io.bkbn.kompendium.core.util.responseHeaders
|
||||
import io.bkbn.kompendium.core.util.returnsEnumList
|
||||
import io.bkbn.kompendium.core.util.returnsList
|
||||
import io.bkbn.kompendium.core.util.rootRoute
|
||||
import io.bkbn.kompendium.core.util.samePathDifferentMethodsAndAuth
|
||||
import io.bkbn.kompendium.core.util.samePathSameMethod
|
||||
import io.bkbn.kompendium.core.util.simpleGenericResponse
|
||||
import io.bkbn.kompendium.core.util.simplePathParsing
|
||||
import io.bkbn.kompendium.core.util.simpleRecursive
|
||||
import io.bkbn.kompendium.core.util.singleException
|
||||
import io.bkbn.kompendium.core.util.stringConstraints
|
||||
import io.bkbn.kompendium.core.util.stringContentEncodingConstraints
|
||||
import io.bkbn.kompendium.core.util.stringPatternConstraints
|
||||
import io.bkbn.kompendium.core.util.topLevelNullable
|
||||
import io.bkbn.kompendium.core.util.trailingSlash
|
||||
import io.bkbn.kompendium.core.util.paramWrapper
|
||||
import io.bkbn.kompendium.core.util.unbackedFieldsResponse
|
||||
import io.bkbn.kompendium.core.util.withOperationId
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.json.schema.exception.UnknownSchemaException
|
||||
import io.bkbn.kompendium.oas.component.Components
|
||||
import io.bkbn.kompendium.oas.security.ApiKeyAuth
|
||||
import io.bkbn.kompendium.oas.security.BasicAuth
|
||||
import io.bkbn.kompendium.oas.security.BearerAuth
|
||||
import io.bkbn.kompendium.oas.security.OAuth
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
import io.kotest.matchers.should
|
||||
import io.kotest.matchers.string.startWith
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.auth.Authentication
|
||||
import io.ktor.server.auth.OAuthServerSettings
|
||||
import io.ktor.server.auth.UserIdPrincipal
|
||||
import io.ktor.server.auth.basic
|
||||
import io.ktor.server.auth.jwt.jwt
|
||||
import io.ktor.server.auth.oauth
|
||||
import io.ktor.server.response.respondText
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.route
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.net.URI
|
||||
import java.time.Instant
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
class KompendiumTest : DescribeSpec({
|
||||
describe("Notarized Open API Metadata Tests") {
|
||||
@ -141,53 +94,6 @@ class KompendiumTest : DescribeSpec({
|
||||
openApiTestAllSerializers("T0066__notarized_get_with_response_headers.json") { responseHeaders() }
|
||||
}
|
||||
}
|
||||
describe("Route Parsing") {
|
||||
it("Can parse a simple path and store it under the expected route") {
|
||||
openApiTestAllSerializers("T0012__path_parser.json") { simplePathParsing() }
|
||||
}
|
||||
it("Can notarize the root route") {
|
||||
openApiTestAllSerializers("T0013__root_route.json") { rootRoute() }
|
||||
}
|
||||
it("Can notarize a route under the root module without appending trailing slash") {
|
||||
openApiTestAllSerializers("T0014__nested_under_root.json") { nestedUnderRoot() }
|
||||
}
|
||||
it("Can notarize a route with a trailing slash") {
|
||||
openApiTestAllSerializers("T0015__trailing_slash.json") { trailingSlash() }
|
||||
}
|
||||
it("Can notarize a route with a parameter") {
|
||||
openApiTestAllSerializers("T0068__param_wrapper.json") { paramWrapper() }
|
||||
}
|
||||
}
|
||||
describe("Exceptions") {
|
||||
it("Can add an exception status code to a response") {
|
||||
openApiTestAllSerializers("T0016__notarized_get_with_exception_response.json") { singleException() }
|
||||
}
|
||||
it("Can support multiple response codes") {
|
||||
openApiTestAllSerializers("T0017__notarized_get_with_multiple_exception_responses.json") { multipleExceptions() }
|
||||
}
|
||||
it("Can add a polymorphic exception response") {
|
||||
openApiTestAllSerializers("T0018__polymorphic_error_status_codes.json") { polymorphicException() }
|
||||
}
|
||||
it("Can add a generic exception response") {
|
||||
openApiTestAllSerializers("T0019__generic_exception.json") { genericException() }
|
||||
}
|
||||
}
|
||||
describe("Examples") {
|
||||
it("Can generate example response and request bodies") {
|
||||
openApiTestAllSerializers("T0020__example_req_and_resp.json") { reqRespExamples() }
|
||||
}
|
||||
it("Can describe example parameters") {
|
||||
openApiTestAllSerializers("T0021__example_parameters.json") { exampleParams() }
|
||||
}
|
||||
it("Can generate example optional request body") {
|
||||
openApiTestAllSerializers("T0069__example_optional_req.json") { optionalReqExample() }
|
||||
}
|
||||
}
|
||||
describe("Defaults") {
|
||||
it("Can generate a default parameter value") {
|
||||
openApiTestAllSerializers("T0022__query_with_default_parameter.json") { defaultParameter() }
|
||||
}
|
||||
}
|
||||
describe("Required Fields") {
|
||||
it("Marks a parameter as required if there is no default and it is not marked nullable") {
|
||||
openApiTestAllSerializers("T0023__required_param.json") { requiredParams() }
|
||||
@ -202,40 +108,6 @@ class KompendiumTest : DescribeSpec({
|
||||
openApiTestAllSerializers("T0026__nullable_field.json") { nullableField() }
|
||||
}
|
||||
}
|
||||
describe("Polymorphism and Generics") {
|
||||
it("can generate a polymorphic response type") {
|
||||
openApiTestAllSerializers("T0027__polymorphic_response.json") { polymorphicResponse() }
|
||||
}
|
||||
it("Can generate a collection with polymorphic response type") {
|
||||
openApiTestAllSerializers("T0028__polymorphic_list_response.json") { polymorphicCollectionResponse() }
|
||||
}
|
||||
it("Can generate a map with a polymorphic response type") {
|
||||
openApiTestAllSerializers("T0029__polymorphic_map_response.json") { polymorphicMapResponse() }
|
||||
}
|
||||
it("Can generate a response type with a generic type") {
|
||||
openApiTestAllSerializers("T0030__simple_generic_response.json") { simpleGenericResponse() }
|
||||
}
|
||||
it("Can generate a response type with a nested generic type") {
|
||||
openApiTestAllSerializers("T0031__nested_generic_response.json") { nestedGenericResponse() }
|
||||
}
|
||||
it("Can generate a polymorphic response type with generics") {
|
||||
openApiTestAllSerializers("T0032__polymorphic_response_with_generics.json") { genericPolymorphicResponse() }
|
||||
}
|
||||
it("Can handle an absolutely psycho inheritance test") {
|
||||
openApiTestAllSerializers("T0033__crazy_polymorphic_example.json") { genericPolymorphicResponseMultipleImpls() }
|
||||
}
|
||||
it("Can support nested generic collections") {
|
||||
openApiTestAllSerializers("T0039__nested_generic_collection.json") { nestedGenericCollection() }
|
||||
}
|
||||
it("Can support nested generics with multiple type parameters") {
|
||||
openApiTestAllSerializers("T0040__nested_generic_multiple_type_params.json") {
|
||||
nestedGenericMultipleParamsCollection()
|
||||
}
|
||||
}
|
||||
it("Can handle a really gnarly generic example") {
|
||||
openApiTestAllSerializers("T0043__gnarly_generic_example.json") { gnarlyGenericResponse() }
|
||||
}
|
||||
}
|
||||
describe("Custom Serializable Reader tests") {
|
||||
it("Can support ignoring fields") {
|
||||
openApiTestAllSerializers("T0048__ignored_property.json") { ignoredFieldsResponse() }
|
||||
@ -272,6 +144,9 @@ class KompendiumTest : DescribeSpec({
|
||||
it("Can have a nullable enum as a member field") {
|
||||
openApiTestAllSerializers("T0037__nullable_enum_field.json") { nullableEnumField() }
|
||||
}
|
||||
it("Can have a list of enums as a field") {
|
||||
openApiTestAllSerializers("T0076__list_of_enums.json") { returnsEnumList() }
|
||||
}
|
||||
it("Can have a nullable reference without impacting base type") {
|
||||
openApiTestAllSerializers("T0041__nullable_reference.json") { nullableReference() }
|
||||
}
|
||||
@ -306,185 +181,49 @@ class KompendiumTest : DescribeSpec({
|
||||
it("Can generate paths without application root-path") {
|
||||
openApiTestAllSerializers(
|
||||
"T0054__app_with_rootpath.json",
|
||||
applicationEnvironmentBuilder = {
|
||||
applicationSetup = {
|
||||
rootPath = "/example"
|
||||
},
|
||||
specOverrides = {
|
||||
copy(
|
||||
servers = servers.map { it.copy(url = URI("${it.url}/example")) }.toMutableList()
|
||||
)
|
||||
},
|
||||
serverConfigSetup = {
|
||||
rootPath = "/example"
|
||||
}
|
||||
) { notarizedGet() }
|
||||
}
|
||||
it("Can apply a custom serialization strategy to the openapi document") {
|
||||
val customJsonEncoder = Json {
|
||||
serializersModule = KompendiumSerializersModule.module
|
||||
encodeDefaults = true
|
||||
explicitNulls = false
|
||||
}
|
||||
describe("Error Handling") {
|
||||
it("Throws a clear exception when an unidentified type is encountered") {
|
||||
val exception = shouldThrow<UnknownSchemaException> { openApiTestAllSerializers("") { dateTimeString() } }
|
||||
exception.message should startWith("An unknown type was encountered: class java.time.Instant")
|
||||
}
|
||||
it("Throws an exception when same method for same path has been previously registered") {
|
||||
val exception = shouldThrow<IllegalArgumentException> {
|
||||
openApiTestAllSerializers(
|
||||
snapshotName = "",
|
||||
applicationSetup = {
|
||||
install(Authentication) {
|
||||
basic("basic") {
|
||||
realm = "Ktor Server"
|
||||
validate { UserIdPrincipal("Placeholder") }
|
||||
snapshotName = "T0072__custom_serialization_strategy.json",
|
||||
notarizedApplicationConfigOverrides = {
|
||||
specRoute = { spec, routing ->
|
||||
routing.route("/openapi.json") {
|
||||
get {
|
||||
call.response.headers.append("Content-Type", "application/json")
|
||||
call.respondText { customJsonEncoder.encodeToString(spec) }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
samePathSameMethod()
|
||||
contentNegotiation = {
|
||||
json(
|
||||
Json {
|
||||
encodeDefaults = true
|
||||
explicitNulls = true
|
||||
}
|
||||
}
|
||||
exception.message should startWith("A route has already been registered for path: /test/{a} and method: GET")
|
||||
}
|
||||
}
|
||||
describe("Formats") {
|
||||
it("Can set a format for a simple type schema") {
|
||||
openApiTestAllSerializers(
|
||||
snapshotName = "T0038__formatted_date_time_string.json",
|
||||
customTypes = mapOf(typeOf<Instant>() to TypeDefinition(type = "string", format = "date"))
|
||||
) { dateTimeString() }
|
||||
}
|
||||
}
|
||||
describe("Free Form") {
|
||||
// todo Assess strategies here
|
||||
}
|
||||
describe("Authentication") {
|
||||
it("Can add a default auth config by default") {
|
||||
openApiTestAllSerializers(
|
||||
snapshotName = "T0045__default_auth_config.json",
|
||||
applicationSetup = {
|
||||
install(Authentication) {
|
||||
basic("basic") {
|
||||
realm = "Ktor Server"
|
||||
validate { UserIdPrincipal("Placeholder") }
|
||||
}
|
||||
}
|
||||
},
|
||||
specOverrides = {
|
||||
this.copy(
|
||||
components = Components(
|
||||
securitySchemes = mutableMapOf(
|
||||
"basic" to BasicAuth()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
) { defaultAuthConfig() }
|
||||
) { notarizedGet() }
|
||||
}
|
||||
it("Can provide custom auth config with proper scopes") {
|
||||
openApiTestAllSerializers(
|
||||
snapshotName = "T0046__custom_auth_config.json",
|
||||
applicationSetup = {
|
||||
install(Authentication) {
|
||||
oauth("auth-oauth-google") {
|
||||
urlProvider = { "http://localhost:8080/callback" }
|
||||
providerLookup = {
|
||||
OAuthServerSettings.OAuth2ServerSettings(
|
||||
name = "google",
|
||||
authorizeUrl = "https://accounts.google.com/o/oauth2/auth",
|
||||
accessTokenUrl = "https://accounts.google.com/o/oauth2/token",
|
||||
requestMethod = HttpMethod.Post,
|
||||
clientId = "DUMMY_VAL",
|
||||
clientSecret = "DUMMY_VAL",
|
||||
defaultScopes = listOf("https://www.googleapis.com/auth/userinfo.profile"),
|
||||
extraTokenParameters = listOf("access_type" to "offline")
|
||||
)
|
||||
}
|
||||
client = HttpClient(CIO)
|
||||
}
|
||||
}
|
||||
},
|
||||
specOverrides = {
|
||||
this.copy(
|
||||
components = Components(
|
||||
securitySchemes = mutableMapOf(
|
||||
"auth-oauth-google" to OAuth(
|
||||
flows = OAuth.Flows(
|
||||
implicit = OAuth.Flows.Implicit(
|
||||
authorizationUrl = "https://accounts.google.com/o/oauth2/auth",
|
||||
scopes = mapOf(
|
||||
"write:pets" to "modify pets in your account",
|
||||
"read:pets" to "read your pets"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
) { customAuthConfig() }
|
||||
}
|
||||
it("Can provide multiple authentication strategies") {
|
||||
openApiTestAllSerializers(
|
||||
snapshotName = "T0047__multiple_auth_strategies.json",
|
||||
applicationSetup = {
|
||||
install(Authentication) {
|
||||
apiKey("api-key") {
|
||||
headerName = "X-API-KEY"
|
||||
validate {
|
||||
UserIdPrincipal("Placeholder")
|
||||
}
|
||||
}
|
||||
jwt("jwt") {
|
||||
realm = "Server"
|
||||
}
|
||||
}
|
||||
},
|
||||
specOverrides = {
|
||||
this.copy(
|
||||
components = Components(
|
||||
securitySchemes = mutableMapOf(
|
||||
"jwt" to BearerAuth("JWT"),
|
||||
"api-key" to ApiKeyAuth(ApiKeyAuth.ApiKeyLocation.HEADER, "X-API-KEY")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
) { multipleAuthStrategies() }
|
||||
}
|
||||
}
|
||||
describe("Enrichment") {
|
||||
it("Can enrich a simple request") {
|
||||
openApiTestAllSerializers("T0055__enriched_simple_request.json") { enrichedSimpleRequest() }
|
||||
}
|
||||
it("Can enrich a simple response") {
|
||||
openApiTestAllSerializers("T0058__enriched_simple_response.json") { enrichedSimpleResponse() }
|
||||
}
|
||||
it("Can enrich a nested collection") {
|
||||
openApiTestAllSerializers("T0056__enriched_nested_collection.json") { enrichedNestedCollection() }
|
||||
}
|
||||
it("Can enrich a complex generic type") {
|
||||
openApiTestAllSerializers("T0057__enriched_complex_generic_type.json") { enrichedComplexGenericType() }
|
||||
}
|
||||
it("Can enrich a generic object") {
|
||||
openApiTestAllSerializers("T0067__enriched_generic_object.json") { enrichedGenericResponse() }
|
||||
}
|
||||
}
|
||||
describe("Constraints") {
|
||||
it("Can apply constraints to an int field") {
|
||||
openApiTestAllSerializers("T0059__int_constraints.json") { intConstraints() }
|
||||
}
|
||||
it("Can apply constraints to a double field") {
|
||||
openApiTestAllSerializers("T0060__double_constraints.json") { doubleConstraints() }
|
||||
}
|
||||
it("Can apply a min and max length to a string field") {
|
||||
openApiTestAllSerializers("T0061__string_min_max_constraints.json") { stringConstraints() }
|
||||
}
|
||||
it("Can apply a pattern to a string field") {
|
||||
openApiTestAllSerializers("T0062__string_pattern_constraints.json") { stringPatternConstraints() }
|
||||
}
|
||||
it("Can apply a content encoding and media type to a string field") {
|
||||
openApiTestAllSerializers("T0063__string_content_encoding_constraints.json") {
|
||||
stringContentEncodingConstraints()
|
||||
}
|
||||
}
|
||||
it("Can apply constraints to an array field") {
|
||||
openApiTestAllSerializers("T0064__array_constraints.json") { arrayConstraints() }
|
||||
it("Can serialize a data class with a field outside of the constructor") {
|
||||
openApiTestAllSerializers("T0073__data_class_with_field_outside_constructor.json") { fieldOutsideConstructor() }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -0,0 +1,19 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers
|
||||
import io.bkbn.kompendium.core.util.dateTimeString
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
import java.time.Instant
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
class KompendiumTypeFormatTest : DescribeSpec({
|
||||
describe("Formats") {
|
||||
it("Can set a format for a simple type schema") {
|
||||
TestHelpers.openApiTestAllSerializers(
|
||||
snapshotName = "T0038__formatted_date_time_string.json",
|
||||
customTypes = mapOf(typeOf<Instant>() to TypeDefinition(type = "string", format = "date"))
|
||||
) { dateTimeString() }
|
||||
}
|
||||
}
|
||||
})
|
@ -2,18 +2,18 @@ package io.bkbn.kompendium.core.util
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||
import io.bkbn.kompendium.core.metadata.PostInfo
|
||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||
import io.bkbn.kompendium.core.util.TestModules.defaultPathDescription
|
||||
import io.bkbn.kompendium.core.util.TestModules.defaultPathSummary
|
||||
import io.bkbn.kompendium.core.util.TestModules.defaultResponseDescription
|
||||
import io.bkbn.kompendium.core.util.TestModules.rootPath
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.route
|
||||
|
||||
fun Routing.defaultAuthConfig() {
|
||||
fun Route.defaultAuthConfig() {
|
||||
authenticate("basic") {
|
||||
route(rootPath) {
|
||||
basicGetGenerator<TestResponse>()
|
||||
@ -21,7 +21,7 @@ fun Routing.defaultAuthConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.customAuthConfig() {
|
||||
fun Route.customAuthConfig() {
|
||||
authenticate("auth-oauth-google") {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
@ -42,7 +42,44 @@ fun Routing.customAuthConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.multipleAuthStrategies() {
|
||||
fun Route.customScopesOnSiblingPathOperations() {
|
||||
authenticate("auth-oauth-google") {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
}
|
||||
security = mapOf(
|
||||
"auth-oauth-google" to listOf("read:pets")
|
||||
)
|
||||
}
|
||||
post = PostInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description(defaultResponseDescription)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
}
|
||||
request {
|
||||
description(defaultResponseDescription)
|
||||
requestType<TestResponse>()
|
||||
}
|
||||
security = mapOf(
|
||||
"auth-oauth-google" to listOf("write:pets")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Route.multipleAuthStrategies() {
|
||||
authenticate("jwt", "api-key") {
|
||||
route(rootPath) {
|
||||
basicGetGenerator<TestResponse>()
|
||||
|
@ -7,13 +7,15 @@ import io.bkbn.kompendium.core.fixtures.TestNested
|
||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||
import io.bkbn.kompendium.core.util.TestModules.defaultPath
|
||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||
import io.bkbn.kompendium.enrichment.CollectionEnrichment
|
||||
import io.bkbn.kompendium.enrichment.NumberEnrichment
|
||||
import io.bkbn.kompendium.enrichment.ObjectEnrichment
|
||||
import io.bkbn.kompendium.enrichment.StringEnrichment
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.route
|
||||
|
||||
fun Routing.intConstraints() {
|
||||
fun Route.intConstraints() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
@ -23,22 +25,24 @@ fun Routing.intConstraints() {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
description("An int")
|
||||
responseType(
|
||||
enrichment = TypeEnrichment("example") {
|
||||
enrichment = ObjectEnrichment("example") {
|
||||
TestCreatedResponse::id {
|
||||
NumberEnrichment("blah-blah-blah") {
|
||||
minimum = 2
|
||||
maximum = 100
|
||||
multipleOf = 2
|
||||
}
|
||||
}
|
||||
)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.doubleConstraints() {
|
||||
fun Route.doubleConstraints() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
@ -48,13 +52,15 @@ fun Routing.doubleConstraints() {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
description("A double")
|
||||
responseType(
|
||||
enrichment = TypeEnrichment("example") {
|
||||
enrichment = ObjectEnrichment("example") {
|
||||
DoubleResponse::payload {
|
||||
NumberEnrichment("blah-blah-blah") {
|
||||
minimum = 2.0
|
||||
maximum = 100.0
|
||||
multipleOf = 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
}
|
||||
@ -63,7 +69,7 @@ fun Routing.doubleConstraints() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.stringConstraints() {
|
||||
fun Route.stringConstraints() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
@ -73,12 +79,14 @@ fun Routing.stringConstraints() {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
description("A string")
|
||||
responseType(
|
||||
enrichment = TypeEnrichment("example") {
|
||||
enrichment = ObjectEnrichment("example") {
|
||||
TestNested::nesty {
|
||||
StringEnrichment("blah") {
|
||||
maxLength = 10
|
||||
minLength = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
}
|
||||
@ -87,7 +95,7 @@ fun Routing.stringConstraints() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.stringPatternConstraints() {
|
||||
fun Route.stringPatternConstraints() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
@ -97,11 +105,13 @@ fun Routing.stringPatternConstraints() {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
description("A string")
|
||||
responseType(
|
||||
enrichment = TypeEnrichment("example") {
|
||||
enrichment = ObjectEnrichment("example") {
|
||||
TestNested::nesty {
|
||||
StringEnrichment("blah") {
|
||||
pattern = "[a-z]+"
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
}
|
||||
@ -110,7 +120,7 @@ fun Routing.stringPatternConstraints() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.stringContentEncodingConstraints() {
|
||||
fun Route.stringContentEncodingConstraints() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
@ -120,12 +130,14 @@ fun Routing.stringContentEncodingConstraints() {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
description("A string")
|
||||
responseType(
|
||||
enrichment = TypeEnrichment("example") {
|
||||
enrichment = ObjectEnrichment("example") {
|
||||
TestNested::nesty {
|
||||
StringEnrichment("blah") {
|
||||
contentEncoding = "base64"
|
||||
contentMediaType = "image/png"
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
}
|
||||
@ -134,7 +146,7 @@ fun Routing.stringContentEncodingConstraints() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.arrayConstraints() {
|
||||
fun Route.arrayConstraints() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
@ -144,13 +156,15 @@ fun Routing.arrayConstraints() {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
description("An array")
|
||||
responseType(
|
||||
enrichment = TypeEnrichment("example") {
|
||||
enrichment = ObjectEnrichment("example") {
|
||||
Page<String>::content {
|
||||
CollectionEnrichment<String>("blah") {
|
||||
minItems = 2
|
||||
maxItems = 10
|
||||
uniqueItems = true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
responseCode(HttpStatusCode.OK)
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ package io.bkbn.kompendium.core.util
|
||||
import io.bkbn.kompendium.core.fixtures.SerialNameObject
|
||||
import io.bkbn.kompendium.core.fixtures.TransientObject
|
||||
import io.bkbn.kompendium.core.fixtures.UnbackedObject
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
|
||||
fun Routing.ignoredFieldsResponse() = basicGetGenerator<TransientObject>()
|
||||
fun Routing.unbackedFieldsResponse() = basicGetGenerator<UnbackedObject>()
|
||||
fun Routing.customFieldNameResponse() = basicGetGenerator<SerialNameObject>()
|
||||
fun Route.ignoredFieldsResponse() = basicGetGenerator<TransientObject>()
|
||||
fun Route.unbackedFieldsResponse() = basicGetGenerator<UnbackedObject>()
|
||||
fun Route.customFieldNameResponse() = basicGetGenerator<SerialNameObject>()
|
||||
|
@ -3,9 +3,9 @@ package io.bkbn.kompendium.core.util
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
|
||||
fun Routing.defaultParameter() = basicGetGenerator<TestResponse>(
|
||||
fun Route.defaultParameter() = basicGetGenerator<TestResponse>(
|
||||
params = listOf(
|
||||
Parameter(
|
||||
name = "id",
|
||||
|
@ -1,22 +1,25 @@
|
||||
package io.bkbn.kompendium.core.util
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.ComplexRequest
|
||||
import io.bkbn.kompendium.core.fixtures.GenericObject
|
||||
import io.bkbn.kompendium.core.fixtures.MultiNestedGenerics
|
||||
import io.bkbn.kompendium.core.fixtures.NestedComplexItem
|
||||
import io.bkbn.kompendium.core.fixtures.TestCreatedResponse
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||
import io.bkbn.kompendium.core.fixtures.TestSimpleRequest
|
||||
import io.bkbn.kompendium.core.fixtures.GenericObject
|
||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||
import io.bkbn.kompendium.core.metadata.PostInfo
|
||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||
import io.bkbn.kompendium.enrichment.TypeEnrichment
|
||||
import io.bkbn.kompendium.enrichment.CollectionEnrichment
|
||||
import io.bkbn.kompendium.enrichment.MapEnrichment
|
||||
import io.bkbn.kompendium.enrichment.NumberEnrichment
|
||||
import io.bkbn.kompendium.enrichment.ObjectEnrichment
|
||||
import io.bkbn.kompendium.enrichment.StringEnrichment
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.route
|
||||
|
||||
fun Routing.enrichedSimpleResponse() {
|
||||
fun Route.enrichedSimpleResponse() {
|
||||
route("/enriched") {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
@ -24,11 +27,13 @@ fun Routing.enrichedSimpleResponse() {
|
||||
description(TestModules.defaultPathDescription)
|
||||
response {
|
||||
responseType(
|
||||
enrichment = TypeEnrichment("simple") {
|
||||
enrichment = ObjectEnrichment("simple") {
|
||||
TestResponse::c {
|
||||
StringEnrichment("blah-blah-blah") {
|
||||
description = "A simple description"
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
description("A good response")
|
||||
responseCode(HttpStatusCode.Created)
|
||||
@ -38,7 +43,7 @@ fun Routing.enrichedSimpleResponse() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.enrichedSimpleRequest() {
|
||||
fun Route.enrichedSimpleRequest() {
|
||||
route("/example") {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = TestModules.defaultParams
|
||||
@ -47,14 +52,18 @@ fun Routing.enrichedSimpleRequest() {
|
||||
description(TestModules.defaultPathDescription)
|
||||
request {
|
||||
requestType(
|
||||
enrichment = TypeEnrichment("simple") {
|
||||
enrichment = ObjectEnrichment("simple") {
|
||||
TestSimpleRequest::a {
|
||||
StringEnrichment("blah-blah-blah") {
|
||||
description = "A simple description"
|
||||
}
|
||||
}
|
||||
TestSimpleRequest::b {
|
||||
NumberEnrichment("blah-blah-blah") {
|
||||
deprecated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
description("A test request")
|
||||
}
|
||||
@ -68,7 +77,7 @@ fun Routing.enrichedSimpleRequest() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.enrichedNestedCollection() {
|
||||
fun Route.enrichedNestedCollection() {
|
||||
route("/example") {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = TestModules.defaultParams
|
||||
@ -77,16 +86,20 @@ fun Routing.enrichedNestedCollection() {
|
||||
description(TestModules.defaultPathDescription)
|
||||
request {
|
||||
requestType(
|
||||
enrichment = TypeEnrichment("simple") {
|
||||
enrichment = ObjectEnrichment("simple") {
|
||||
ComplexRequest::tables {
|
||||
description = "A nested item"
|
||||
typeEnrichment = TypeEnrichment("nested") {
|
||||
CollectionEnrichment<NestedComplexItem>("blah-blah") {
|
||||
description = "A nested description"
|
||||
itemEnrichment = ObjectEnrichment("nested") {
|
||||
NestedComplexItem::name {
|
||||
StringEnrichment("beleheh") {
|
||||
description = "A nested description"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
description("A test request")
|
||||
}
|
||||
@ -100,7 +113,7 @@ fun Routing.enrichedNestedCollection() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.enrichedComplexGenericType() {
|
||||
fun Route.enrichedTopLevelCollection() {
|
||||
route("/example") {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = TestModules.defaultParams
|
||||
@ -109,15 +122,57 @@ fun Routing.enrichedComplexGenericType() {
|
||||
description(TestModules.defaultPathDescription)
|
||||
request {
|
||||
requestType(
|
||||
enrichment = TypeEnrichment("simple") {
|
||||
enrichment = CollectionEnrichment<List<TestSimpleRequest>>("blah-blah") {
|
||||
itemEnrichment = ObjectEnrichment("simple") {
|
||||
TestSimpleRequest::a {
|
||||
StringEnrichment("blah-blah-blah") {
|
||||
description = "A simple description"
|
||||
}
|
||||
}
|
||||
TestSimpleRequest::b {
|
||||
NumberEnrichment("blah-blah-blah") {
|
||||
deprecated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
description("A test request")
|
||||
}
|
||||
response {
|
||||
responseCode(HttpStatusCode.Created)
|
||||
responseType<TestCreatedResponse>()
|
||||
description(TestModules.defaultResponseDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Route.enrichedComplexGenericType() {
|
||||
route("/example") {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = TestModules.defaultParams
|
||||
post = PostInfo.builder {
|
||||
summary(TestModules.defaultPathSummary)
|
||||
description(TestModules.defaultPathDescription)
|
||||
request {
|
||||
requestType(
|
||||
enrichment = ObjectEnrichment("simple") {
|
||||
MultiNestedGenerics<String, ComplexRequest>::content {
|
||||
description = "Getting pretty crazy"
|
||||
typeEnrichment = TypeEnrichment("nested") {
|
||||
ComplexRequest::tables {
|
||||
description = "A nested item"
|
||||
typeEnrichment = TypeEnrichment("nested") {
|
||||
NestedComplexItem::name {
|
||||
MapEnrichment<ComplexRequest>("blah") {
|
||||
description = "A nested description"
|
||||
valueEnrichment = ObjectEnrichment("nested") {
|
||||
ComplexRequest::tables {
|
||||
CollectionEnrichment<NestedComplexItem>("blah-blah") {
|
||||
description = "A nested description"
|
||||
itemEnrichment = ObjectEnrichment("nested") {
|
||||
NestedComplexItem::name {
|
||||
StringEnrichment("beleheh") {
|
||||
description = "A nested description"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -137,7 +192,7 @@ fun Routing.enrichedComplexGenericType() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.enrichedGenericResponse() {
|
||||
fun Route.enrichedGenericResponse() {
|
||||
route("/example") {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
@ -145,14 +200,51 @@ fun Routing.enrichedGenericResponse() {
|
||||
description(TestModules.defaultPathDescription)
|
||||
response {
|
||||
responseType(
|
||||
enrichment = TypeEnrichment("generic") {
|
||||
enrichment = ObjectEnrichment("generic") {
|
||||
description = "another description"
|
||||
GenericObject<TestSimpleRequest>::data {
|
||||
description = "A simple description"
|
||||
typeEnrichment = TypeEnrichment("simple") {
|
||||
ObjectEnrichment("simple") {
|
||||
description = "also a description"
|
||||
TestSimpleRequest::a {
|
||||
StringEnrichment("blah-blah-blah") {
|
||||
description = "A simple description"
|
||||
}
|
||||
}
|
||||
TestSimpleRequest::b {
|
||||
NumberEnrichment("blah-blah-blah") {
|
||||
deprecated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
description("A good response")
|
||||
responseCode(HttpStatusCode.Created)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Route.enrichedMap() {
|
||||
route("/example") {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
summary(TestModules.defaultPathSummary)
|
||||
description(TestModules.defaultPathDescription)
|
||||
response {
|
||||
responseType<Map<String, TestSimpleRequest>>(
|
||||
enrichment = MapEnrichment("blah") {
|
||||
description = "A nested description"
|
||||
valueEnrichment = ObjectEnrichment("nested") {
|
||||
TestSimpleRequest::a {
|
||||
StringEnrichment("blah-blah-blah") {
|
||||
description = "A simple description"
|
||||
}
|
||||
}
|
||||
TestSimpleRequest::b {
|
||||
NumberEnrichment("blah-blah-blah") {
|
||||
deprecated = true
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ package io.bkbn.kompendium.core.util
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||
import io.bkbn.kompendium.core.util.TestModules.defaultPath
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.route
|
||||
|
||||
fun Routing.samePathSameMethod() {
|
||||
fun Route.samePathSameMethod() {
|
||||
route(defaultPath) {
|
||||
basicGetGenerator<TestResponse>()
|
||||
authenticate("basic") {
|
||||
|
@ -11,13 +11,13 @@ import io.bkbn.kompendium.core.util.TestModules.defaultRequestDescription
|
||||
import io.bkbn.kompendium.core.util.TestModules.defaultResponseDescription
|
||||
import io.bkbn.kompendium.core.util.TestModules.rootPath
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.oas.payload.MediaType
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.route
|
||||
|
||||
fun Routing.reqRespExamples() {
|
||||
fun Route.reqRespExamples() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
post = PostInfo.builder {
|
||||
@ -27,7 +27,7 @@ fun Routing.reqRespExamples() {
|
||||
description(defaultRequestDescription)
|
||||
requestType<TestRequest>()
|
||||
examples(
|
||||
"Testerina" to TestRequest(TestNested("asdf"), 1.5, emptyList())
|
||||
"Testerina" to MediaType.Example(TestRequest(TestNested("asdf"), 1.5, emptyList()))
|
||||
)
|
||||
}
|
||||
response {
|
||||
@ -35,7 +35,7 @@ fun Routing.reqRespExamples() {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
examples(
|
||||
"Testerino" to TestResponse("Heya")
|
||||
"Testerino" to MediaType.Example(TestResponse("Heya"))
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -43,20 +43,20 @@ fun Routing.reqRespExamples() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.exampleParams() = basicGetGenerator<TestResponse>(
|
||||
fun Route.exampleParams() = basicGetGenerator<TestResponse>(
|
||||
params = listOf(
|
||||
Parameter(
|
||||
name = "id",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.STRING,
|
||||
examples = mapOf(
|
||||
"foo" to Parameter.Example("testing")
|
||||
"foo" to MediaType.Example("testing")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fun Routing.optionalReqExample() {
|
||||
fun Route.optionalReqExample() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
post = PostInfo.builder {
|
||||
@ -66,7 +66,7 @@ fun Routing.optionalReqExample() {
|
||||
description(defaultRequestDescription)
|
||||
requestType<TestRequest>()
|
||||
examples(
|
||||
"Testerina" to TestRequest(TestNested("asdf"), 1.5, emptyList())
|
||||
"Testerina" to MediaType.Example(TestRequest(TestNested("asdf"), 1.5, emptyList()))
|
||||
)
|
||||
required(false)
|
||||
}
|
||||
@ -75,7 +75,37 @@ fun Routing.optionalReqExample() {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
examples(
|
||||
"Testerino" to TestResponse("Heya")
|
||||
"Testerino" to MediaType.Example(TestResponse("Heya"))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Route.exampleSummaryAndDescription() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
post = PostInfo.builder {
|
||||
summary("This is a summary")
|
||||
description("This is a description")
|
||||
request {
|
||||
description("This is a request description")
|
||||
requestType<TestRequest>()
|
||||
examples(
|
||||
"Testerina" to MediaType.Example(
|
||||
TestRequest(TestNested("asdf"), 1.5, emptyList()),
|
||||
"summary",
|
||||
"description"
|
||||
)
|
||||
)
|
||||
}
|
||||
response {
|
||||
description("This is a response description")
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
examples(
|
||||
"Testerino" to MediaType.Example(TestResponse("Heya"), "summary", "description")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,10 @@ import io.bkbn.kompendium.core.util.TestModules.defaultPathSummary
|
||||
import io.bkbn.kompendium.core.util.TestModules.defaultResponseDescription
|
||||
import io.bkbn.kompendium.core.util.TestModules.rootPath
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.route
|
||||
|
||||
fun Routing.singleException() {
|
||||
fun Route.singleException() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
@ -36,7 +35,7 @@ fun Routing.singleException() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.multipleExceptions() {
|
||||
fun Route.multipleExceptions() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
@ -62,7 +61,7 @@ fun Routing.multipleExceptions() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.polymorphicException() {
|
||||
fun Route.polymorphicException() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
@ -83,7 +82,7 @@ fun Routing.polymorphicException() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.genericException() {
|
||||
fun Route.genericException() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
get = GetInfo.builder {
|
||||
|
@ -21,17 +21,16 @@ import io.bkbn.kompendium.core.util.TestModules.defaultResponseDescription
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.route
|
||||
|
||||
fun Routing.withOperationId() = basicGetGenerator<TestResponse>(operationId = "getThisDude")
|
||||
fun Routing.nullableNestedObject() = basicGetGenerator<ProfileUpdateRequest>()
|
||||
fun Routing.nullableEnumField() = basicGetGenerator<NullableEnum>()
|
||||
fun Routing.nullableReference() = basicGetGenerator<ManyThings>()
|
||||
fun Routing.dateTimeString() = basicGetGenerator<DateTimeString>()
|
||||
fun Routing.headerParameter() = basicGetGenerator<TestResponse>(
|
||||
fun Route.withOperationId() = basicGetGenerator<TestResponse>(operationId = "getThisDude")
|
||||
fun Route.nullableNestedObject() = basicGetGenerator<ProfileUpdateRequest>()
|
||||
fun Route.nullableEnumField() = basicGetGenerator<NullableEnum>()
|
||||
fun Route.nullableReference() = basicGetGenerator<ManyThings>()
|
||||
fun Route.dateTimeString() = basicGetGenerator<DateTimeString>()
|
||||
fun Route.headerParameter() = basicGetGenerator<TestResponse>(
|
||||
params = listOf(
|
||||
Parameter(
|
||||
name = "X-User-Email",
|
||||
@ -42,10 +41,10 @@ fun Routing.headerParameter() = basicGetGenerator<TestResponse>(
|
||||
)
|
||||
)
|
||||
|
||||
fun Routing.nestedTypeName() = basicGetGenerator<Nested.Response>()
|
||||
fun Routing.topLevelNullable() = basicGetGenerator<TestResponse?>()
|
||||
fun Routing.simpleRecursive() = basicGetGenerator<ColumnSchema>()
|
||||
fun Routing.samePathDifferentMethodsAndAuth() {
|
||||
fun Route.nestedTypeName() = basicGetGenerator<Nested.Response>()
|
||||
fun Route.topLevelNullable() = basicGetGenerator<TestResponse?>()
|
||||
fun Route.simpleRecursive() = basicGetGenerator<ColumnSchema>()
|
||||
fun Route.samePathDifferentMethodsAndAuth() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
|
@ -1,7 +1,9 @@
|
||||
package io.bkbn.kompendium.core.util
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.ComplexRequest
|
||||
import io.bkbn.kompendium.core.fixtures.SomethingSimilar
|
||||
import io.bkbn.kompendium.core.fixtures.TestCreatedResponse
|
||||
import io.bkbn.kompendium.core.fixtures.TestEnum
|
||||
import io.bkbn.kompendium.core.fixtures.TestRequest
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||
import io.bkbn.kompendium.core.fixtures.TestSimpleRequest
|
||||
@ -24,11 +26,9 @@ import io.bkbn.kompendium.oas.payload.Header
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.response.respondText
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.delete
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.head
|
||||
@ -38,7 +38,7 @@ import io.ktor.server.routing.post
|
||||
import io.ktor.server.routing.put
|
||||
import io.ktor.server.routing.route
|
||||
|
||||
fun Routing.notarizedGet() {
|
||||
fun Route.notarizedGet() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
@ -58,7 +58,7 @@ fun Routing.notarizedGet() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.responseHeaders() {
|
||||
fun Route.responseHeaders() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
@ -90,7 +90,7 @@ fun Routing.responseHeaders() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedPost() {
|
||||
fun Route.notarizedPost() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
@ -114,7 +114,7 @@ fun Routing.notarizedPost() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedPut() {
|
||||
fun Route.notarizedPut() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
@ -138,7 +138,7 @@ fun Routing.notarizedPut() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedDelete() {
|
||||
fun Route.notarizedDelete() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
@ -158,7 +158,7 @@ fun Routing.notarizedDelete() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedPatch() {
|
||||
fun Route.notarizedPatch() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
@ -182,7 +182,7 @@ fun Routing.notarizedPatch() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedHead() {
|
||||
fun Route.notarizedHead() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
@ -203,7 +203,7 @@ fun Routing.notarizedHead() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.notarizedOptions() {
|
||||
fun Route.notarizedOptions() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
@ -223,7 +223,7 @@ fun Routing.notarizedOptions() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.complexRequest() {
|
||||
fun Route.complexRequest() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
put = PutInfo.builder {
|
||||
@ -246,7 +246,7 @@ fun Routing.complexRequest() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.primitives() {
|
||||
fun Route.primitives() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
put = PutInfo.builder {
|
||||
@ -266,7 +266,7 @@ fun Routing.primitives() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.returnsList() {
|
||||
fun Route.returnsList() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
@ -283,7 +283,24 @@ fun Routing.returnsList() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.nonRequiredParams() {
|
||||
fun Route.returnsEnumList() {
|
||||
route(defaultPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = defaultParams
|
||||
get = GetInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
response {
|
||||
description("A Successful List-y Endeavor")
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<List<TestEnum>>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Route.nonRequiredParams() {
|
||||
route("/optional") {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = listOf(
|
||||
@ -312,7 +329,7 @@ fun Routing.nonRequiredParams() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.overrideMediaTypes() {
|
||||
fun Route.overrideMediaTypes() {
|
||||
route("/media_types") {
|
||||
install(NotarizedRoute()) {
|
||||
put = PutInfo.builder {
|
||||
@ -334,7 +351,7 @@ fun Routing.overrideMediaTypes() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.postNoReqBody() {
|
||||
fun Route.postNoReqBody() {
|
||||
route("/no_req_body") {
|
||||
install(NotarizedRoute()) {
|
||||
post = PostInfo.builder {
|
||||
@ -349,3 +366,23 @@ fun Routing.postNoReqBody() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Route.fieldOutsideConstructor() {
|
||||
route("/field_outside_constructor") {
|
||||
install(NotarizedRoute()) {
|
||||
post = PostInfo.builder {
|
||||
summary(defaultPathSummary)
|
||||
description(defaultPathDescription)
|
||||
request {
|
||||
requestType<SomethingSimilar>()
|
||||
description("A cool request")
|
||||
}
|
||||
response {
|
||||
responseType<TestResponse>()
|
||||
description("Cool response")
|
||||
responseCode(HttpStatusCode.Created)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,26 @@
|
||||
package io.bkbn.kompendium.core.util
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.Barzo
|
||||
import io.bkbn.kompendium.core.fixtures.ChillaxificationMaximization
|
||||
import io.bkbn.kompendium.core.fixtures.ComplexRequest
|
||||
import io.bkbn.kompendium.core.fixtures.Flibbity
|
||||
import io.bkbn.kompendium.core.fixtures.FlibbityGibbit
|
||||
import io.bkbn.kompendium.core.fixtures.Foosy
|
||||
import io.bkbn.kompendium.core.fixtures.Gibbity
|
||||
import io.bkbn.kompendium.core.fixtures.Gizmo
|
||||
import io.bkbn.kompendium.core.fixtures.MultiNestedGenerics
|
||||
import io.bkbn.kompendium.core.fixtures.Page
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
|
||||
fun Routing.polymorphicResponse() = basicGetGenerator<FlibbityGibbit>()
|
||||
fun Routing.polymorphicCollectionResponse() = basicGetGenerator<List<FlibbityGibbit>>()
|
||||
fun Routing.polymorphicMapResponse() = basicGetGenerator<Map<String, FlibbityGibbit>>()
|
||||
fun Routing.simpleGenericResponse() = basicGetGenerator<Gibbity<String>>()
|
||||
fun Routing.gnarlyGenericResponse() = basicGetGenerator<Foosy<Barzo<Int>, String>>()
|
||||
fun Routing.nestedGenericResponse() = basicGetGenerator<Gibbity<Map<String, String>>>()
|
||||
fun Routing.genericPolymorphicResponse() = basicGetGenerator<Flibbity<Double>>()
|
||||
fun Routing.genericPolymorphicResponseMultipleImpls() = basicGetGenerator<Flibbity<FlibbityGibbit>>()
|
||||
fun Routing.nestedGenericCollection() = basicGetGenerator<Page<Int>>()
|
||||
fun Routing.nestedGenericMultipleParamsCollection() = basicGetGenerator<MultiNestedGenerics<String, ComplexRequest>>()
|
||||
fun Route.polymorphicResponse() = basicGetGenerator<FlibbityGibbit>()
|
||||
fun Route.polymorphicCollectionResponse() = basicGetGenerator<List<FlibbityGibbit>>()
|
||||
fun Route.polymorphicMapResponse() = basicGetGenerator<Map<String, FlibbityGibbit>>()
|
||||
fun Route.simpleGenericResponse() = basicGetGenerator<Gibbity<String>>()
|
||||
fun Route.gnarlyGenericResponse() = basicGetGenerator<Foosy<Barzo<Int>, String>>()
|
||||
fun Route.nestedGenericResponse() = basicGetGenerator<Gibbity<Map<String, String>>>()
|
||||
fun Route.genericPolymorphicResponse() = basicGetGenerator<Flibbity<Double>>()
|
||||
fun Route.genericPolymorphicResponseMultipleImpls() = basicGetGenerator<Flibbity<FlibbityGibbit>>()
|
||||
fun Route.nestedGenericCollection() = basicGetGenerator<Page<Int>>()
|
||||
fun Route.nestedGenericMultipleParamsCollection() = basicGetGenerator<MultiNestedGenerics<String, ComplexRequest>>()
|
||||
fun Route.overrideSealedTypeIdentifier() = basicGetGenerator<ChillaxificationMaximization>()
|
||||
fun Route.subtypeNotCompleteSetOfParentProperties() = basicGetGenerator<Gizmo>()
|
||||
|
@ -5,9 +5,9 @@ import io.bkbn.kompendium.core.fixtures.NullableField
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
|
||||
fun Routing.requiredParams() = basicGetGenerator<TestResponse>(
|
||||
fun Route.requiredParams() = basicGetGenerator<TestResponse>(
|
||||
params = listOf(
|
||||
Parameter(
|
||||
name = "id",
|
||||
@ -17,7 +17,7 @@ fun Routing.requiredParams() = basicGetGenerator<TestResponse>(
|
||||
)
|
||||
)
|
||||
|
||||
fun Routing.nonRequiredParam() = basicGetGenerator<TestResponse>(
|
||||
fun Route.nonRequiredParam() = basicGetGenerator<TestResponse>(
|
||||
params = listOf(
|
||||
Parameter(
|
||||
name = "id",
|
||||
@ -28,5 +28,5 @@ fun Routing.nonRequiredParam() = basicGetGenerator<TestResponse>(
|
||||
)
|
||||
)
|
||||
|
||||
fun Routing.defaultField() = basicGetGenerator<DefaultField>()
|
||||
fun Routing.nullableField() = basicGetGenerator<NullableField>()
|
||||
fun Route.defaultField() = basicGetGenerator<DefaultField>()
|
||||
fun Route.nullableField() = basicGetGenerator<NullableField>()
|
||||
|
@ -11,12 +11,11 @@ import io.bkbn.kompendium.core.util.TestModules.rootPath
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.route
|
||||
import io.ktor.server.routing.param
|
||||
|
||||
fun Routing.simplePathParsing() {
|
||||
fun Route.simplePathParsing() {
|
||||
route("/this") {
|
||||
route("/is") {
|
||||
route("/a") {
|
||||
@ -49,7 +48,7 @@ fun Routing.simplePathParsing() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.rootRoute() {
|
||||
fun Route.rootRoute() {
|
||||
route(rootPath) {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = listOf(defaultParams.last())
|
||||
@ -66,7 +65,7 @@ fun Routing.rootRoute() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.nestedUnderRoot() {
|
||||
fun Route.nestedUnderRoot() {
|
||||
route("/") {
|
||||
route("/testerino") {
|
||||
install(NotarizedRoute()) {
|
||||
@ -84,7 +83,7 @@ fun Routing.nestedUnderRoot() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.trailingSlash() {
|
||||
fun Route.trailingSlash() {
|
||||
route("/test") {
|
||||
route("/") {
|
||||
install(NotarizedRoute()) {
|
||||
@ -102,7 +101,7 @@ fun Routing.trailingSlash() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Routing.paramWrapper() {
|
||||
fun Route.paramWrapper() {
|
||||
route("/test") {
|
||||
param("a") {
|
||||
param("b") {
|
||||
|
@ -9,7 +9,6 @@ import io.bkbn.kompendium.core.util.TestModules.rootPath
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.routing.route
|
||||
|
@ -132,7 +132,6 @@
|
||||
]
|
||||
},
|
||||
"SimpleEnum": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ONE",
|
||||
"TWO"
|
||||
|
@ -86,12 +86,18 @@
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.ComplexGibbit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c",
|
||||
"z"
|
||||
"z",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"SimpleGibbit": {
|
||||
@ -102,10 +108,16 @@
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.SimpleGibbit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
"a",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"FlibbityGibbit": {
|
||||
|
@ -82,11 +82,17 @@
|
||||
},
|
||||
"f": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.Bibbity"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"f"
|
||||
"f",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"Gibbity-String": {
|
||||
@ -94,10 +100,16 @@
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.Gibbity"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
"a",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"Flibbity-String": {
|
||||
|
@ -65,12 +65,18 @@
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.ComplexGibbit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c",
|
||||
"z"
|
||||
"z",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"SimpleGibbit": {
|
||||
@ -81,10 +87,16 @@
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.SimpleGibbit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
"a",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"FlibbityGibbit": {
|
||||
|
@ -65,12 +65,18 @@
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.ComplexGibbit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c",
|
||||
"z"
|
||||
"z",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"SimpleGibbit": {
|
||||
@ -81,10 +87,16 @@
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.SimpleGibbit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
"a",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"List-FlibbityGibbit": {
|
||||
|
@ -65,12 +65,18 @@
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.ComplexGibbit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c",
|
||||
"z"
|
||||
"z",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"SimpleGibbit": {
|
||||
@ -81,10 +87,16 @@
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.SimpleGibbit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
"a",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"Map-String-FlibbityGibbit": {
|
||||
|
@ -62,11 +62,17 @@
|
||||
"f": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.Bibbity"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"f"
|
||||
"f",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"Gibbity-Double": {
|
||||
@ -75,10 +81,16 @@
|
||||
"a": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.Gibbity"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
"a",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"Flibbity-Double": {
|
||||
|
@ -53,40 +53,6 @@
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ComplexGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c",
|
||||
"z"
|
||||
]
|
||||
},
|
||||
"SimpleGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
]
|
||||
},
|
||||
"Bibbity-FlibbityGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -102,11 +68,63 @@
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.Bibbity"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"f"
|
||||
"f",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"ComplexGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.ComplexGibbit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c",
|
||||
"z",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"SimpleGibbit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
},
|
||||
"z": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.SimpleGibbit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"Gibbity-FlibbityGibbit": {
|
||||
@ -121,10 +139,16 @@
|
||||
"$ref": "#/components/schemas/SimpleGibbit"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"io.bkbn.kompendium.core.fixtures.Gibbity"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
"a",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"Flibbity-FlibbityGibbit": {
|
||||
|
@ -71,7 +71,6 @@
|
||||
},
|
||||
"TestEnum":
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"YES",
|
||||
"NO"
|
||||
|
@ -119,7 +119,6 @@
|
||||
]
|
||||
},
|
||||
"SimpleEnum": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ONE",
|
||||
"TWO"
|
||||
|
@ -54,7 +54,6 @@
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ColumnMode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"NULLABLE",
|
||||
"REQUIRED",
|
||||
|
@ -111,9 +111,9 @@
|
||||
},
|
||||
"tables": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NestedComplexItem-nested"
|
||||
"$ref": "#/components/schemas/NestedComplexItem-blah-blah"
|
||||
},
|
||||
"description": "A nested item",
|
||||
"description": "A nested description",
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
@ -154,11 +154,29 @@
|
||||
]
|
||||
},
|
||||
"SimpleEnum": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ONE",
|
||||
"TWO"
|
||||
]
|
||||
},
|
||||
"NestedComplexItem-blah-blah": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alias": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/components/schemas/CrazyItem"
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "A nested description"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"alias",
|
||||
"name"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
|
@ -105,9 +105,9 @@
|
||||
"properties": {
|
||||
"content": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/components/schemas/ComplexRequest-nested"
|
||||
"$ref": "#/components/schemas/ComplexRequest-blah"
|
||||
},
|
||||
"description": "Getting pretty crazy",
|
||||
"description": "A nested description",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
@ -126,9 +126,9 @@
|
||||
},
|
||||
"tables": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NestedComplexItem-nested"
|
||||
"$ref": "#/components/schemas/NestedComplexItem-blah-blah"
|
||||
},
|
||||
"description": "A nested item",
|
||||
"description": "A nested description",
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
@ -169,11 +169,52 @@
|
||||
]
|
||||
},
|
||||
"SimpleEnum": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ONE",
|
||||
"TWO"
|
||||
]
|
||||
},
|
||||
"NestedComplexItem-blah-blah": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"alias": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/components/schemas/CrazyItem"
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "A nested description"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"alias",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"ComplexRequest-blah": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amazingField": {
|
||||
"type": "string"
|
||||
},
|
||||
"org": {
|
||||
"type": "string"
|
||||
},
|
||||
"tables": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/NestedComplexItem-blah-blah"
|
||||
},
|
||||
"description": "A nested description",
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"amazingField",
|
||||
"org",
|
||||
"tables"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
|
@ -58,7 +58,7 @@
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/TestSimpleRequest-simple",
|
||||
"description": "A simple description"
|
||||
"description": "also a description"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -0,0 +1,106 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ChillaxificationMaximization"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Chillax": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"chillax"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"ToDaMax": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"maximize"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"ChillaxificationMaximization": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Chillax"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ToDaMax"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Gizmo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Gizmo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"title"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -27,15 +27,15 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/list/{name}/page/{page}": {
|
||||
"/test/{a}": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Location",
|
||||
"description": "example location",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "does great things",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@ -49,7 +49,7 @@
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
@ -58,8 +58,8 @@
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"in": "path",
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
@ -0,0 +1,94 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/field_outside_constructor": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "A cool request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SomethingSimilar"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Cool response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
]
|
||||
},
|
||||
"SomethingSimilar": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -27,15 +27,15 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/type/{name}/edit": {
|
||||
"/": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Edit",
|
||||
"description": "example location",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "does great things",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@ -45,29 +45,34 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": [
|
||||
"deprecated": false,
|
||||
"security": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
"auth-oauth-google": [
|
||||
"read:pets"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"/type/{name}/other/{page}": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "Other",
|
||||
"description": "example location",
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "does great things",
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
@ -77,29 +82,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": [
|
||||
"deprecated": false,
|
||||
"security": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
"auth-oauth-google": [
|
||||
"write:pets"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
@ -117,7 +109,20 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
"securitySchemes": {
|
||||
"auth-oauth-google": {
|
||||
"flows": {
|
||||
"implicit": {
|
||||
"authorizationUrl": "https://accounts.google.com/o/oauth2/auth",
|
||||
"scopes": {
|
||||
"write:pets": "modify pets in your account",
|
||||
"read:pets": "read your pets"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "oauth2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
@ -0,0 +1,140 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "This is a summary",
|
||||
"description": "This is a description",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "This is a request description",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestRequest"
|
||||
},
|
||||
"examples": {
|
||||
"Testerina": {
|
||||
"value": {
|
||||
"fieldName": {
|
||||
"nesty": "asdf"
|
||||
},
|
||||
"b": 1.5,
|
||||
"aaa": []
|
||||
},
|
||||
"summary": "summary",
|
||||
"description": "description"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "This is a response description",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
},
|
||||
"examples": {
|
||||
"Testerino": {
|
||||
"value": {
|
||||
"c": "Heya"
|
||||
},
|
||||
"summary": "summary",
|
||||
"description": "description"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
]
|
||||
},
|
||||
"TestRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"aaa": {
|
||||
"items": {
|
||||
"type": "number",
|
||||
"format": "int64"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"fieldName": {
|
||||
"$ref": "#/components/schemas/TestNested"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"aaa",
|
||||
"b",
|
||||
"fieldName"
|
||||
]
|
||||
},
|
||||
"TestNested": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
93
core/src/test/resources/T0076__list_of_enums.json
Normal file
93
core/src/test/resources/T0076__list_of_enums.json
Normal file
@ -0,0 +1,93 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/{a}": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful List-y Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/List-TestEnum"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestEnum": {
|
||||
"enum": [
|
||||
"YES",
|
||||
"NO"
|
||||
]
|
||||
},
|
||||
"List-TestEnum": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TestEnum"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
150
core/src/test/resources/T0077__enriched_top_level_list.json
Normal file
150
core/src/test/resources/T0077__enriched_top_level_list.json
Normal file
@ -0,0 +1,150 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/example": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "A test request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/List-TestSimpleRequest-blah-blah"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestCreatedResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestCreatedResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c",
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"TestSimpleRequest-simple": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string",
|
||||
"description": "A simple description"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"format": "int32",
|
||||
"deprecated": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
},
|
||||
"TestSimpleRequest-blah-blah": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string",
|
||||
"description": "A simple description"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"format": "int32",
|
||||
"deprecated": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
},
|
||||
"List-TestSimpleRequest-blah-blah": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TestSimpleRequest-blah-blah"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
103
core/src/test/resources/T0078__enriched_top_level_map.json
Normal file
103
core/src/test/resources/T0078__enriched_top_level_map.json
Normal file
@ -0,0 +1,103 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/example": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Great Summary!",
|
||||
"description": "testing more",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "A good response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Map-String-TestSimpleRequest-blah"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestSimpleRequest-nested": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string",
|
||||
"description": "A simple description"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"format": "int32",
|
||||
"deprecated": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
},
|
||||
"TestSimpleRequest-blah": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string",
|
||||
"description": "A simple description"
|
||||
},
|
||||
"b": {
|
||||
"type": "number",
|
||||
"format": "int32",
|
||||
"deprecated": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
},
|
||||
"Map-String-TestSimpleRequest-blah": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/components/schemas/TestSimpleRequest-blah"
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package io.bkbn.kompendium.core.fixtures
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.google.gson.annotations.Expose
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import io.bkbn.kompendium.json.schema.SchemaConfigurator
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
/*
|
||||
These are test implementation and may well be a good starting point for creating production ones.
|
||||
Both Gson and Jackson are complex and can achieve this things is more than one way therefore
|
||||
these will not always work hence why they are in the test package
|
||||
*/
|
||||
|
||||
class GsonSchemaConfigurator: SchemaConfigurator {
|
||||
|
||||
override fun serializableMemberProperties(clazz: KClass<*>): Collection<KProperty1<out Any, *>> {
|
||||
// NOTE: This is test logic Expose is set at a global Gson level so configure to match your Gson set up
|
||||
val hasAnyExpose = clazz.memberProperties.any { it.hasJavaAnnotation<Expose>() }
|
||||
return if(hasAnyExpose) {
|
||||
clazz.memberProperties
|
||||
.filter { it.hasJavaAnnotation<Expose>() }
|
||||
} else clazz.memberProperties
|
||||
}
|
||||
|
||||
override fun serializableName(property: KProperty1<out Any, *>): String =
|
||||
property.getJavaAnnotation<SerializedName>()?.value?: property.name
|
||||
|
||||
}
|
||||
|
||||
class JacksonSchemaConfigurator: SchemaConfigurator {
|
||||
|
||||
override fun serializableMemberProperties(clazz: KClass<*>): Collection<KProperty1<out Any, *>> =
|
||||
clazz.memberProperties
|
||||
.filterNot {
|
||||
it.hasJavaAnnotation<JsonIgnore>()
|
||||
}
|
||||
|
||||
override fun serializableName(property: KProperty1<out Any, *>): String =
|
||||
property.getJavaAnnotation<JsonProperty>()?.value?: property.name
|
||||
|
||||
}
|
||||
|
||||
inline fun <reified T: Annotation> KProperty1<*, *>.hasJavaAnnotation(): Boolean {
|
||||
return javaField?.isAnnotationPresent(T::class.java)?: false
|
||||
}
|
||||
|
||||
inline fun <reified T: Annotation> KProperty1<*, *>.getJavaAnnotation(): T? {
|
||||
return javaField?.getDeclaredAnnotation(T::class.java)
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package io.bkbn.kompendium.core.fixtures
|
||||
|
||||
enum class SupportedSerializer {
|
||||
KOTLINX,
|
||||
GSON,
|
||||
JACKSON
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package io.bkbn.kompendium.core.fixtures
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import io.bkbn.kompendium.core.fixtures.TestSpecs.defaultSpec
|
||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||
import io.bkbn.kompendium.core.routes.redoc
|
||||
@ -16,19 +14,18 @@ import io.kotest.matchers.shouldNot
|
||||
import io.kotest.matchers.string.beBlank
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.serialization.gson.gson
|
||||
import io.ktor.serialization.jackson.jackson
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.engine.ApplicationEngineEnvironmentBuilder
|
||||
import io.ktor.server.application.ServerConfigBuilder
|
||||
import io.ktor.server.engine.ApplicationEnvironmentBuilder
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiationConfig
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.testing.ApplicationTestBuilder
|
||||
import io.ktor.server.testing.testApplication
|
||||
import java.io.File
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
import kotlin.reflect.KType
|
||||
|
||||
object TestHelpers {
|
||||
@ -55,7 +52,7 @@ object TestHelpers {
|
||||
/**
|
||||
* This will take a provided JSON snapshot file, retrieve it from the resource folder,
|
||||
* and build a test ktor server to compare the expected output with the output found in the default
|
||||
* OpenAPI json endpoint. By default, this will run the same test with Gson, Kotlinx, and Jackson serializers
|
||||
* OpenAPI json endpoint.
|
||||
* @param snapshotName The snapshot file to retrieve from the resources folder
|
||||
*/
|
||||
fun openApiTestAllSerializers(
|
||||
@ -63,79 +60,62 @@ object TestHelpers {
|
||||
customTypes: Map<KType, JsonSchema> = emptyMap(),
|
||||
applicationSetup: Application.() -> Unit = { },
|
||||
specOverrides: OpenApiSpec.() -> OpenApiSpec = { this },
|
||||
applicationEnvironmentBuilder: ApplicationEngineEnvironmentBuilder.() -> Unit = {},
|
||||
routeUnderTest: Routing.() -> Unit
|
||||
applicationEnvironmentBuilder: ApplicationEnvironmentBuilder.() -> Unit = {},
|
||||
notarizedApplicationConfigOverrides: NotarizedApplication.Config.() -> Unit = {},
|
||||
contentNegotiation: ContentNegotiationConfig.() -> Unit = {
|
||||
json(Json {
|
||||
encodeDefaults = true
|
||||
explicitNulls = false
|
||||
serializersModule = KompendiumSerializersModule.module
|
||||
})
|
||||
},
|
||||
|
||||
serverConfigSetup: ServerConfigBuilder.() -> Unit = { },
|
||||
routeUnderTest: Route.() -> Unit
|
||||
) {
|
||||
openApiTest(
|
||||
snapshotName,
|
||||
SupportedSerializer.KOTLINX,
|
||||
routeUnderTest,
|
||||
applicationSetup,
|
||||
specOverrides,
|
||||
customTypes,
|
||||
applicationEnvironmentBuilder
|
||||
)
|
||||
openApiTest(
|
||||
snapshotName,
|
||||
SupportedSerializer.JACKSON,
|
||||
routeUnderTest,
|
||||
applicationSetup,
|
||||
specOverrides,
|
||||
customTypes,
|
||||
applicationEnvironmentBuilder
|
||||
)
|
||||
openApiTest(
|
||||
snapshotName,
|
||||
SupportedSerializer.GSON,
|
||||
routeUnderTest,
|
||||
applicationSetup,
|
||||
specOverrides,
|
||||
customTypes,
|
||||
applicationEnvironmentBuilder
|
||||
notarizedApplicationConfigOverrides,
|
||||
contentNegotiation,
|
||||
applicationEnvironmentBuilder,
|
||||
serverConfigSetup
|
||||
)
|
||||
}
|
||||
|
||||
private fun openApiTest(
|
||||
snapshotName: String,
|
||||
serializer: SupportedSerializer,
|
||||
routeUnderTest: Routing.() -> Unit,
|
||||
routeUnderTest: Route.() -> Unit,
|
||||
applicationSetup: Application.() -> Unit,
|
||||
specOverrides: OpenApiSpec.() -> OpenApiSpec,
|
||||
typeOverrides: Map<KType, JsonSchema> = emptyMap(),
|
||||
applicationBuilder: ApplicationEngineEnvironmentBuilder.() -> Unit = {}
|
||||
notarizedApplicationConfigOverrides: NotarizedApplication.Config.() -> Unit,
|
||||
contentNegotiation: ContentNegotiationConfig.() -> Unit,
|
||||
applicationBuilder: ApplicationEnvironmentBuilder.() -> Unit,
|
||||
serverConfigSetup: ServerConfigBuilder.() -> Unit
|
||||
) = testApplication {
|
||||
environment(applicationBuilder)
|
||||
install(NotarizedApplication()) {
|
||||
customTypes = typeOverrides
|
||||
spec = defaultSpec().specOverrides()
|
||||
schemaConfigurator = when (serializer) {
|
||||
SupportedSerializer.KOTLINX -> KotlinXSchemaConfigurator()
|
||||
SupportedSerializer.GSON -> GsonSchemaConfigurator()
|
||||
SupportedSerializer.JACKSON -> JacksonSchemaConfigurator()
|
||||
}
|
||||
spec = { specOverrides(defaultSpec()) }
|
||||
schemaConfigurator = KotlinXSchemaConfigurator()
|
||||
notarizedApplicationConfigOverrides()
|
||||
}
|
||||
install(ContentNegotiation) {
|
||||
when (serializer) {
|
||||
SupportedSerializer.KOTLINX -> json(Json {
|
||||
encodeDefaults = true
|
||||
explicitNulls = false
|
||||
serializersModule = KompendiumSerializersModule.module
|
||||
})
|
||||
|
||||
SupportedSerializer.GSON -> gson()
|
||||
SupportedSerializer.JACKSON -> jackson(ContentType.Application.Json) {
|
||||
enable(SerializationFeature.INDENT_OUTPUT)
|
||||
setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
}
|
||||
}
|
||||
contentNegotiation()
|
||||
}
|
||||
application(applicationSetup)
|
||||
serverConfig(serverConfigSetup)
|
||||
routing {
|
||||
swagger()
|
||||
redoc()
|
||||
routeUnderTest()
|
||||
}
|
||||
val root = ApplicationEngineEnvironmentBuilder().apply(applicationBuilder).rootPath
|
||||
|
||||
val root = ServerConfigBuilder(ApplicationEnvironmentBuilder().apply(applicationBuilder).build()).apply(serverConfigSetup).rootPath
|
||||
compareOpenAPISpec(root, snapshotName)
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
package io.bkbn.kompendium.core.fixtures
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.google.gson.annotations.Expose
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.time.Instant
|
||||
|
||||
@Serializable
|
||||
@ -91,6 +89,25 @@ data class AnothaJamma(val b: Float) : SlammaJamma
|
||||
|
||||
data class InsaneJamma(val c: SlammaJamma) : SlammaJamma
|
||||
|
||||
sealed interface ChillaxificationMaximization
|
||||
|
||||
@Serializable
|
||||
@SerialName("chillax")
|
||||
data class Chillax(val a: String) : ChillaxificationMaximization
|
||||
|
||||
@Serializable
|
||||
@SerialName("maximize")
|
||||
data class ToDaMax(val b: Int) : ChillaxificationMaximization
|
||||
|
||||
sealed class Gadget(
|
||||
open val title: String,
|
||||
open val description: String
|
||||
)
|
||||
|
||||
class Gizmo(
|
||||
override val title: String,
|
||||
) : Gadget(title, "Just a gizmo")
|
||||
|
||||
sealed interface Flibbity<T>
|
||||
|
||||
data class Gibbity<T>(val a: T) : Flibbity<T>
|
||||
@ -158,9 +175,7 @@ object Nested {
|
||||
|
||||
@Serializable
|
||||
data class TransientObject(
|
||||
@field:Expose
|
||||
val nonTransient: String,
|
||||
@field:JsonIgnore
|
||||
@Transient
|
||||
val transient: String = "transient"
|
||||
)
|
||||
@ -174,8 +189,6 @@ data class UnbackedObject(
|
||||
|
||||
@Serializable
|
||||
data class SerialNameObject(
|
||||
@field:JsonProperty("snake_case_name")
|
||||
@field:SerializedName("snake_case_name")
|
||||
@SerialName("snake_case_name")
|
||||
val camelCaseName: String
|
||||
)
|
||||
@ -194,3 +207,8 @@ enum class Color {
|
||||
data class ObjectWithEnum(
|
||||
val color: Color
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SomethingSimilar(val a: String) {
|
||||
val b = "something else"
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ complexity:
|
||||
active: true
|
||||
functionThreshold: 10
|
||||
constructorThreshold: 15
|
||||
ComplexMethod:
|
||||
threshold: 20
|
||||
CyclomaticComplexMethod:
|
||||
threshold: 15
|
||||
style:
|
||||
MaxLineLength:
|
||||
excludes: ['**/test/**/*']
|
||||
|
@ -4,7 +4,6 @@
|
||||
* [Plugins](plugins/index.md)
|
||||
* [Notarized Application](plugins/notarized_application.md)
|
||||
* [Notarized Route](plugins/notarized_route.md)
|
||||
* [Notarized Locations](plugins/notarized_locations.md)
|
||||
* [Notarized Resources](plugins/notarized_resources.md)
|
||||
* [Concepts](concepts/index.md)
|
||||
* [Enrichment](concepts/enrichment.md)
|
||||
|
@ -12,7 +12,6 @@ At the moment, the following playground applications are
|
||||
| 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 |
|
||||
| Resources | Using the Ktor Resources API to define routes |
|
||||
|
||||
You can find all of the playground
|
||||
|
@ -7,5 +7,5 @@ From there, a `NotarizedRoute` plugin is attached to each route you wish to docu
|
||||
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.
|
||||
Finally, there is the `NotarizedResources` plugin that allows you to leverage and document your usage of the
|
||||
Ktor [Resources](https://ktor.io/docs/server-resources.html) API.
|
||||
|
@ -1,62 +0,0 @@
|
||||
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!
|
@ -3,7 +3,7 @@ plugins {
|
||||
id("io.bkbn.sourdough.library.jvm")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
id("com.adarshr.test-logger")
|
||||
id("maven-publish")
|
||||
id("com.vanniktech.maven.publish")
|
||||
id("java-library")
|
||||
id("signing")
|
||||
id("org.jetbrains.kotlinx.kover")
|
||||
|
@ -0,0 +1,16 @@
|
||||
package io.bkbn.kompendium.enrichment
|
||||
|
||||
class BooleanEnrichment(override val id: String) : Enrichment {
|
||||
override var deprecated: Boolean? = null
|
||||
override var description: String? = null
|
||||
|
||||
companion object {
|
||||
inline operator fun invoke(
|
||||
id: String,
|
||||
init: BooleanEnrichment.() -> Unit
|
||||
): BooleanEnrichment {
|
||||
val builder = BooleanEnrichment(id)
|
||||
return builder.apply(init)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package io.bkbn.kompendium.enrichment
|
||||
|
||||
class CollectionEnrichment<T>(override val id: String) : TypeEnrichment<T> {
|
||||
|
||||
override var deprecated: Boolean? = null
|
||||
override var description: String? = null
|
||||
|
||||
var maxItems: Int? = null
|
||||
var minItems: Int? = null
|
||||
var uniqueItems: Boolean? = null
|
||||
// TODO How to handle contains, minContains, maxContains?
|
||||
|
||||
var itemEnrichment: TypeEnrichment<*>? = null
|
||||
|
||||
companion object {
|
||||
inline operator fun <reified T> invoke(
|
||||
id: String,
|
||||
init: CollectionEnrichment<T>.() -> Unit
|
||||
): CollectionEnrichment<T> {
|
||||
val builder = CollectionEnrichment<T>(id)
|
||||
return builder.apply(init)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
package io.bkbn.kompendium.enrichment
|
||||
|
||||
sealed interface Enrichment
|
||||
sealed interface Enrichment {
|
||||
val id: String
|
||||
var deprecated: Boolean?
|
||||
var description: String?
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
package io.bkbn.kompendium.enrichment
|
||||
|
||||
class MapEnrichment<V>(override val id: String) : TypeEnrichment<V> {
|
||||
|
||||
override var deprecated: Boolean? = null
|
||||
override var description: String? = null
|
||||
|
||||
var maxProperties: Int? = null
|
||||
var minProperties: Int? = null
|
||||
|
||||
lateinit var keyEnrichment: StringEnrichment
|
||||
lateinit var valueEnrichment: TypeEnrichment<*>
|
||||
|
||||
companion object {
|
||||
inline operator fun <reified V> invoke(
|
||||
id: String,
|
||||
init: MapEnrichment<V>.() -> Unit
|
||||
): MapEnrichment<V> {
|
||||
val builder = MapEnrichment<V>(id)
|
||||
return builder.apply(init)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package io.bkbn.kompendium.enrichment
|
||||
class NumberEnrichment(override val id: String) : Enrichment {
|
||||
|
||||
override var deprecated: Boolean? = null
|
||||
override var description: String? = null
|
||||
|
||||
var multipleOf: Number? = null
|
||||
var maximum: Number? = null
|
||||
var exclusiveMaximum: Number? = null
|
||||
var minimum: Number? = null
|
||||
var exclusiveMinimum: Number? = null
|
||||
|
||||
companion object {
|
||||
inline operator fun invoke(id: String, init: NumberEnrichment.() -> Unit): NumberEnrichment {
|
||||
val builder = NumberEnrichment(id)
|
||||
return builder.apply(init)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package io.bkbn.kompendium.enrichment
|
||||
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
class ObjectEnrichment<T>(override val id: String) : TypeEnrichment<T> {
|
||||
|
||||
override var deprecated: Boolean? = null
|
||||
override var description: String? = null
|
||||
|
||||
private val _propertyEnrichments: MutableMap<KProperty1<*, *>, Enrichment> = mutableMapOf()
|
||||
|
||||
val propertyEnrichment: Map<KProperty1<*, *>, Enrichment>
|
||||
get() = _propertyEnrichments.toMap()
|
||||
|
||||
operator fun <R> KProperty1<T, R>.invoke(init: () -> Enrichment) {
|
||||
require(!_propertyEnrichments.containsKey(this)) { "${this.name} has already been registered" }
|
||||
val enrichment = init.invoke()
|
||||
_propertyEnrichments[this] = enrichment
|
||||
}
|
||||
|
||||
companion object {
|
||||
inline operator fun <reified T> invoke(id: String, init: ObjectEnrichment<T>.() -> Unit): ObjectEnrichment<T> {
|
||||
val builder = ObjectEnrichment<T>(id)
|
||||
return builder.apply(init)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package io.bkbn.kompendium.enrichment
|
||||
|
||||
/**
|
||||
* Reference https://json-schema.org/draft/2020-12/json-schema-validation.html#name-multipleof
|
||||
*/
|
||||
class PropertyEnrichment : Enrichment {
|
||||
// Metadata
|
||||
var deprecated: Boolean? = null
|
||||
var description: String? = null
|
||||
var typeEnrichment: TypeEnrichment<*>? = null
|
||||
|
||||
// Number and Integer Constraints
|
||||
var multipleOf: Number? = null
|
||||
var maximum: Number? = null
|
||||
var exclusiveMaximum: Number? = null
|
||||
var minimum: Number? = null
|
||||
var exclusiveMinimum: Number? = null
|
||||
|
||||
// String constraints
|
||||
var maxLength: Int? = null
|
||||
var minLength: Int? = null
|
||||
var pattern: String? = null
|
||||
var contentEncoding: String? = null
|
||||
var contentMediaType: String? = null
|
||||
// TODO how to handle contentSchema?
|
||||
|
||||
// Array constraints
|
||||
var maxItems: Int? = null
|
||||
var minItems: Int? = null
|
||||
var uniqueItems: Boolean? = null
|
||||
// TODO How to handle contains, minContains, maxContains?
|
||||
|
||||
// Object constraints
|
||||
var maxProperties: Int? = null
|
||||
var minProperties: Int? = null
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package io.bkbn.kompendium.enrichment
|
||||
|
||||
class StringEnrichment(override val id: String) : Enrichment {
|
||||
override var deprecated: Boolean? = null
|
||||
override var description: String? = null
|
||||
|
||||
var maxLength: Int? = null
|
||||
var minLength: Int? = null
|
||||
var pattern: String? = null
|
||||
var contentEncoding: String? = null
|
||||
var contentMediaType: String? = null
|
||||
// TODO how to handle contentSchema?
|
||||
|
||||
companion object {
|
||||
inline operator fun invoke(id: String, init: StringEnrichment.() -> Unit): StringEnrichment {
|
||||
val builder = StringEnrichment(id)
|
||||
return builder.apply(init)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +1,3 @@
|
||||
package io.bkbn.kompendium.enrichment
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
class TypeEnrichment<T>(val id: String) : Enrichment {
|
||||
|
||||
private val enrichments: MutableMap<KProperty1<*, *>, Enrichment> = mutableMapOf()
|
||||
|
||||
fun getEnrichmentForProperty(property: KProperty<*>): Enrichment? = enrichments[property]
|
||||
|
||||
operator fun <R> KProperty1<T, R>.invoke(init: PropertyEnrichment.() -> Unit) {
|
||||
require(!enrichments.containsKey(this)) { "${this.name} has already been registered" }
|
||||
val propertyEnrichment = PropertyEnrichment()
|
||||
init.invoke(propertyEnrichment)
|
||||
enrichments[this] = propertyEnrichment
|
||||
}
|
||||
|
||||
companion object {
|
||||
inline operator fun <reified T> invoke(id: String, init: TypeEnrichment<T>.() -> Unit): TypeEnrichment<T> {
|
||||
val builder = TypeEnrichment<T>(id)
|
||||
return builder.apply(init)
|
||||
}
|
||||
}
|
||||
}
|
||||
sealed interface TypeEnrichment<T> : Enrichment
|
||||
|
64
flake.lock
generated
Normal file
64
flake.lock
generated
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": [
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1723362943,
|
||||
"narHash": "sha256-dFZRVSgmJkyM0bkPpaYRtG/kRMRTorUIDj8BxoOt1T4=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a58bc8ad779655e790115244571758e8de055e3d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
29
flake.nix
Normal file
29
flake.nix
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
description = "Minimal example of building Kotlin with Gradle and Nix";
|
||||
|
||||
inputs = {nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";};
|
||||
|
||||
inputs.systems.url = "github:nix-systems/default";
|
||||
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
inputs.flake-utils.inputs.systems.follows = "systems";
|
||||
|
||||
outputs = {
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
...
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (system: let
|
||||
pkgs = import nixpkgs {inherit system;};
|
||||
updateLocks = pkgs.callPackage ./update-locks.nix {};
|
||||
in {
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
gradle_8
|
||||
temurin-bin-21
|
||||
updateLocks
|
||||
ktlint
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
# Kompendium
|
||||
project.version=3.14.3
|
||||
project.version=4.0.0-test
|
||||
|
||||
# Kotlin
|
||||
kotlin.code.style=official
|
||||
#kotlin.experimental.tryK2=true
|
||||
|
||||
# Gradle
|
||||
org.gradle.vfs.watch=true
|
||||
org.gradle.vfs.verbose=true
|
||||
@ -9,6 +12,8 @@ org.gradle.jvmargs=-Xmx2000m
|
||||
org.gradle.parallel=true
|
||||
|
||||
# Dependencies
|
||||
ktorVersion=2.3.0
|
||||
kotestVersion=5.6.2
|
||||
detektVersion=1.22.0
|
||||
kotlinVersion=2.0.21
|
||||
kotlinSerializeVersion=1.7.+
|
||||
ktorVersion=3.0.1
|
||||
kotestVersion=6.0.0.M1
|
||||
detektVersion=1.23.7
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
22
gradlew
vendored
22
gradlew
vendored
@ -83,7 +83,8 @@ done
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@ -130,18 +131,21 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@ -149,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@ -198,11 +202,11 @@ fi
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
@ -4,7 +4,7 @@ plugins {
|
||||
id("io.bkbn.sourdough.library.jvm")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
id("com.adarshr.test-logger")
|
||||
id("maven-publish")
|
||||
id("com.vanniktech.maven.publish")
|
||||
id("java-library")
|
||||
id("signing")
|
||||
id("org.jetbrains.kotlinx.kover")
|
||||
@ -19,12 +19,14 @@ sourdoughLibrary {
|
||||
dependencies {
|
||||
// Versions
|
||||
val detektVersion: String by project
|
||||
val kotlinVersion: String by project
|
||||
val kotlinSerializeVersion: String by project
|
||||
|
||||
// Kompendium
|
||||
api(projects.kompendiumEnrichment)
|
||||
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.21")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinSerializeVersion")
|
||||
|
||||
// Formatting
|
||||
detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion")
|
||||
|
@ -1,20 +1,51 @@
|
||||
package io.bkbn.kompendium.json.schema
|
||||
|
||||
import io.bkbn.kompendium.json.schema.definition.EnumDefinition
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Transient
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.hasAnnotation
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
class KotlinXSchemaConfigurator : SchemaConfigurator {
|
||||
|
||||
override fun serializableMemberProperties(clazz: KClass<*>): Collection<KProperty1<out Any, *>> =
|
||||
clazz.memberProperties
|
||||
override fun serializableMemberProperties(clazz: KClass<*>): Collection<KProperty1<out Any, *>> {
|
||||
return clazz.memberProperties
|
||||
.filterNot { it.hasAnnotation<Transient>() }
|
||||
.filter { clazz.primaryConstructor?.parameters?.map { it.name }?.contains(it.name) ?: true }
|
||||
}
|
||||
|
||||
override fun serializableName(property: KProperty1<out Any, *>): String =
|
||||
property.annotations
|
||||
.filterIsInstance<SerialName>()
|
||||
.firstOrNull()?.value ?: property.name
|
||||
|
||||
override fun sealedObjectEnrichment(
|
||||
implementationType: KType,
|
||||
implementationSchema: JsonSchema,
|
||||
): JsonSchema {
|
||||
return if (implementationSchema is TypeDefinition && implementationSchema.type == "object") {
|
||||
implementationSchema.copy(
|
||||
required = implementationSchema.required?.plus("type"),
|
||||
properties = implementationSchema.properties?.plus(
|
||||
mapOf(
|
||||
"type" to EnumDefinition(enum = setOf(determineTypeQualifier(implementationType)))
|
||||
)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
implementationSchema
|
||||
}
|
||||
}
|
||||
|
||||
private fun determineTypeQualifier(type: KType): String {
|
||||
val nameOverrideAnnotation = (type.classifier as KClass<*>).findAnnotation<SerialName>()
|
||||
return nameOverrideAnnotation?.value ?: (type.classifier as KClass<*>).qualifiedName!!
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,16 @@
|
||||
package io.bkbn.kompendium.json.schema
|
||||
|
||||
import io.bkbn.kompendium.json.schema.definition.JsonSchema
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.KType
|
||||
|
||||
interface SchemaConfigurator {
|
||||
fun serializableMemberProperties(clazz: KClass<*>): Collection<KProperty1<out Any, *>>
|
||||
fun serializableName(property: KProperty1<out Any, *>): String
|
||||
|
||||
open class Default : SchemaConfigurator {
|
||||
override fun serializableMemberProperties(clazz: KClass<*>): Collection<KProperty1<out Any, *>> =
|
||||
clazz.memberProperties
|
||||
|
||||
override fun serializableName(property: KProperty1<out Any, *>): String = property.name
|
||||
}
|
||||
fun sealedObjectEnrichment(
|
||||
implementationType: KType,
|
||||
implementationSchema: JsonSchema
|
||||
): JsonSchema
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user