Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make schema nullable instead of adding null-types into the apispec schema #3331

Merged
merged 1 commit into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ private[schema] class TSchemaToASchema(toSchemaReference: ToSchemaReference, mar
// the initial list of schemas.
val propagated = propagateMetadataForOption(schema, opt).element
val ref = toSchemaReference.map(propagated, name)
if (!markOptionsAsNullable) ref else ASchema.oneOf(List(ref, ASchema(SchemaType.Null)), None)
if (!markOptionsAsNullable) ref else ref.copy(nullable = Some(true))
case TSchemaType.SOption(el) => apply(el, isOptionElement = true)
case TSchemaType.SBinary() => ASchema(SchemaType.String).copy(format = SchemaFormat.Binary)
case TSchemaType.SDate() => ASchema(SchemaType.String).copy(format = SchemaFormat.Date)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
openapi: 3.0.3
info:
title: ClassWithOptionClassField
version: '1.0'
paths:
/:
post:
operationId: postRoot
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ClassWithOptionClassField'
required: true
responses:
'200':
description: ''
content:
text/plain:
schema:
type: string
'400':
description: 'Invalid value for: body'
content:
text/plain:
schema:
type: string
components:
schemas:
Bar:
required:
- bar
type: object
properties:
bar:
type: integer
format: int32
ClassWithOptionClassField:
required:
- requiredStringField
type: object
properties:
optionalObjField:
allOf:
- $ref: '#/components/schemas/Bar'
nullable: true
requiredStringField:
type: string
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
openapi: 3.0.3
info:
title: ClassWithOptionField
version: '1.0'
paths:
/:
get:
operationId: getRoot
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ClassWithOptionField'
required: true
responses:
'200':
description: ''
content:
text/plain:
schema:
type: string
'400':
description: 'Invalid value for: body'
content:
text/plain:
schema:
type: string
components:
schemas:
ClassWithOptionField:
required:
- requiredStringField
type: object
properties:
optionalIntField:
type: integer
format: int32
nullable: true
requiredStringField:
type: string
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,19 @@ class VerifyYamlTest extends AnyFunSuite with Matchers {
actualYamlNoIndent shouldBe expectedYaml
}

test("should mark optional fields as nullable when configured to do so using OpenAPI 3.0") {
case class ClassWithOptionField(optionalIntField: Option[Int], requiredStringField: String)

val e = endpoint.in(jsonBody[ClassWithOptionField]).out(stringBody)
val expectedYaml = load("expected_nullable_option_field_303.yml")

val options = OpenAPIDocsOptions.default.copy(markOptionsAsNullable = true)

val actualYaml = OpenAPIDocsInterpreter(options).toOpenAPI(e, Info("ClassWithOptionField", "1.0")).copy(openapi = "3.0.3").toYaml3_0_3
val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}

test("should mark optional class fields as nullable when configured to do so") {
case class Bar(bar: Int)
case class ClassWithOptionClassField(optionalObjField: Option[Bar], requiredStringField: String)
Expand All @@ -690,6 +703,21 @@ class VerifyYamlTest extends AnyFunSuite with Matchers {
actualYamlNoIndent shouldBe expectedYaml
}

test("should mark optional class fields as nullable when configured to do so using OpenAPI 3.0") {
case class Bar(bar: Int)
case class ClassWithOptionClassField(optionalObjField: Option[Bar], requiredStringField: String)

val e = endpoint.in(jsonBody[ClassWithOptionClassField]).out(stringBody).post
val expectedYaml = load("expected_nullable_option_class_field_303.yml")

val options = OpenAPIDocsOptions.default.copy(markOptionsAsNullable = true)

val actualYaml =
OpenAPIDocsInterpreter(options).toOpenAPI(e, Info("ClassWithOptionClassField", "1.0")).copy(openapi = "3.0.3").toYaml3_0_3
val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}

test("should generate default and example values for nested optional fields") {
case class Nested(nestedValue: String)
case class ClassWithNestedOptionalField(
Expand Down
2 changes: 1 addition & 1 deletion project/Versions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object Versions {
val sttp = "3.9.1"
val sttpModel = "1.7.6"
val sttpShared = "1.3.16"
val sttpApispec = "0.7.1"
val sttpApispec = "0.7.2"
val akkaHttp = "10.2.10"
val akkaStreams = "2.6.20"
val pekkoHttp = "1.0.0"
Expand Down
Loading