Skip to content

Commit 42b6702

Browse files
committed
calculate response interfaces (#247)
1 parent cd44c1e commit 42b6702

4 files changed

Lines changed: 159 additions & 16 deletions

File tree

openapi-processor-core/src/main/kotlin/io/openapiprocessor/core/converter/ContentTypeInterfaceCollector.kt

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,42 @@
55

66
package io.openapiprocessor.core.converter
77

8+
import io.openapiprocessor.core.model.datatypes.DataType
89
import io.openapiprocessor.core.parser.ContentType
910
import io.openapiprocessor.core.parser.HttpMethod
1011
import io.openapiprocessor.core.parser.HttpStatus
1112
import io.openapiprocessor.core.parser.Response
13+
import io.openapiprocessor.core.model.Response as ModelResponse
1214

1315
class ContentTypeInterfaceCollector(
1416
private val path: String,
15-
private val method: HttpMethod,
16-
responseCollector: ContentTypeResponseCollector
17+
private val method: HttpMethod
1718
) {
18-
val contentTypeInterfaces: Map<String, ContentTypeInterface> = collectInterfaces(
19-
responseCollector.contentTypeResponses
20-
)
2119

22-
private fun collectInterfaces(contentTypeResponses: Map<ContentType, Map<HttpStatus, Response>>)
23-
: Map<String, ContentTypeInterface> {
24-
val contentTypeInterfaces = mutableMapOf<String, ContentTypeInterface>()
20+
fun collectContentTypeInterfaces(
21+
contentTypeResponses: Map<ContentType, Map<HttpStatus, Response>>,
22+
statusResultResponses: Map<HttpStatus, List<ModelResponse>>
23+
): Map<ContentType, ContentTypeInterface> {
24+
val contentTypeInterfaces = mutableMapOf<ContentType, ContentTypeInterface>()
2525

26-
contentTypeResponses.forEach { (contentType, statusResponses) ->
27-
if (statusResponses.size > 1) {
28-
contentTypeInterfaces[contentType] = ContentTypeInterface(path, method)
26+
contentTypeResponses.forEach { (contentType, statusResponse) ->
27+
if (statusResponse.size == 1) {
28+
return@forEach
29+
}
30+
31+
var dataType: DataType? = null
32+
statusResponse.forEach { (status, response) ->
33+
val match = statusResultResponses[status]?.find { r -> r.contentType == contentType }
34+
if (match != null) {
35+
if (dataType == null) {
36+
dataType = match.responseType
37+
} else {
38+
if (match.responseType !== dataType) {
39+
contentTypeInterfaces[contentType] = ContentTypeInterface(path, method)
40+
return@forEach
41+
}
42+
}
43+
}
2944
}
3045
}
3146

openapi-processor-core/src/main/kotlin/io/openapiprocessor/core/converter/ContentTypeResponseCollector.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ private const val EMPTY: String = ""
3131
* success 2x void
3232
* errors 4x
3333
*/
34-
class ContentTypeResponseCollector(val responses: Map<HttpStatus, Response>, private val resultStyle: ResultStyle) {
34+
class ContentTypeResponseCollector(responses: Map<HttpStatus, Response>, private val resultStyle: ResultStyle) {
3535
val contentTypeResponses: Map<ContentType, Map<HttpStatus, Response>> = collectResponses(responses)
3636

3737
private fun collectResponses(responses: Map<HttpStatus, Response>): Map<ContentType, Map<HttpStatus, Response>> {
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2025 https://github.com/openapi-processor/openapi-processor-base
3+
* PDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.openapiprocessor.core.converter
7+
8+
import io.kotest.core.spec.style.StringSpec
9+
import io.kotest.matchers.maps.shouldBeEmpty
10+
import io.kotest.matchers.maps.shouldHaveSize
11+
import io.kotest.matchers.nulls.shouldNotBeNull
12+
import io.mockk.mockk
13+
import io.openapiprocessor.core.model.Documentation
14+
import io.openapiprocessor.core.model.datatypes.DataTypeName
15+
import io.openapiprocessor.core.model.datatypes.ObjectDataType
16+
import io.openapiprocessor.core.model.datatypes.PropertyDataType
17+
import io.openapiprocessor.core.model.datatypes.StringDataType
18+
import io.openapiprocessor.core.parser.ContentType
19+
import io.openapiprocessor.core.parser.HttpMethod
20+
import io.openapiprocessor.core.parser.HttpStatus
21+
import io.openapiprocessor.core.parser.Response
22+
import io.openapiprocessor.core.model.Response as ModelResponse
23+
24+
25+
class ContentTypeInterfaceCollectorSpec: StringSpec({
26+
27+
val foo = ObjectDataType(
28+
DataTypeName("Foo"),
29+
"pkg",
30+
linkedMapOf(
31+
"foo" to PropertyDataType(
32+
readOnly = false,
33+
writeOnly = false,
34+
dataType = StringDataType(),
35+
documentation = Documentation())))
36+
37+
val oof = ObjectDataType(
38+
DataTypeName("Oof"),
39+
"pkg",
40+
linkedMapOf(
41+
"oof" to PropertyDataType(
42+
readOnly = false,
43+
writeOnly = false,
44+
dataType = StringDataType(),
45+
documentation = Documentation())))
46+
47+
val bar = ObjectDataType(
48+
DataTypeName("Bar"),
49+
"pkg",
50+
linkedMapOf(
51+
"bar" to PropertyDataType(
52+
readOnly = false,
53+
writeOnly = false,
54+
dataType = StringDataType(),
55+
documentation = Documentation())))
56+
57+
"returns empty result on empty inputs" {
58+
val collector = ContentTypeInterfaceCollector("/foo", HttpMethod.GET)
59+
60+
val cti = collector.collectContentTypeInterfaces(emptyMap(), emptyMap())
61+
62+
cti.shouldBeEmpty()
63+
}
64+
65+
"returns empty result with identical response data types" {
66+
val collector = ContentTypeInterfaceCollector("/foo", HttpMethod.GET)
67+
68+
val ctr = mutableMapOf<ContentType, Map<HttpStatus, Response>>()
69+
ctr["application/json"] = mapOf(
70+
"200" to mockk<Response>(),
71+
"201" to mockk<Response>()
72+
)
73+
74+
val srr = mutableMapOf<HttpStatus, List<ModelResponse>>()
75+
srr["200"] = listOf(ModelResponse("application/json", foo))
76+
srr["201"] = listOf(ModelResponse("application/json", foo))
77+
78+
val cti = collector.collectContentTypeInterfaces(ctr, srr)
79+
80+
cti.shouldBeEmpty()
81+
}
82+
83+
"returns interface result with different response data types" {
84+
val collector = ContentTypeInterfaceCollector("/foo", HttpMethod.GET)
85+
86+
val ctr = mutableMapOf<ContentType, Map<HttpStatus, Response>>()
87+
ctr["application/json"] = mapOf(
88+
"200" to mockk<Response>(),
89+
"201" to mockk<Response>(),
90+
)
91+
92+
val srr = mutableMapOf<HttpStatus, List<ModelResponse>>()
93+
srr["200"] = listOf(ModelResponse("application/json", foo))
94+
srr["201"] = listOf(ModelResponse("application/json", bar))
95+
96+
val cti = collector.collectContentTypeInterfaces(ctr, srr)
97+
98+
cti shouldHaveSize 1
99+
100+
cti["application/json"].shouldNotBeNull()
101+
}
102+
103+
"returns interface result with different response data types including errors" {
104+
val collector = ContentTypeInterfaceCollector("/foo", HttpMethod.GET)
105+
106+
val ctr = mutableMapOf<ContentType, Map<HttpStatus, Response>>()
107+
ctr["application/json"] = mapOf(
108+
"200" to mockk<Response>(),
109+
"202" to mockk<Response>(),
110+
"400" to mockk<Response>(),
111+
"401" to mockk<Response>(),
112+
)
113+
114+
val srr = mutableMapOf<HttpStatus, List<ModelResponse>>()
115+
srr["200"] = listOf(ModelResponse("application/json", foo))
116+
srr["201"] = listOf(ModelResponse("application/json", bar))
117+
srr["400"] = listOf(ModelResponse("application/json", oof))
118+
srr["401"] = listOf(ModelResponse("application/json", oof))
119+
120+
val cti = collector.collectContentTypeInterfaces(ctr, srr)
121+
122+
cti shouldHaveSize 1
123+
124+
cti["application/json"].shouldNotBeNull()
125+
}
126+
127+
})

openapi-processor-core/src/test/kotlin/io/openapiprocessor/core/converter/ContentTypeResponseCollectorSpec.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
/*
2+
* Copyright 2025 https://github.com/openapi-processor/openapi-processor-base
3+
* PDX-License-Identifier: Apache-2.0
4+
*/
5+
16
package io.openapiprocessor.core.converter
27

38
import io.kotest.core.spec.style.StringSpec
4-
import io.kotest.core.spec.style.scopes.StringSpecRootScope.invoke
59
import io.kotest.matchers.nulls.shouldBeNull
610
import io.kotest.matchers.nulls.shouldNotBeNull
711
import io.openapiprocessor.core.processor.mapping.v2.ResultStyle
@@ -59,9 +63,6 @@ class ContentTypeResponseCollectorSpec: StringSpec({
5963
collector.contentTypeResponses["application/json"]!!["200"].shouldNotBeNull()
6064
}
6165

62-
// same result, no need to create interface, has to know if types are identical
63-
// "light" call to data type converter ?
64-
// ask if it is the same type? could use own DataTypes object???? might work...
6566
"multiple success responses without marker interface" {
6667
val openApi = parseApi(
6768
"""

0 commit comments

Comments
 (0)