Skip to content

Commit 19c0149

Browse files
committed
GH-191 Support complex nested structures in additionalProperties (Resolve #191)
1 parent b5d3fcf commit 19c0149

File tree

5 files changed

+62
-31
lines changed

5 files changed

+62
-31
lines changed

openapi-annotation-processor/src/test/kotlin/io/javalin/openapi/processor/ComponentAnnotationsTest.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import io.javalin.openapi.OpenApiPropertyType
1111
import io.javalin.openapi.OpenApiResponse
1212
import io.javalin.openapi.processor.specification.OpenApiAnnotationProcessorSpecification
1313
import net.javacrumbs.jsonunit.assertj.JsonAssertions.json
14-
import net.javacrumbs.jsonunit.assertj.JsonAssertions.value
1514
import net.javacrumbs.jsonunit.assertj.assertThatJson
1615
import org.junit.jupiter.api.Test
1716
import java.math.BigDecimal

openapi-annotation-processor/src/test/kotlin/io/javalin/openapi/processor/TypeMappersTest.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ internal class TypeMappersTest : OpenApiAnnotationProcessorSpecification() {
5454
val localDateTime: LocalDateTime,
5555
val instant: Instant,
5656
val obj: Object,
57-
val map: Map<*, *>
57+
val map: Map<*, *>,
58+
val mapWithList: Map<*, List<*>>
5859
)
5960

6061
@OpenApi(
@@ -182,6 +183,15 @@ internal class TypeMappersTest : OpenApiAnnotationProcessorSpecification() {
182183
"additionalProperties": {
183184
"type": "object"
184185
}
186+
},
187+
"mapWithList": {
188+
"type": "object",
189+
"additionalProperties": {
190+
"type": "array",
191+
"items": {
192+
"type": "object"
193+
}
194+
}
185195
}
186196
}"""
187197
))

openapi-specification/src/main/kotlin/io/javalin/openapi/experimental/ClassDefinitionApi.kt

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ class ClassDefinition(
3737
with(context) {
3838
with (mirror) {
3939
when (this) {
40-
is TypeVariable -> upperBound?.toClassDefinition(generics, type) ?: lowerBound?.toClassDefinition(generics, type)
41-
is ArrayType -> componentType.toClassDefinition(generics, type = ARRAY)
40+
is TypeVariable ->
41+
upperBound?.toClassDefinition(generics, type) ?: lowerBound?.toClassDefinition(generics, type)
42+
is ArrayType ->
43+
componentType.toClassDefinition(generics, type = ARRAY)
4244
is PrimitiveType ->
4345
ClassDefinition(
4446
context = context,
@@ -47,32 +49,34 @@ class ClassDefinition(
4749
generics = generics,
4850
structureType = type
4951
)
50-
is DeclaredType -> when {
51-
types.isAssignable(types.erasure(this), mapType().asType()) ->
52-
ClassDefinition(
53-
context = context,
54-
mirror = this,
55-
source = mapType(),
56-
generics = listOfNotNull(
57-
typeArguments.getOrElse(0) { objectType().asType() }.toClassDefinition(),
58-
typeArguments.getOrElse(1) { objectType().asType() }.toClassDefinition()
59-
),
60-
structureType = DICTIONARY
61-
)
62-
types.isAssignable(types.erasure(this), collectionType().asType()) ->
63-
typeArguments.getOrElse(0) { objectType().asType() }.toClassDefinition(generics, ARRAY)
64-
else ->
65-
ClassDefinition(
66-
context = context,
67-
mirror = this,
68-
source = asElement(),
69-
generics = typeArguments.mapNotNull { it.toClassDefinition() },
70-
structureType = type
71-
)
72-
}
73-
else -> types.asElement(this)?.asType()?.toClassDefinition(generics, type)
52+
is DeclaredType ->
53+
when {
54+
types.isAssignable(types.erasure(this), mapType().asType()) ->
55+
ClassDefinition(
56+
context = context,
57+
mirror = this,
58+
source = mapType(),
59+
generics = listOfNotNull(
60+
typeArguments.getOrElse(0) { objectType().asType() }.toClassDefinition(),
61+
typeArguments.getOrElse(1) { objectType().asType() }.toClassDefinition()
62+
),
63+
structureType = DICTIONARY
64+
)
65+
types.isAssignable(types.erasure(this), collectionType().asType()) ->
66+
typeArguments.getOrElse(0) { objectType().asType() }.toClassDefinition(generics, ARRAY)
67+
else ->
68+
ClassDefinition(
69+
context = context,
70+
mirror = this,
71+
source = asElement(),
72+
generics = typeArguments.mapNotNull { it.toClassDefinition() },
73+
structureType = type
74+
)
75+
}
76+
else ->
77+
types.asElement(this)?.asType()?.toClassDefinition(generics, type)
7478
}
75-
} ?: objectType().asType().toClassDefinition()
79+
} ?: objectType().asType().toClassDefinition(type = type)
7680
}
7781

7882
}

openapi-specification/src/main/kotlin/io/javalin/openapi/experimental/defaults/DictionaryEmbeddedTypeProcessor.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,25 @@ class DictionaryEmbeddedTypeProcessor : EmbeddedTypeProcessor {
1111
if (type.structureType == DICTIONARY) {
1212
scheme.addProperty("type", "object")
1313
val additionalProperties = JsonObject()
14-
parentContext.typeSchemaGenerator.addType(additionalProperties, context.type.generics[1], inlineRefs, references, requiresNonNulls)
14+
val additionalType = context.type.generics[1]
15+
16+
context.parentContext.configuration.embeddedTypeProcessors
17+
.firstOrNull {
18+
it.process(
19+
context.copy(
20+
scheme = additionalProperties,
21+
type = additionalType
22+
)
23+
)
24+
}
25+
?: parentContext.typeSchemaGenerator.addType(
26+
scheme = additionalProperties,
27+
type = additionalType,
28+
inlineRefs = inlineRefs,
29+
references = references,
30+
requiresNonNulls = requiresNonNulls
31+
)
32+
1533
scheme.add("additionalProperties", additionalProperties)
1634
return true
1735
}

wiki

Submodule wiki updated from e37d8a8 to 1bb9aa5

0 commit comments

Comments
 (0)