Skip to content

Commit 8ca1aaa

Browse files
committed
1 parent 7b7ab48 commit 8ca1aaa

34 files changed

Lines changed: 303 additions & 6 deletions

File tree

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-jdk = "11"
44
test-jdk = "17"
55

66
api = "2024.2"
7-
base = "2025.2"
7+
base = "2025.3-SNAPSHOT"
88

99
junit = "5.9.3"
1010
jacoco = "0.8.7"

src/main/kotlin/io/openapiprocessor/micronaut/processor/MicronautFrameworkAnnotations.kt

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55

66
package io.openapiprocessor.micronaut.processor
77

8+
import io.openapiprocessor.core.converter.mapping.SimpleParameterValue
89
import io.openapiprocessor.core.framework.FrameworkAnnotations
910
import io.openapiprocessor.core.model.Annotation
11+
import io.openapiprocessor.core.model.EndpointResponseStatus
1012
import io.openapiprocessor.core.parser.HttpMethod
1113
import io.openapiprocessor.core.model.RequestBody
1214
import io.openapiprocessor.core.model.parameters.*
@@ -39,6 +41,19 @@ class MicronautFrameworkAnnotations: FrameworkAnnotations {
3941
}
4042
}
4143

44+
override fun getAnnotation(status: EndpointResponseStatus): Annotation {
45+
val statusCode = HTTP_STATUS[status.statusCode]
46+
if (statusCode == null) {
47+
log.error("unknown http status code: ${status.statusCode}")
48+
return UNKNOWN_ANNOTATION
49+
}
50+
51+
return Annotation(
52+
getAnnotationName("Status"),
53+
linkedMapOf(
54+
"code" to SimpleParameterValue(statusCode, HTTP_STATUS_ENUM)))
55+
}
56+
4257
private fun getAnnotation(key: String): Annotation {
4358
return PARAMETER_ANNOTATIONS.getValue(key)
4459
}
@@ -73,3 +88,83 @@ private const val ANNOTATION_PKG = "io.micronaut.http.annotation"
7388
private fun getAnnotationName(name: String): String {
7489
return "${ANNOTATION_PKG}.${name}"
7590
}
91+
92+
private val HTTP_STATUS = hashMapOf(
93+
"100" to getEnum("CONTINUE"),
94+
"101" to getEnum("SWITCHING_PROTOCOLS"),
95+
"102" to getEnum("PROCESSING"), // WebDAV
96+
97+
"200" to getEnum("OK"),
98+
"201" to getEnum("CREATED"),
99+
"202" to getEnum("ACCEPTED"),
100+
"203" to getEnum("NON_AUTHORITATIVE_INFORMATION"),
101+
"204" to getEnum("NO_CONTENT"),
102+
"205" to getEnum("RESET_CONTENT"),
103+
"206" to getEnum("PARTIAL_CONTENT"),
104+
"207" to getEnum("MULTI_STATUS"), // WebDAV
105+
"208" to getEnum("ALREADY_REPORTED"), // WebDAV
106+
"226" to getEnum("IM_USED"),
107+
108+
"300" to getEnum("MULTIPLE_CHOICES"),
109+
"301" to getEnum("MOVED_PERMANENTLY"),
110+
"302" to getEnum("FOUND"), // replaces MOVED_TEMPORARILY
111+
"303" to getEnum("SEE_OTHER"),
112+
"304" to getEnum("NOT_MODIFIED"),
113+
"305" to getEnum("USE_PROXY"),
114+
"306" to getEnum("SWITCH_PROXY"),
115+
"307" to getEnum("TEMPORARY_REDIRECT"),
116+
"308" to getEnum("PERMANENT_REDIRECT"),
117+
118+
"400" to getEnum("BAD_REQUEST"),
119+
"401" to getEnum("UNAUTHORIZED"),
120+
"402" to getEnum("PAYMENT_REQUIRED"),
121+
"403" to getEnum("FORBIDDEN"),
122+
"404" to getEnum("NOT_FOUND"),
123+
"405" to getEnum("METHOD_NOT_ALLOWED"),
124+
"406" to getEnum("NOT_ACCEPTABLE"),
125+
"407" to getEnum("PROXY_AUTHENTICATION_REQUIRED"),
126+
"408" to getEnum("REQUEST_TIMEOUT"),
127+
"409" to getEnum("CONFLICT"),
128+
"410" to getEnum("GONE"),
129+
"411" to getEnum("LENGTH_REQUIRED"),
130+
"412" to getEnum("PRECONDITION_FAILED"),
131+
"413" to getEnum("REQUEST_ENTITY_TOO_LARGE"),
132+
"414" to getEnum("REQUEST_URI_TOO_LONG"),
133+
"415" to getEnum("UNSUPPORTED_MEDIA_TYPE"),
134+
"416" to getEnum("REQUESTED_RANGE_NOT_SATISFIABLE"),
135+
"417" to getEnum("EXPECTATION_FAILED"),
136+
"418" to getEnum("I_AM_A_TEAPOT"),
137+
"420" to getEnum("ENHANCE_YOUR_CALM"),
138+
"422" to getEnum("UNPROCESSABLE_ENTITY"), // WebDAV
139+
"423" to getEnum("LOCKED"), // WebDAV
140+
"424" to getEnum("FAILED_DEPENDENCY"), // WebDAV
141+
"425" to getEnum("UNORDERED_COLLECTION"),
142+
"426" to getEnum("UPGRADE_REQUIRED"),
143+
"428" to getEnum("PRECONDITION_REQUIRED"),
144+
"429" to getEnum("TOO_MANY_REQUESTS"),
145+
"431" to getEnum("REQUEST_HEADER_FIELDS_TOO_LARGE"),
146+
"444" to getEnum("NO_RESPONSE"),
147+
"450" to getEnum("BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS"),
148+
"451" to getEnum("UNAVAILABLE_FOR_LEGAL_REASONS"),
149+
"494" to getEnum("REQUEST_HEADER_TOO_LARGE"),
150+
151+
"500" to getEnum("INTERNAL_SERVER_ERROR"),
152+
"501" to getEnum("NOT_IMPLEMENTED"),
153+
"502" to getEnum("BAD_GATEWAY"),
154+
"503" to getEnum("SERVICE_UNAVAILABLE"),
155+
"504" to getEnum("GATEWAY_TIMEOUT"),
156+
"505" to getEnum("HTTP_VERSION_NOT_SUPPORTED"),
157+
"506" to getEnum("VARIANT_ALSO_NEGOTIATES"),
158+
"507" to getEnum("INSUFFICIENT_STORAGE"), // WebDAV
159+
"508" to getEnum("LOOP_DETECTED"), // WebDAV
160+
"509" to getEnum("BANDWIDTH_LIMIT_EXCEEDED"),
161+
"510" to getEnum("NOT_EXTENDED"),
162+
"511" to getEnum("NETWORK_AUTHENTICATION_REQUIRED"),
163+
"522" to getEnum("CONNECTION_TIMED_OUT")
164+
)
165+
166+
private const val HTTP_STATUS_ENUM = "io.micronaut.http.HttpStatus"
167+
168+
private fun getEnum(name: String): String {
169+
return "HttpStatus.${name}"
170+
}

