From 47305458737c056a738a33a7786fe50598b459d9 Mon Sep 17 00:00:00 2001 From: xeromank Date: Thu, 31 Aug 2023 15:05:12 +0900 Subject: [PATCH 1/5] feat: apply optional is nullable --- ...JsonSchemaFromFieldDescriptorsGenerator.kt | 19 +++++++++++++++++-- ...SchemaFromFieldDescriptorsGeneratorTest.kt | 14 +++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt index 569a6123..f116ef80 100644 --- a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt +++ b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt @@ -15,6 +15,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.everit.json.schema.ArraySchema import org.everit.json.schema.BooleanSchema import org.everit.json.schema.CombinedSchema +import org.everit.json.schema.CombinedSchema.oneOf import org.everit.json.schema.EmptySchema import org.everit.json.schema.EnumSchema import org.everit.json.schema.NullSchema @@ -207,8 +208,22 @@ class JsonSchemaFromFieldDescriptorsGenerator { fun jsonSchemaType(): Schema { val schemaBuilders = jsonSchemaPrimitiveTypes.map { typeToSchema(it) } - return if (schemaBuilders.size == 1) schemaBuilders.first().description(description).build() - else CombinedSchema.oneOf(schemaBuilders.map { it.build() }).description(description).build() + return if (schemaBuilders.size == 1) { + val builder = schemaBuilders.first().description(description) + checkNullable(builder) + builder.build() + } else { + val builder = oneOf(schemaBuilders.map { it.build() }).description(description) + checkNullable(builder) + builder.build() + } + } + + private fun checkNullable(builder: Schema.Builder): Schema.Builder { + if (optional) { + builder.nullable(true) + } + return builder } fun merge(fieldDescriptor: FieldDescriptor): FieldDescriptorWithSchemaType { diff --git a/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt b/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt index d7299227..8c1a6f36 100644 --- a/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt +++ b/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt @@ -19,6 +19,7 @@ import org.everit.json.schema.Schema import org.everit.json.schema.StringSchema import org.everit.json.schema.ValidationException import org.everit.json.schema.loader.SchemaLoader +import org.everit.json.schema.loader.internal.DefaultSchemaClient import org.json.JSONArray import org.json.JSONObject import org.junit.jupiter.api.Test @@ -61,6 +62,7 @@ class JsonSchemaFromFieldDescriptorsGeneratorTest { val shippingAddressSchema = objectSchema.propertySchemas["shippingAddress"]!! then(shippingAddressSchema).isInstanceOf(ObjectSchema::class.java) then(shippingAddressSchema.description).isNotEmpty() + then(shippingAddressSchema.isNullable).isTrue() then(objectSchema.definesProperty("billingAddress")).isTrue() val billingAddressSchema = objectSchema.propertySchemas["billingAddress"] as ObjectSchema @@ -120,6 +122,7 @@ class JsonSchemaFromFieldDescriptorsGeneratorTest { then(pagePositiveSchema.minimum.toInt()).isEqualTo(1) then(pagePositiveSchema.maximum).isNull() then(pagePositiveSchema.requiresInteger()).isTrue + then(pagePositiveSchema.isNullable).isTrue() then(objectSchema.definesProperty("page100_200")).isTrue then(objectSchema.propertySchemas["page100_200"]).isInstanceOf(NumberSchema::class.java) @@ -445,7 +448,16 @@ class JsonSchemaFromFieldDescriptorsGeneratorTest { private fun whenSchemaGenerated() { schemaString = generator.generateSchema(fieldDescriptors!!) println(schemaString) - schema = SchemaLoader.load(JSONObject(schemaString)) + schema = SchemaLoader + .builder() + .nullableSupport(true) + .schemaJson(JSONObject(schemaString)) + .schemaClient(DefaultSchemaClient()) + .build() + .load() + .build() +// schema = SchemaLoader.load(schemaString) +// schema = SchemaLoader.load(JSONObject(schemaString)) } private fun givenFieldDescriptorWithPrimitiveArray() { From a4183bc51ba1934840565f02188197d77a5bb4e8 Mon Sep 17 00:00:00 2001 From: xeromank Date: Thu, 31 Aug 2023 15:27:25 +0900 Subject: [PATCH 2/5] chore: refactoring --- .../JsonSchemaFromFieldDescriptorsGenerator.kt | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt index f116ef80..71893a79 100644 --- a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt +++ b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt @@ -208,22 +208,15 @@ class JsonSchemaFromFieldDescriptorsGenerator { fun jsonSchemaType(): Schema { val schemaBuilders = jsonSchemaPrimitiveTypes.map { typeToSchema(it) } - return if (schemaBuilders.size == 1) { - val builder = schemaBuilders.first().description(description) - checkNullable(builder) - builder.build() - } else { - val builder = oneOf(schemaBuilders.map { it.build() }).description(description) - checkNullable(builder) - builder.build() - } + return if (schemaBuilders.size == 1) schemaBuilders.first().description(description).checkNullable().build() + else oneOf(schemaBuilders.map { it.build() }).description(description).checkNullable().build() } - private fun checkNullable(builder: Schema.Builder): Schema.Builder { + private fun Schema.Builder.checkNullable(): Schema.Builder { if (optional) { - builder.nullable(true) + this.nullable(true) } - return builder + return this } fun merge(fieldDescriptor: FieldDescriptor): FieldDescriptorWithSchemaType { From 8011ae4c40630aa051dfb3044dca70d25d646e3c Mon Sep 17 00:00:00 2001 From: xeromank Date: Thu, 31 Aug 2023 15:29:33 +0900 Subject: [PATCH 3/5] chore: remove needless --- .../jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt b/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt index 8c1a6f36..3b15d96b 100644 --- a/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt +++ b/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt @@ -456,8 +456,6 @@ class JsonSchemaFromFieldDescriptorsGeneratorTest { .build() .load() .build() -// schema = SchemaLoader.load(schemaString) -// schema = SchemaLoader.load(JSONObject(schemaString)) } private fun givenFieldDescriptorWithPrimitiveArray() { From 59767f688ae061364f6fd53dbe8cb610dd3a0828 Mon Sep 17 00:00:00 2001 From: xeromank Date: Thu, 31 Aug 2023 16:37:34 +0900 Subject: [PATCH 4/5] fix deprecated --- .../jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt index 71893a79..de373962 100644 --- a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt +++ b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt @@ -256,7 +256,7 @@ class JsonSchemaFromFieldDescriptorsGenerator { private fun arrayItemsSchema(): Schema { return attributes.itemsType - ?.let { typeToSchema(it.toLowerCase()).build() } + ?.let { typeToSchema(it.lowercase()).build() } ?: CombinedSchema.oneOf( listOf( ObjectSchema.builder().build(), @@ -285,7 +285,7 @@ class JsonSchemaFromFieldDescriptorsGenerator { ) private fun jsonSchemaPrimitiveTypeFromDescriptorType(fieldDescriptorType: String) = - fieldDescriptorType.toLowerCase() + fieldDescriptorType.lowercase() .let { if (it == "varies") "empty" else it } // varies is used by spring rest docs if the type is ambiguous - in json schema we want to represent as empty } } From ce7215fa8516b0ad8b4b281e54adabde8165d2fb Mon Sep 17 00:00:00 2001 From: xeromank Date: Thu, 31 Aug 2023 16:39:16 +0900 Subject: [PATCH 5/5] fix for test --- ...JsonSchemaFromFieldDescriptorsGenerator.kt | 21 +++++++++++++++++-- ...SchemaFromFieldDescriptorsGeneratorTest.kt | 6 +++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt index de373962..d9b11154 100644 --- a/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt +++ b/restdocs-api-spec-jsonschema/src/main/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGenerator.kt @@ -207,7 +207,17 @@ class JsonSchemaFromFieldDescriptorsGenerator { ) : FieldDescriptor(path, description, type, optional, ignored, attributes) { fun jsonSchemaType(): Schema { - val schemaBuilders = jsonSchemaPrimitiveTypes.map { typeToSchema(it) } + val schemaBuilders: List> + if (jsonSchemaPrimitiveTypes.size > 1 && + optional && + !jsonSchemaPrimitiveTypes.contains("null") + ) { + schemaBuilders = jsonSchemaPrimitiveTypes + .plus(jsonSchemaPrimitiveTypeFromDescriptorType("null")) + .map { typeToSchema(it) } + } else { + schemaBuilders = jsonSchemaPrimitiveTypes.map { typeToSchema(it) } + } return if (schemaBuilders.size == 1) schemaBuilders.first().description(description).checkNullable().build() else oneOf(schemaBuilders.map { it.build() }).description(description).checkNullable().build() } @@ -238,7 +248,9 @@ class JsonSchemaFromFieldDescriptorsGenerator { private fun typeToSchema(type: String): Schema.Builder<*> = when (type) { - "null" -> NullSchema.builder() + "null" -> { + NullSchema.builder().nullable() + } "empty" -> EmptySchema.builder() "object" -> ObjectSchema.builder() "array" -> ArraySchema.builder().applyConstraints(this).allItemSchema(arrayItemsSchema()) @@ -254,6 +266,11 @@ class JsonSchemaFromFieldDescriptorsGenerator { else -> throw IllegalArgumentException("unknown field type $type") } + private fun NullSchema.Builder.nullable(): NullSchema.Builder { + this.nullable(true) + return this + } + private fun arrayItemsSchema(): Schema { return attributes.itemsType ?.let { typeToSchema(it.lowercase()).build() } diff --git a/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt b/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt index 3b15d96b..dddf15e7 100644 --- a/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt +++ b/restdocs-api-spec-jsonschema/src/test/kotlin/com/epages/restdocs/apispec/jsonschema/JsonSchemaFromFieldDescriptorsGeneratorTest.kt @@ -599,9 +599,9 @@ class JsonSchemaFromFieldDescriptorsGeneratorTest { private fun givenDifferentFieldDescriptorsWithSamePathAndDifferentTypes() { fieldDescriptors = listOf( - FieldDescriptor("id", "some", "STRING"), - FieldDescriptor("id", "some", "NULL"), - FieldDescriptor("id", "some", "BOOLEAN") + FieldDescriptor("id", "some", "STRING", true), + FieldDescriptor("id", "some", "NULL", true), + FieldDescriptor("id", "some", "BOOLEAN", true) ) }