From 335492a38d0190f04cdc3211b877b1436534b134 Mon Sep 17 00:00:00 2001 From: Jules Ivanic Date: Mon, 26 Aug 2024 21:00:53 +1000 Subject: [PATCH] Review: Remove optional fields from the `required` ones --- .../endpoint/openapi/OpenAPIGenSpec.scala | 6 +--- .../http/endpoint/openapi/JsonSchema.scala | 32 ++++++++++++------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/OpenAPIGenSpec.scala b/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/OpenAPIGenSpec.scala index efe02f838f..b7f2f46640 100644 --- a/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/OpenAPIGenSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/endpoint/openapi/OpenAPIGenSpec.scala @@ -2839,7 +2839,6 @@ object OpenAPIGenSpec extends ZIOSpecDefault { | } | }, | "required" : [ - | "nestedOption", | "nestedList", | "nestedMap", | "nestedSet", @@ -3067,10 +3066,7 @@ object OpenAPIGenSpec extends ZIOSpecDefault { | { "$ref": "#/components/schemas/SealedTraitCustomDiscriminator" } | ] | } - | }, - | "required" : [ - | "optionalAdtField" - | ] + | } | }, | "Two" : | { diff --git a/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/JsonSchema.scala b/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/JsonSchema.scala index ce0204de24..2382c698a2 100644 --- a/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/JsonSchema.scala +++ b/zio-http/shared/src/main/scala/zio/http/endpoint/openapi/JsonSchema.scala @@ -1,10 +1,9 @@ package zio.http.endpoint.openapi -import scala.annotation.{nowarn, tailrec} - import zio._ +import zio.http.codec.{SegmentCodec, TextCodec} +import zio.http.endpoint.openapi.JsonSchema.MetaData import zio.json.ast.Json - import zio.schema.Schema.CaseClass0 import zio.schema._ import zio.schema.annotation._ @@ -12,7 +11,7 @@ import zio.schema.codec._ import zio.schema.codec.json._ import zio.schema.validation._ -import zio.http.codec.{SegmentCodec, TextCodec} +import scala.annotation.{nowarn, tailrec} @nowarn("msg=possible missing interpolator") private[openapi] case class SerializableJsonSchema( @@ -161,6 +160,12 @@ sealed trait JsonSchema extends Product with Serializable { self => case _ => Chunk.empty } + final def isNullable: Boolean = + annotations.exists { + case MetaData.Nullable(nullable) => nullable + case _ => false + } + def withoutAnnotations: JsonSchema = self match { case JsonSchema.AnnotatedSchema(schema, _) => schema.withoutAnnotations case _ => self @@ -874,9 +879,7 @@ object JsonSchema { obj <- objects otherObj <- objects notNullableSchemas = obj.withoutAnnotations.asInstanceOf[JsonSchema.Object].properties.collect { - case (name, schema) - if !schema.annotations.exists { case MetaData.Nullable(nullable) => nullable; case _ => false } => - name -> schema + case (name, schema) if !schema.isNullable => name -> schema } if notNullableSchemas == otherObj.withoutAnnotations.asInstanceOf[JsonSchema.Object].properties } yield otherObj).distinct @@ -885,9 +888,7 @@ object JsonSchema { val annotations = obj.annotations val asObject = obj.withoutAnnotations.asInstanceOf[JsonSchema.Object] val notNullableSchemas = asObject.properties.collect { - case (name, schema) - if !schema.annotations.exists { case MetaData.Nullable(nullable) => nullable; case _ => false } => - name -> schema + case (name, schema) if !schema.isNullable => name -> schema } asObject.required(asObject.required.filter(notNullableSchemas.contains)).annotate(annotations) } @@ -1278,11 +1279,20 @@ object JsonSchema { case Left(false) => Some(BoolOrSchema.BooleanWrapper(false)) case Right(schema) => Some(BoolOrSchema.SchemaWrapper(schema.toSerializableSchema)) } + + val nullableFields = properties.collect { case (name, schema) if schema.isNullable => name }.toSet + SerializableJsonSchema( schemaType = Some(TypeOrTypes.Type("object")), properties = Some(properties.map { case (name, schema) => name -> schema.toSerializableSchema }), additionalProperties = additionalProperties, - required = if (required.isEmpty) None else Some(required), + required = + if (required.isEmpty) None + else if (nullableFields.isEmpty) Some(required) + else { + val newRequired = required.filterNot(nullableFields.contains) + if (newRequired.isEmpty) None else Some(newRequired) + }, ) } }