src/main/kotlin/io/openapiprocessor/micronaut/processor/MicronautProcessor.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import io.openapiprocessor.micronaut.Version
1616
import io.openapiprocessor.micronaut.writer.java.BeanValidations
1717
import io.openapiprocessor.micronaut.writer.java.MappingAnnotationWriter
1818
import io.openapiprocessor.micronaut.writer.java.ParameterAnnotationWriter
19+
import io.openapiprocessor.micronaut.writer.java.StatusAnnotationWriter
1920
import io.openapiprocessor.test.api.OpenApiProcessorTest
2021
import org.slf4j.Logger
2122
import org.slf4j.LoggerFactory
@@ -65,6 +66,7 @@ class MicronautProcessor : OpenApiProcessorTest {
6566
MethodWriter(
6667
options,
6768
identifier,
69+
StatusAnnotationWriter(annotations),
6870
MappingAnnotationWriter(),
6971
ParameterAnnotationWriter(annotations),
7072
beanValidations,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2025 https://github.com/openapi-processor/openapi-processor-micronaut
3+
* PDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.openapiprocessor.micronaut.writer.java
7+
8+
import io.openapiprocessor.core.framework.FrameworkAnnotations
9+
import io.openapiprocessor.core.model.Endpoint
10+
import io.openapiprocessor.core.model.EndpointResponse
11+
import io.openapiprocessor.core.model.EndpointResponseStatus
12+
import java.io.Writer
13+
import io.openapiprocessor.core.writer.java.StatusAnnotationWriter as CoreStatusAnnotationWriter
14+
15+
class StatusAnnotationWriter(private val annotations: FrameworkAnnotations): CoreStatusAnnotationWriter {
16+
17+
override fun write(
18+
target: Writer,
19+
endpoint: Endpoint,
20+
endpointResponse: EndpointResponse
21+
) {
22+
target.write(createStatusAnnotation(endpointResponse))
23+
}
24+
25+
private fun createStatusAnnotation(status: EndpointResponseStatus): String {
26+
val data = annotations.getAnnotation(status)
27+
val status = data.parameters["code"]
28+
29+
var annotation = data.annotationName
30+
annotation += "(${status!!.value})"
31+
return annotation
32+
}
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2025 https://github.com/openapi-processor/openapi-processor-micronaut
3+
* PDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.openapiprocessor.micronaut.processor
7+
8+
import io.kotest.core.spec.style.StringSpec
9+
import io.kotest.matchers.collections.shouldContainExactly
10+
import io.kotest.matchers.shouldBe
11+
import io.openapiprocessor.core.model.EndpointResponseStatus
12+
import io.openapiprocessor.core.model.HttpStatus
13+
14+
class MicronautFrameworkAnnotationsSpec : StringSpec({
15+
16+
class Status(override val statusCode: HttpStatus) : EndpointResponseStatus
17+
18+
"provides known HttpStatus annotation with specific status code import" {
19+
val framework = MicronautFrameworkAnnotations()
20+
21+
val annotation200 = framework.getAnnotation(Status("200"))
22+
annotation200.referencedImports shouldContainExactly setOf("io.micronaut.http.HttpStatus")
23+
annotation200.parameters["code"]!!.value shouldBe "HttpStatus.OK"
24+
25+
val annotation400 = framework.getAnnotation(Status("400"))
26+
annotation400.referencedImports shouldContainExactly setOf("io.micronaut.http.HttpStatus")
27+
annotation400.parameters["code"]!!.value shouldBe "HttpStatus.BAD_REQUEST"
28+
29+
val annotation500 = framework.getAnnotation(Status("500"))
30+
annotation500.referencedImports shouldContainExactly setOf("io.micronaut.http.HttpStatus")
31+
annotation500.parameters["code"]!!.value shouldBe "HttpStatus.INTERNAL_SERVER_ERROR"
32+
}
33+
})
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2025 https://github.com/openapi-processor/openapi-processor-micronaut
3+
* PDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.openapiprocessor.micronaut.writer.java
7+
8+
import io.kotest.core.spec.style.StringSpec
9+
import io.kotest.matchers.equals.shouldBeEqual
10+
import io.openapiprocessor.core.model.Endpoint
11+
import io.openapiprocessor.core.model.Response
12+
import io.openapiprocessor.core.model.datatypes.NoneDataType
13+
import io.openapiprocessor.core.parser.HttpMethod
14+
import io.openapiprocessor.micronaut.processor.MicronautFrameworkAnnotations
15+
import java.io.StringWriter
16+
17+
class StatusAnnotationWriterSpec: StringSpec({
18+
val writer = StatusAnnotationWriter(MicronautFrameworkAnnotations())
19+
val target = StringWriter()
20+
21+
"write response status annotation" {
22+
val ep = Endpoint(
23+
"/foo",
24+
HttpMethod.GET,
25+
emptyList(),
26+
emptyList(),
27+
mapOf(
28+
"204" to listOf(Response("?", NoneDataType()))
29+
)
30+
)
31+
32+
writer.write(target, ep, ep.endpointResponses.first())
33+
34+
target.toString() shouldBeEqual """@Status(HttpStatus.NO_CONTENT)"""
35+
}
36+
})

src/testInt/kotlin/io/openapiprocessor/micronaut/ProcessorTestSets.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.openapiprocessor.micronaut
77

88
import io.openapiprocessor.test.*
9+
import io.openapiprocessor.test.test30_D_
910

1011
val ALL_30: List<TestParams> = listOf(
1112
test30_DR("bean-validation-introspected"),
@@ -15,7 +16,8 @@ val ALL_30: List<TestParams> = listOf(
1516
test30_D_("params-path-simple-data-types"),
1617
test30_DR("params-request-body"),
1718
test30_DR("params-request-body-multipart-mapping"),
18-
test30_D_("params-simple-data-types")
19+
test30_D_("params-simple-data-types"),
20+
test30_D_("response-status")
1921
)
2022

2123
val ALL_31: List<TestParams> = listOf(
@@ -26,5 +28,6 @@ val ALL_31: List<TestParams> = listOf(
2628
test31_D_("params-path-simple-data-types"),
2729
test31_DR("params-request-body"),
2830
test31_DR("params-request-body-multipart-mapping"),
29-
test31_D_("params-simple-data-types")
31+
test31_D_("params-simple-data-types"),
32+
test31_D_("response-status")
3033
)
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
prefix: generated
12
items:
23
- outputs/api/Api.java
3-
- outputs/<model>/Foo.java
4+
- outputs/model/<model>/Foo.java

src/testInt/resources/tests/bean-validation-introspected/outputs/api/Api.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22

33
import generated.model.Foo;
44
import generated.support.Generated;
5+
import io.micronaut.http.HttpStatus;
56
import io.micronaut.http.annotation.Body;
67
import io.micronaut.http.annotation.Post;
8+
import io.micronaut.http.annotation.Status;
79
import javax.validation.Valid;
810
import javax.validation.constraints.NotNull;
911

1012
@Generated(value = "openapi-processor-micronaut", version = "test")
1113
public interface Api {
1214

15+
@Status(HttpStatus.NO_CONTENT)
1316
@Post(uri = "/endpoint", consumes = {"application/json"})
1417
void postEndpoint(@Body @Valid @NotNull Foo body);
1518

src/testInt/resources/tests/bean-validation-introspected/outputs/model/default/Foo.java renamed to src/testInt/resources/tests/bean-validation-introspected/outputs/model/_default_/Foo.java

File renamed without changes.

0 commit comments

Comments
 (0)