Skip to content

Commit

Permalink
Fix OpenAPI security schema (#2813)
Browse files Browse the repository at this point in the history
  • Loading branch information
987Nabil committed Jun 9, 2024
1 parent f070e41 commit 111482f
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 22 deletions.
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ object Dependencies {
val ZioVersion = "2.1.1"
val ZioCliVersion = "0.5.0"
val ZioJsonVersion = "0.6.2"
val ZioSchemaVersion = "1.2.0"
val ZioSchemaVersion = "1.2.1"
val SttpVersion = "3.3.18"
val ZioConfigVersion = "4.0.2"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package zio.http

import java.lang.System
import java.nio.file.{Files, Path => NIOPath}

import zio._
Expand All @@ -32,7 +31,7 @@ object StaticFileRoutesSpec extends HttpRunnableSpec {
private def deleteTempFile(tempPath: NIOPath) = ZIO.attempt(Files.deleteIfExists(tempPath)).ignore
private val createAndDeleteTempFile = createTempFile.flatMap(f => deleteTempFile(f).as(f))

override def spec = suite("StaticFileServerSpec") {
override def spec = suite("StaticFileRoutesSpec") {
serve.as(List(staticSpec))
}
.provideSome[DynamicServer & Server & Client](Scope.default)
Expand Down Expand Up @@ -78,7 +77,9 @@ object StaticFileRoutesSpec extends HttpRunnableSpec {
assert(response.status)(equalTo(Status.Ok)) &&
assert(response.header(Header.ContentLength))(isSome(equalTo(Header.ContentLength(7L)))) &&
assert(body)(equalTo("foo\nbar")) &&
assert(response.header(Header.ContentType))(isSome(equalTo(Header.ContentType(MediaType.text.plain))))
assert(response.header(Header.ContentType))(
isSome(equalTo(Header.ContentType(MediaType.text.plain, charset = Some(Charsets.Utf8)))),
)
}
},
test("serve a non-existing resource") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import zio.Chunk
import zio.json.ast._

import zio.schema._
import zio.schema.annotation.{fieldName, noDiscriminator}
import zio.schema.annotation.{caseName, discriminatorName, fieldName, noDiscriminator}
import zio.schema.codec.JsonCodec
import zio.schema.codec.json._

Expand Down Expand Up @@ -1271,8 +1271,8 @@ object OpenAPI {
*/
final case class XML(name: String, namespace: URI, prefix: String, attribute: Boolean = false, wrapped: Boolean)

@discriminatorName("type")
sealed trait SecurityScheme {
def `type`: String
def description: Option[Doc]
}

Expand All @@ -1291,9 +1291,8 @@ object OpenAPI {
* @param in
* The location of the API key.
*/
final case class ApiKey(description: Option[Doc], name: String, in: ApiKey.In) extends SecurityScheme {
override def `type`: String = "apiKey"
}
@caseName("apiKey")
final case class ApiKey(description: Option[Doc], name: String, in: ApiKey.In) extends SecurityScheme

object ApiKey {
sealed trait In extends Product with Serializable
Expand All @@ -1319,11 +1318,8 @@ object OpenAPI {
* Bearer tokens are usually generated by an authorization server, so this
* information is primarily for documentation purposes.
*/
final case class Http(description: Option[Doc], scheme: String, bearerFormat: Option[String])
extends SecurityScheme {
override def `type`: String = "http"

}
@caseName("http")
final case class Http(description: Option[Doc], scheme: String, bearerFormat: Option[String]) extends SecurityScheme

/**
* @param description
Expand All @@ -1332,21 +1328,17 @@ object OpenAPI {
* An object containing configuration information for the flow types
* supported.
*/
final case class OAuth2(description: Option[Doc], flows: OAuthFlows) extends SecurityScheme {
override def `type`: String = "oauth2"

}
@caseName("oauth2")
final case class OAuth2(description: Option[Doc], flows: OAuthFlows) extends SecurityScheme

/**
* @param description
* A short description for security scheme.
* @param openIdConnectUrl
* OpenId Connect URL to discover OAuth2 configuration values.
*/
final case class OpenIdConnect(description: Option[Doc], openIdConnectUrl: URI) extends SecurityScheme {
override def `type`: String = "openIdConnect"

}
@caseName("openIdConnect")
final case class OpenIdConnect(description: Option[Doc], openIdConnectUrl: URI) extends SecurityScheme

/**
* Allows configuration of the supported OAuth Flows.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package zio.http.endpoint.openapi

import scala.collection.immutable.ListMap

import zio.json.ast.Json
import zio.test._

import zio.http.endpoint.openapi.OpenAPI.SecurityScheme._

object OpenAPISpec extends ZIOSpecDefault {

def toJsonAst(str: String): Json =
Json.decoder.decodeJson(str).toOption.get

def toJsonAst(api: OpenAPI): Json =
toJsonAst(api.toJson)

val spec = suite("OpenAPISpec")(
test("auth schema serialization") {
import OpenAPI._
val securitySchemes: ListMap[Key, ReferenceOr[SecurityScheme]] = ListMap(
Key.fromString("apiKeyAuth").get -> ReferenceOr.Or(
SecurityScheme.ApiKey(
None,
"Authorization",
ApiKey.In.Header,
),
),
)

val openApi = OpenAPI.empty.copy(
security = List(SecurityRequirement(Map("apiKeyAuth" -> List.empty))),
components = Some(OpenAPI.Components(securitySchemes = securitySchemes)),
)
val json = openApi.toJsonPretty
val expected = """{
| "openapi" : "3.1.0",
| "info" : {
| "title" : "",
| "version" : ""
| },
| "components" : {
| "securitySchemes" : {
| "apiKeyAuth" :
| {
| "type" : "apiKey",
| "name" : "Authorization",
| "in" : "Header"
| }
| }
| },
| "security" : [
| {
| "securitySchemes" : {
| "apiKeyAuth" : []
| }
| }
| ]
|}""".stripMargin

assertTrue(toJsonAst(json) == toJsonAst(expected))
},
)
}

0 comments on commit 111482f

Please sign in to comment.