Skip to content

Commit

Permalink
add support for specifying @securityrequirement for routes
Browse files Browse the repository at this point in the history
  • Loading branch information
angryziber committed Aug 23, 2023
1 parent e15299f commit 422c594
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 8 deletions.
11 changes: 8 additions & 3 deletions openapi/src/OpenAPI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode
import io.swagger.v3.oas.annotations.parameters.RequestBody
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.security.SecurityRequirements
import io.swagger.v3.oas.annotations.security.SecurityScheme
import io.swagger.v3.oas.annotations.security.SecuritySchemes
import io.swagger.v3.oas.annotations.tags.Tag
Expand Down Expand Up @@ -55,7 +57,7 @@ internal fun Router.generateOpenAPI() = mapOf(
"servers" to listOf(mapOf("url" to fullUrl(prefix))),
"tags" to toTags(routes),
"components" to mapOfNotNull(
"securitySchemes" to (route.annotations.filterIsInstance<SecurityScheme>() + (route.annotation<SecuritySchemes>()?.value ?: emptyArray())).associate { s ->
"securitySchemes" to route.repeatableAnnotations<SecurityScheme, SecuritySchemes>().associate { s ->
s.name to s.toNonEmptyValues { it.name != "paramName" }.let { it + ("name" to s.paramName) }
}.takeIf { it.isNotEmpty() }
).takeIf { it.isNotEmpty() },
Expand All @@ -66,6 +68,9 @@ internal fun Router.generateOpenAPI() = mapOf(
it.toNonEmptyValues() + ("security" to it.security.associate { it.name to it.scopes.toList() })
} ?: emptyMap())

private inline fun <reified A: Annotation, reified R: Annotation> Route.repeatableAnnotations() =
annotations.filterIsInstance<A>() + (annotation<R>()?.let { it.publicProperties.first().valueOf(it) as Array<A> } ?: emptyArray())

internal fun toTags(routes: List<Route>) = routes.asSequence()
.map { it.handler }
.filterIsInstance<FunHandler>()
Expand All @@ -83,7 +88,7 @@ internal fun toOperation(route: Route): Pair<String, Any> {
},
"requestBody" to toRequestBody(route, route.annotation<RequestBody>() ?: op?.requestBody),
"responses" to toResponsesByCode(route, op, funHandler?.f?.returnType),
"security" to op?.security?.associate { it.name to it.scopes.toList() }
"security" to route.repeatableAnnotations<SecurityRequirement, SecurityRequirements>().associate { it.name to it.scopes.toList() }
) + (op?.let { it.toNonEmptyValues { it.name !in setOf("method", "requestBody", "responses") } } ?: emptyMap())
}

Expand Down Expand Up @@ -153,7 +158,7 @@ private fun toResponsesByCode(route: Route, op: Operation?, returnType: KType?):
val responses = LinkedHashMap<StatusCode, Any?>()
if (returnType?.classifier == Unit::class) responses[NoContent] = mapOf("description" to "No content")
else if (op?.responses?.isEmpty() != false) responses[OK] = mapOfNotNull("description" to "OK", "content" to returnType?.toJsonContent(response = true))
(route.annotations.filterIsInstance<ApiResponse>() + (route.annotation<ApiResponses>()?.value ?: emptyArray()) + (op?.responses ?: emptyArray())).forEach {
(route.repeatableAnnotations<ApiResponse, ApiResponses>() + (op?.responses ?: emptyArray())).forEach {
responses[StatusCode(it.responseCode.toInt())] = it.toNonEmptyValues { it.name != "responseCode" }
}
return responses
Expand Down
16 changes: 11 additions & 5 deletions openapi/test/OpenAPITest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ class OpenAPITest {
mapOf("name" to "force", "required" to false, "in" to QUERY, "schema" to mapOf("type" to "boolean"))
),
"requestBody" to null,
"responses" to mapOf(OK to mapOf("description" to "OK", "content" to mapOf(MimeTypes.json to mapOf("schema" to mapOf("type" to "null")))))
"responses" to mapOf(OK to mapOf("description" to "OK", "content" to mapOf(MimeTypes.json to mapOf("schema" to mapOf("type" to "null"))))),
"security" to emptyMap<String, Any>()
))
}

Expand All @@ -103,7 +104,8 @@ class OpenAPITest {
mapOf("name" to "userId", "required" to true, "in" to PATH, "schema" to mapOf("type" to "string", "format" to "uuid"))
),
"requestBody" to mapOf("content" to userSchema(), "required" to true),
"responses" to mapOf(NoContent to mapOf("description" to "No content"))
"responses" to mapOf(NoContent to mapOf("description" to "No content")),
"security" to emptyMap<String, Any>()
))
}

Expand All @@ -112,6 +114,7 @@ class OpenAPITest {
@RequestBody(description = "Application and applicant", content = [Content(mediaType = MimeTypes.json, schema = Schema(implementation = User::class))])
@ApiResponse(responseCode = "400", description = "Very bad request")
@ApiResponse(responseCode = "401", description = "Unauthorized")
@SecurityRequirement(name = "MySecurity")
fun saveUser(e: HttpExchange): User = User("x", UUID.randomUUID())
}
expect(toOperation(Route(POST, "/x".toRegex(), handler = FunHandler(MyRoutes(), MyRoutes::saveUser), annotations = MyRoutes::saveUser.annotations))).toEqual("post" to mapOf(
Expand All @@ -123,7 +126,8 @@ class OpenAPITest {
OK to mapOf("description" to "OK", "content" to userSchema(response = true)),
BadRequest to mapOf("description" to "Very bad request"),
Unauthorized to mapOf("description" to "Unauthorized"),
)
),
"security" to mapOf("MySecurity" to emptyList<String>())
))
}

Expand All @@ -133,7 +137,8 @@ class OpenAPITest {
"tags" to emptyList<Any>(),
"parameters" to null,
"requestBody" to null,
"responses" to mapOf(OK to mapOf("description" to "OK"))
"responses" to mapOf(OK to mapOf("description" to "OK")),
"security" to emptyMap<String, Any>()
))
}

Expand All @@ -151,7 +156,8 @@ class OpenAPITest {
"parameters" to listOf(mapOf("name" to "param", "in" to QUERY, "description" to "description")),
"requestBody" to null,
"responses" to mapOf(Found to mapOf("description" to "desc")),
"summary" to "summary"
"summary" to "summary",
"security" to emptyMap<String, Any>()
))
}

Expand Down

0 comments on commit 422c594

Please sign in to comment.