Skip to content

Commit

Permalink
Fixed bug in map-based classes, added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pwall567 committed Sep 4, 2024
1 parent 0af9402 commit 1664df5
Show file tree
Hide file tree
Showing 24 changed files with 645 additions and 32 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

The format is based on [Keep a Changelog](http://keepachangelog.com/).

## Unreleased
## [0.109] - 2024-09-05
### Changed
- templates: fixed bug in templates for map-based classes
- `CodeGenerator`: added explicit types to public APIs

## [0.108] - 2024-08-07
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,25 +334,25 @@ operation:

## Dependency Specification

The latest version of the library is 0.108, and it may be obtained from the Maven Central repository.
The latest version of the library is 0.109, and it may be obtained from the Maven Central repository.

### Maven
```xml
<dependency>
<groupId>net.pwall.json</groupId>
<artifactId>json-kotlin-schema-codegen</artifactId>
<version>0.108</version>
<version>0.109</version>
</dependency>
```
### Gradle
```groovy
implementation 'net.pwall.json:json-kotlin-schema-codegen:0.108'
implementation 'net.pwall.json:json-kotlin-schema-codegen:0.109'
```
### Gradle (kts)
```kotlin
implementation("net.pwall.json:json-kotlin-schema-codegen:0.108")
implementation("net.pwall.json:json-kotlin-schema-codegen:0.109")
```

Peter Wall

2024-08-07
2024-09-05
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>net.pwall.json</groupId>
<artifactId>json-kotlin-schema-codegen</artifactId>
<version>0.108</version>
<version>0.109</version>
<name>JSON Schema Code Generation</name>
<description>Code generation from JSON Schema to Kotlin or Java</description>
<packaging>jar</packaging>
Expand Down
5 changes: 2 additions & 3 deletions src/main/resources/kotlin/class_map_property.mustache
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{{&indent}} require(cg_map["{{&name}}"] is {{#isArray}}{{>list_or_set}}<*>{{/isArray}}{{!
}}{{^isArray}}{{>type_ex_nullable}}{{/isArray}}) { "{{&name}} is not the correct type, expecting {{>type}}" }
{{&indent}} require(cg_map["{{&name}}"] is {{>type_or_list_or_set}}) { "{{&name}} is not the correct type, expecting {{>type}}" }
{{#isArray}}{{&indent}} (cg_map["{{&name}}"] as {{>list_or_set}}<*>).forEach { {{#indent.increment}}{{#arrayItems}}{{&kotlinName}} ->
{{>class_map_property_item}}{{/arrayItems}}{{/indent.increment}}{{&indent}} }
{{/isArray}}{{^isArray}}{{#validations}}{{#first}}{{&indent}} (cg_map["{{&name}}"] as {{>type_ex_nullable}}).let { {{&kotlinName}} ->
{{/isArray}}{{^isArray}}{{#validations}}{{#first}}{{&indent}} (cg_map["{{&name}}"] as {{>type}}){{#nullable}}?{{/nullable}}.let { {{&kotlinName}} ->
{{/first}}{{#indent.increment}}{{>validations_2}}{{/indent.increment}}{{#last}}{{&indent}} }
{{/last}}{{/validations}}{{/isArray}}
15 changes: 10 additions & 5 deletions src/main/resources/kotlin/nested_class_map.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@
{{#properties}}{{#defaultValue}}{{&indent}} if (cg_map.containsKey("{{&name}}")){{#validations}}{{#first}} {{&openBrace}}{{/first}}{{/validations}}
{{#indent.increment}}{{>class_map_property}}{{/indent.increment}}{{#validations}}{{#first}}{{&indent}} }
{{/first}}{{/validations}}{{/defaultValue}}{{^defaultValue}}{{!
}}{{#nullable}}{{&indent}} if (cg_map.containsKey("{{&name}}")){{#validations}}{{#first}} {{&openBrace}}{{/first}}{{/validations}}
{{#indent.increment}}{{>class_map_property}}{{/indent.increment}}{{#validations}}{{#first}}{{&indent}} }
{{/first}}{{/validations}}{{/nullable}}{{^nullable}}{{&indent}} require(cg_map.containsKey("{{&name}}")) { "required property missing - {{&name}}" }
}}{{#nullable}}{{&indent}} if (cg_map.containsKey("{{&name}}")){{!
}}{{#isArray}} {{&openBrace}}{{/isArray}}{{^isArray}}{{#validations}}{{#first}} {{&openBrace}}{{/first}}{{/validations}}{{/isArray}}
{{#indent.increment}}{{>class_map_property}}{{/indent.increment}}{{!
}}{{#isArray}}{{&indent}} }
{{/isArray}}{{^isArray}}{{#validations}}{{#first}}{{&indent}} }
{{/first}}{{/validations}}{{/isArray}}{{/nullable}}{{^nullable}}{{&indent}} require(cg_map.containsKey("{{&name}}")) { "required property missing - {{&name}}" }
{{>class_map_property}}{{/nullable}}{{/defaultValue}}{{/properties}}{{!
}}{{^properties}}{{#additionalProperties}}{{#validations}}{{#first}}{{&indent}} cg_map.entries.forEach { (key, value) ->
{{/first}}{{#indent.increment}}{{#mapEntryContext}}{{>validations_2}}{{/mapEntryContext}}{{/indent.increment}}{{#last}}{{&indent}} }
{{/last}}{{/validations}}{{/additionalProperties}}{{/properties}}{{#validations}}{{>validations_2}}{{/validations}}{{&indent}} }{{/additionalPropertiesNeedsInit}}
{{#properties}}
{{#schema}}{{#description}}{{&indent}} /** {{&safeDescription}} */
{{/description}}{{/schema}}{{&indent}} val {{&kotlinName}}: {{>type}}{{#defaultValue}}
{{&indent}} get() = if (cg_map.containsKey("{{&name}}")) cg_map["{{&name}}"] as {{>type}} else {{>default_value}}
{{#isArray}}{{&indent}} @Suppress("unchecked_cast")
{{/isArray}}{{&indent}} get() = if (cg_map.containsKey("{{&name}}")) cg_map["{{&name}}"] as {{>type}} else {{>default_value}}
{{/defaultValue}}{{^defaultValue}}{{#nullable}}
{{&indent}} get() = cg_map["{{&name}}"] as {{>type}}
{{#isArray}}{{&indent}} @Suppress("unchecked_cast")
{{/isArray}}{{&indent}} get() = cg_map["{{&name}}"] as {{>type}}
{{/nullable}}{{^nullable}} by cg_map
{{/nullable}}{{/defaultValue}}{{/properties}}
{{&indent}} override fun toString(): String = "{{&className}}(${cg_map.entries.joinToString { "${it.key}=${it.value}" }})"
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/kotlin/type_or_list_or_set.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{#isArray}}{{>list_or_set}}<*>{{/isArray}}{{^isArray}}{{>type}}{{/isArray}}
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,42 @@ class CodeGeneratorAdditionalPropertiesTest {
expect(resultFile("TestApTrueExtra")) { outputDetails.output() }
}

@Test fun `should generate code for aP true with optional extra`() {
val input = File("src/test/resources/test-ap-true-extra-optional.schema.json")
val outputDetails = OutputDetails(TargetFileName("TestApTrueExtraOptional", "kt", packageDirs))
CodeGenerator().apply {
additionalPropertiesOption = CodeGenerator.AdditionalPropertiesOption.STRICT
basePackageName = packageName
outputResolver = outputCapture(outputDetails)
generate(input)
}
expect(resultFile("TestApTrueExtraOptional")) { outputDetails.output() }
}

@Test fun `should generate code for aP true with extra with validation`() {
val input = File("src/test/resources/test-ap-true-extra-valid.schema.json")
val outputDetails = OutputDetails(TargetFileName("TestApTrueExtraValid", "kt", packageDirs))
CodeGenerator().apply {
additionalPropertiesOption = CodeGenerator.AdditionalPropertiesOption.STRICT
basePackageName = packageName
outputResolver = outputCapture(outputDetails)
generate(input)
}
expect(resultFile("TestApTrueExtraValid")) { outputDetails.output() }
}

@Test fun `should generate code for aP true with optional extra with validation`() {
val input = File("src/test/resources/test-ap-true-extra-opt-valid.schema.json")
val outputDetails = OutputDetails(TargetFileName("TestApTrueExtraOptValid", "kt", packageDirs))
CodeGenerator().apply {
additionalPropertiesOption = CodeGenerator.AdditionalPropertiesOption.STRICT
basePackageName = packageName
outputResolver = outputCapture(outputDetails)
generate(input)
}
expect(resultFile("TestApTrueExtraOptValid")) { outputDetails.output() }
}

@Test fun `should generate code for aP true with extra array`() {
val input = File("src/test/resources/test-ap-true-extra-array.schema.json")
val outputDetails = OutputDetails(TargetFileName("TestApTrueExtraArray", "kt", packageDirs))
Expand All @@ -135,6 +171,30 @@ class CodeGeneratorAdditionalPropertiesTest {
expect(resultFile("TestApTrueExtraArray")) { outputDetails.output() }
}

@Test fun `should generate code for aP true with optional extra array`() {
val input = File("src/test/resources/test-ap-true-extra-array-opt.schema.json")
val outputDetails = OutputDetails(TargetFileName("TestApTrueExtraArrayOpt", "kt", packageDirs))
CodeGenerator().apply {
additionalPropertiesOption = CodeGenerator.AdditionalPropertiesOption.STRICT
basePackageName = packageName
outputResolver = outputCapture(outputDetails)
generate(input)
}
expect(resultFile("TestApTrueExtraArrayOpt")) { outputDetails.output() }
}

@Test fun `should generate code for aP true with optional extra array with validation`() {
val input = File("src/test/resources/test-ap-true-extra-array-opt-valid.schema.json")
val outputDetails = OutputDetails(TargetFileName("TestApTrueExtraArrayOptValid", "kt", packageDirs))
CodeGenerator().apply {
additionalPropertiesOption = CodeGenerator.AdditionalPropertiesOption.STRICT
basePackageName = packageName
outputResolver = outputCapture(outputDetails)
generate(input)
}
expect(resultFile("TestApTrueExtraArrayOptValid")) { outputDetails.output() }
}

@Test fun `should generate code for aP true with extra with default value`() {
val input = File("src/test/resources/test-ap-true-extra-default.schema.json")
val outputDetails = OutputDetails(TargetFileName("TestApTrueExtraDefault", "kt", packageDirs))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,31 +94,31 @@ class AdditionalPropertiesDeserializationTest {

@Test fun `should deserialize TestApTrueExtra`() {
val json1 = """{"extra":"content"}"""
val result1 = json1.parseJSON<TestApTrueExtra>()
val result1 = json1.parseJSON<TestApTrueExtraValid>()
expect("content") { result1.extra }
assertNull(result1["whatever"])

val json2 = """{"extra":"content","anything":"another"}"""
val result2 = json2.parseJSON<TestApTrueExtra>()
val result2 = json2.parseJSON<TestApTrueExtraValid>()
expect("content") { result2.extra }
expect("another") { result2["anything"] }
assertNull(result2["whatever"])

assertFailsWith<JSONKotlinException> {
"""{"anything":"another"}""".parseJSON<TestApTrueExtra>()
"""{"anything":"another"}""".parseJSON<TestApTrueExtraValid>()
}.cause.let {
assertIs<IllegalArgumentException>(it)
expect("required property missing - extra") { it.message }
}

assertFailsWith<JSONKotlinException> {
"""{"extra":123}""".parseJSON<TestApTrueExtra>()
"""{"extra":123}""".parseJSON<TestApTrueExtraValid>()
}.let {
expect("Can't deserialize 123 as String at /extra") { it.message }
}

assertFailsWith<JSONKotlinException> {
"""{"extra":""}""".parseJSON<TestApTrueExtra>()
"""{"extra":""}""".parseJSON<TestApTrueExtraValid>()
}.cause.let {
assertIs<IllegalArgumentException>(it)
expect("extra length < minimum 1 - 0") { it.message }
Expand Down Expand Up @@ -150,7 +150,7 @@ class AdditionalPropertiesDeserializationTest {
}

assertFailsWith<JSONKotlinException> {
"""{"extra":""}""".parseJSON<TestApTrueExtra>()
"""{"extra":""}""".parseJSON<TestApTrueExtraValid>()
}.cause.let {
assertIs<IllegalArgumentException>(it)
expect("extra length < minimum 1 - 0") { it.message }
Expand Down
Loading

0 comments on commit 1664df5

Please sign in to comment.