From 8f14833609684f0c8b02c6c1b6169222679dc3af Mon Sep 17 00:00:00 2001 From: Stefan Meissner Date: Mon, 10 Aug 2026 18:41:07 +0200 Subject: [PATCH 1/5] Handle defualt value for arrays. --- .../ramldoc/extensions/VrapExtensions.kt | 2 ++ .../languages/ramldoc/TestCodeGenerator.kt | 23 +++++++++++++++++++ .../src/test/resources/arraydefault.raml | 18 +++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 languages/ramldoc/src/test/resources/arraydefault.raml diff --git a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt index e2156bd3..0d217d2e 100644 --- a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt +++ b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt @@ -373,6 +373,8 @@ fun Instance.toYaml(): String { example = mapper.writeValueAsString(this) } catch (e: JsonProcessingException) { } + } else if (this is ArrayInstance && this.value.size == 1) { + example = this.value[0].toYaml() } else { example = mapper.writeValueAsString(this.value) } diff --git a/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt b/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt index 8aa1ba6b..4e6adde6 100644 --- a/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt +++ b/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt @@ -332,6 +332,29 @@ class TestCodeGenerator { .doesNotContain("value: |") } + @Test + fun testArrayDefaultWithSingleElementRendersAsScalar() { + val generatorConfig = CodeGeneratorConfig( + basePackageName = "com/commercetools/importer", + outputFolder = Paths.get("build/gensrc"), + inlineExamples = true + ) + + val apiProvider = RamlApiProvider(Paths.get("src/test/resources/arraydefault.raml")) + + val dataSink = MemoryDataSink() + val generatorModule = RamlGeneratorModule(apiProvider, generatorConfig, RamldocBaseTypes, dataSink = dataSink) + val generatorComponent = RamlGeneratorComponent(generatorModule, RamldocModelModule) + generatorComponent.generateFiles() + + Assertions.assertThat(dataSink.files).isNotEmpty() + val typeContent = dataSink.files.get("types/foo.raml") + Assertions.assertThat(typeContent).isNotNull() + Assertions.assertThat(typeContent) + .contains("default: \"InventorySupply\"") + .doesNotContain("default: - \"InventorySupply\"") + } + @Test fun ramlRenderToRamlDoc() { val generatorConfig = CodeGeneratorConfig( diff --git a/languages/ramldoc/src/test/resources/arraydefault.raml b/languages/ramldoc/src/test/resources/arraydefault.raml new file mode 100644 index 00000000..55062a37 --- /dev/null +++ b/languages/ramldoc/src/test/resources/arraydefault.raml @@ -0,0 +1,18 @@ +#%RAML 1.0 +--- +title: Array Default Example Test API +version: 1.0 +baseUri: http://example.com/api +types: + ChannelRoleEnum: + type: string + enum: + - InventorySupply + - OrderExport + - OrderImport + foo: + properties: + roles: + type: ChannelRoleEnum[] + default: [ InventorySupply ] + From 1bc66d516d90746e70824eefab4c73bc99846a08 Mon Sep 17 00:00:00 2001 From: Stefan Meissner Date: Tue, 11 Aug 2026 16:31:31 +0200 Subject: [PATCH 2/5] Only the default: splice sites now opt in via toYaml(true); all othercallers (e.g. renderAnnotation) keep the original sequence-preserving behavior. Also fixes a compile error in OasResourceRenderer.renderQueryParameter: queryParameter. Adds regression tests: array default renders as scalar (testArrayDefaultWithSingleElementRendersAsScalar) and array annotation still renders as sequence (testArrayAnnotationWithSingleElementRendersAsSequence). --- .../ramldoc/extensions/VrapExtensions.kt | 8 ++++-- .../ramldoc/model/OasTypeRenderer.kt | 2 +- .../ramldoc/model/RamlObjectTypeRenderer.kt | 2 +- .../ramldoc/model/RamlResourceRenderer.kt | 2 +- .../languages/ramldoc/TestCodeGenerator.kt | 25 +++++++++++++++++++ .../src/test/resources/arrayannotation.raml | 8 ++++++ 6 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 languages/ramldoc/src/test/resources/arrayannotation.raml diff --git a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt index 0d217d2e..7d46ec95 100644 --- a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt +++ b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt @@ -352,7 +352,7 @@ class ObjectInstanceSerializer : JsonSerializer() { } } -fun Instance.toYaml(): String { +fun Instance.toYaml(unwrapSingleItemArray: Boolean = false): String { var example = "" val mapper = YAMLMapper() mapper.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) @@ -373,7 +373,11 @@ fun Instance.toYaml(): String { example = mapper.writeValueAsString(this) } catch (e: JsonProcessingException) { } - } else if (this is ArrayInstance && this.value.size == 1) { + } else if (unwrapSingleItemArray && this is ArrayInstance && this.value.size == 1) { + // Only used for single-line `default:` rendering, where a block-sequence item + // cannot be spliced onto the same line as the key. Sequence-valued renderings + // (e.g. annotations) must keep calling toYaml() without this flag so a + // single-item array still renders as a YAML sequence. example = this.value[0].toYaml() } else { example = mapper.writeValueAsString(this.value) diff --git a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/OasTypeRenderer.kt b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/OasTypeRenderer.kt index edb778a4..46b7a34a 100644 --- a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/OasTypeRenderer.kt +++ b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/OasTypeRenderer.kt @@ -248,7 +248,7 @@ sealed class OasTypeRenderer> constructor(open val api: OpenAPI, | <<${property.type.enum.joinToString("\n") { "- ${it.value}" }}>>""" else ""}${if (examples.isNotEmpty()) """ | examples: | <<${examples.joinToString("\n") { renderExample(it) }}>>""" else ""}${if (discriminatorProp != property.name && property.type.default != null) """ - | default: ${property.type.default.toYaml()}""" else ""}${if (property.type?.isInlineType == true && property.type?.annotations != null) """ + | default: ${property.type.default.toYaml(true)}""" else ""}${if (property.type?.isInlineType == true && property.type?.annotations != null) """ | <<${property.type.annotations.joinToString("\n") { it.renderAnnotation() }}>>""" else ""} | required: ${property.required} | (inherited): $inherited diff --git a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlObjectTypeRenderer.kt b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlObjectTypeRenderer.kt index 0c90604c..6b7b7425 100644 --- a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlObjectTypeRenderer.kt +++ b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlObjectTypeRenderer.kt @@ -108,7 +108,7 @@ class RamlObjectTypeRenderer constructor(override val vrapTypeProvider: VrapType | <<${property.type.enum.joinToString("\n") { "- '${it.value}'" }}>>""" else ""}${if (examples.isNotEmpty()) """ | examples: | <<${examples.joinToString("\n") { renderExample(it) }}>>""" else ""}${if (discriminatorProp != property.name && property.type.default != null) """ - | default: ${property.type.default.toYaml()}""" else ""}${if (property.type?.isInlineType == true && property.type?.annotations != null) """ + | default: ${property.type.default.toYaml(true)}""" else ""}${if (property.type?.isInlineType == true && property.type?.annotations != null) """ | <<${property.type.annotations.joinToString("\n") { it.renderAnnotation() }}>>""" else ""} | required: ${property.required} | (inherited): $inherited diff --git a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlResourceRenderer.kt b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlResourceRenderer.kt index 47e2a27e..71414c0e 100644 --- a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlResourceRenderer.kt +++ b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlResourceRenderer.kt @@ -182,7 +182,7 @@ class RamlResourceRenderer constructor(val api: Api, val vrapTypeProvider: VrapT } return """ |${parameterName}:${if (queryParameter.type.default != null) """ - | default: ${queryParameter.type.default.toYaml()}""" else ""} + | default: ${queryParameter.type.default.toYaml(true)}""" else ""} | required: ${queryParameter.required} | <<${queryParameter.type.renderType()}>>${if (parameterExamples.isNotEmpty()) """ | examples: diff --git a/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt b/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt index 4e6adde6..a655894a 100644 --- a/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt +++ b/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt @@ -355,6 +355,31 @@ class TestCodeGenerator { .doesNotContain("default: - \"InventorySupply\"") } + @Test + fun testArrayAnnotationWithSingleElementRendersAsSequence() { + val generatorConfig = CodeGeneratorConfig( + basePackageName = "com/commercetools/importer", + outputFolder = Paths.get("build/gensrc") + ) + + val apiProvider = RamlApiProvider(Paths.get("src/test/resources/arrayannotation.raml")) + + val generatorModule = RamlGeneratorModule(apiProvider, generatorConfig, RamldocBaseTypes) + val generatorComponent = RamlGeneratorComponent(generatorModule, RamldocModelModule) + generatorComponent.generateFiles() + + val api = apiProvider.api + val t = api.getAnnotation("test").renderAnnotation() + + // Guards against regressing renderAnnotation() (ArrayAnnotationType) into rendering a + // single-item array as a bare scalar - it must stay a YAML sequence, unlike the + // `default:` single-line rendering which intentionally unwraps single-item arrays. + Assertions.assertThat(t).isEqualTo(""" + (test): + - "foo" + """.trimIndent().trimStart()) + } + @Test fun ramlRenderToRamlDoc() { val generatorConfig = CodeGeneratorConfig( diff --git a/languages/ramldoc/src/test/resources/arrayannotation.raml b/languages/ramldoc/src/test/resources/arrayannotation.raml new file mode 100644 index 00000000..5a85ffc3 --- /dev/null +++ b/languages/ramldoc/src/test/resources/arrayannotation.raml @@ -0,0 +1,8 @@ +#%RAML 1.0 +--- +title: Array Annotation Example Test API +annotationTypes: + test: + type: string[] +baseUri: https://localhost +(test): [ foo ] From e95791636e2d320f58a84d758cb6fdb6fddec470 Mon Sep 17 00:00:00 2001 From: Stefan Meissner Date: Tue, 11 Aug 2026 16:42:54 +0200 Subject: [PATCH 3/5] Reverting change to unrelated code. --- .../io/vrap/codegen/languages/ramldoc/model/OasTypeRenderer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/OasTypeRenderer.kt b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/OasTypeRenderer.kt index 46b7a34a..edb778a4 100644 --- a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/OasTypeRenderer.kt +++ b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/OasTypeRenderer.kt @@ -248,7 +248,7 @@ sealed class OasTypeRenderer> constructor(open val api: OpenAPI, | <<${property.type.enum.joinToString("\n") { "- ${it.value}" }}>>""" else ""}${if (examples.isNotEmpty()) """ | examples: | <<${examples.joinToString("\n") { renderExample(it) }}>>""" else ""}${if (discriminatorProp != property.name && property.type.default != null) """ - | default: ${property.type.default.toYaml(true)}""" else ""}${if (property.type?.isInlineType == true && property.type?.annotations != null) """ + | default: ${property.type.default.toYaml()}""" else ""}${if (property.type?.isInlineType == true && property.type?.annotations != null) """ | <<${property.type.annotations.joinToString("\n") { it.renderAnnotation() }}>>""" else ""} | required: ${property.required} | (inherited): $inherited From 7a26b9c1da0f59bd34e25f37e1ddcfa3c32770b2 Mon Sep 17 00:00:00 2001 From: Jens Schulze Date: Thu, 13 Aug 2026 11:08:47 +0200 Subject: [PATCH 4/5] refactor array and object renderer for default values --- .../ramldoc/extensions/VrapExtensions.kt | 8 +--- .../ramldoc/model/RamlObjectTypeRenderer.kt | 3 +- .../ramldoc/model/RamlResourceRenderer.kt | 3 +- .../languages/ramldoc/TestCodeGenerator.kt | 42 +++++++++++++++++-- .../src/test/resources/arraydefault.raml | 23 +++++----- 5 files changed, 56 insertions(+), 23 deletions(-) diff --git a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt index 7d46ec95..e2156bd3 100644 --- a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt +++ b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/extensions/VrapExtensions.kt @@ -352,7 +352,7 @@ class ObjectInstanceSerializer : JsonSerializer() { } } -fun Instance.toYaml(unwrapSingleItemArray: Boolean = false): String { +fun Instance.toYaml(): String { var example = "" val mapper = YAMLMapper() mapper.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) @@ -373,12 +373,6 @@ fun Instance.toYaml(unwrapSingleItemArray: Boolean = false): String { example = mapper.writeValueAsString(this) } catch (e: JsonProcessingException) { } - } else if (unwrapSingleItemArray && this is ArrayInstance && this.value.size == 1) { - // Only used for single-line `default:` rendering, where a block-sequence item - // cannot be spliced onto the same line as the key. Sequence-valued renderings - // (e.g. annotations) must keep calling toYaml() without this flag so a - // single-item array still renders as a YAML sequence. - example = this.value[0].toYaml() } else { example = mapper.writeValueAsString(this.value) } diff --git a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlObjectTypeRenderer.kt b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlObjectTypeRenderer.kt index 6b7b7425..34c01381 100644 --- a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlObjectTypeRenderer.kt +++ b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlObjectTypeRenderer.kt @@ -108,7 +108,8 @@ class RamlObjectTypeRenderer constructor(override val vrapTypeProvider: VrapType | <<${property.type.enum.joinToString("\n") { "- '${it.value}'" }}>>""" else ""}${if (examples.isNotEmpty()) """ | examples: | <<${examples.joinToString("\n") { renderExample(it) }}>>""" else ""}${if (discriminatorProp != property.name && property.type.default != null) """ - | default: ${property.type.default.toYaml(true)}""" else ""}${if (property.type?.isInlineType == true && property.type?.annotations != null) """ + | default: ${if (property.type.default is ObjectInstance || property.type.default is ArrayInstance) """ + | <<${property.type.default.toYaml().escapeAll()}>>""" else property.type.default.toYaml()}""" else ""}${if (property.type?.isInlineType == true && property.type?.annotations != null) """ | <<${property.type.annotations.joinToString("\n") { it.renderAnnotation() }}>>""" else ""} | required: ${property.required} | (inherited): $inherited diff --git a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlResourceRenderer.kt b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlResourceRenderer.kt index 71414c0e..213c7940 100644 --- a/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlResourceRenderer.kt +++ b/languages/ramldoc/src/main/kotlin/io/vrap/codegen/languages/ramldoc/model/RamlResourceRenderer.kt @@ -182,7 +182,8 @@ class RamlResourceRenderer constructor(val api: Api, val vrapTypeProvider: VrapT } return """ |${parameterName}:${if (queryParameter.type.default != null) """ - | default: ${queryParameter.type.default.toYaml(true)}""" else ""} + | default: ${if (queryParameter.type.default is ObjectInstance || queryParameter.type.default is ArrayInstance) """ + | <<${queryParameter.type.default.toYaml().escapeAll()}>>""" else queryParameter.type.default.toYaml()}""" else ""} | required: ${queryParameter.required} | <<${queryParameter.type.renderType()}>>${if (parameterExamples.isNotEmpty()) """ | examples: diff --git a/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt b/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt index a655894a..3c69fafb 100644 --- a/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt +++ b/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt @@ -333,7 +333,7 @@ class TestCodeGenerator { } @Test - fun testArrayDefaultWithSingleElementRendersAsScalar() { + fun testDefaultValueRenders() { val generatorConfig = CodeGeneratorConfig( basePackageName = "com/commercetools/importer", outputFolder = Paths.get("build/gensrc"), @@ -350,9 +350,43 @@ class TestCodeGenerator { Assertions.assertThat(dataSink.files).isNotEmpty() val typeContent = dataSink.files.get("types/foo.raml") Assertions.assertThat(typeContent).isNotNull() - Assertions.assertThat(typeContent) - .contains("default: \"InventorySupply\"") - .doesNotContain("default: - \"InventorySupply\"") + Assertions.assertThat(typeContent).isEqualTo(""" + #%RAML 1.0 DataType + displayName: foo + type: object + (builtinType): object + properties: + foo: + type: object + (builtinType): object + default: + test: 1 + required: true + (inherited): false + bar: + type: array + items: + type: string + (builtinType): array + default: + - "foo" + - "bar" + required: true + (inherited): false + baz: + type: string + (builtinType): string + default: "baz" + required: true + (inherited): false + foobar: + type: object + (builtinType): object + default: + test: 1 + required: true + (inherited): false + """.trimIndent().trimStart()) } @Test diff --git a/languages/ramldoc/src/test/resources/arraydefault.raml b/languages/ramldoc/src/test/resources/arraydefault.raml index 55062a37..f53b123b 100644 --- a/languages/ramldoc/src/test/resources/arraydefault.raml +++ b/languages/ramldoc/src/test/resources/arraydefault.raml @@ -4,15 +4,18 @@ title: Array Default Example Test API version: 1.0 baseUri: http://example.com/api types: - ChannelRoleEnum: - type: string - enum: - - InventorySupply - - OrderExport - - OrderImport foo: properties: - roles: - type: ChannelRoleEnum[] - default: [ InventorySupply ] - + foo: + type: object + default: { test: 1 } + bar: + type: string[] + default: [ "foo", "bar" ] + baz: + type: string + default: "baz" + foobar: + type: object + default: + test: 1 From 36afac01f54c065cab0933358e2d3aaaaf92e3d4 Mon Sep 17 00:00:00 2001 From: Jens Schulze Date: Thu, 13 Aug 2026 11:18:02 +0200 Subject: [PATCH 5/5] some more test cases --- .../languages/ramldoc/TestCodeGenerator.kt | 18 ++++++++++++++++++ .../src/test/resources/arraydefault.raml | 7 +++++++ 2 files changed, 25 insertions(+) diff --git a/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt b/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt index 3c69fafb..fbcbee46 100644 --- a/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt +++ b/languages/ramldoc/src/test/kotlin/io/vrap/codegen/languages/ramldoc/TestCodeGenerator.kt @@ -373,6 +373,24 @@ class TestCodeGenerator { - "bar" required: true (inherited): false + foobaz: + type: array + items: + type: string + (builtinType): array + default: + - "foo" + required: true + (inherited): false + fooz: + type: array + items: + type: string + (builtinType): array + default: + - "foo" + required: true + (inherited): false baz: type: string (builtinType): string diff --git a/languages/ramldoc/src/test/resources/arraydefault.raml b/languages/ramldoc/src/test/resources/arraydefault.raml index f53b123b..cb631e79 100644 --- a/languages/ramldoc/src/test/resources/arraydefault.raml +++ b/languages/ramldoc/src/test/resources/arraydefault.raml @@ -12,6 +12,13 @@ types: bar: type: string[] default: [ "foo", "bar" ] + foobaz: + type: string[] + default: [ "foo" ] + fooz: + type: string[] + default: + - "foo" baz: type: string default: "baz"