Skip to content

Commit 6a7e91d

Browse files
authored
Use parameter type for declarative query defaults (helidon-io#11590)
1 parent d36d651 commit 6a7e91d

4 files changed

Lines changed: 158 additions & 5 deletions

File tree

declarative/codegen/src/main/java/io/helidon/declarative/codegen/http/webserver/ParamProviderHttpQuery.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import io.helidon.codegen.CodegenException;
2222
import io.helidon.codegen.classmodel.ContentBuilder;
2323
import io.helidon.common.types.Annotation;
24+
import io.helidon.common.types.TypeName;
2425
import io.helidon.declarative.codegen.http.webserver.spi.HttpParameterCodegenProvider;
2526
import io.helidon.service.codegen.DefaultsCodegen;
2627
import io.helidon.service.codegen.DefaultsParams;
@@ -38,21 +39,22 @@ public boolean codegen(ParameterCodegenContext ctx) {
3839
return false;
3940
}
4041

41-
Optional<DefaultsCodegen.DefaultCode> defaultCode = DefaultsCodegen.findDefault(ctx.annotations(),
42-
HTTP_QUERY_PARAM_ANNOTATION);
43-
4442
Annotation queryParam = first.get();
4543
String queryParamName = queryParam.value()
4644
.orElseThrow(() -> new CodegenException("@QueryParam annotation must have a value."));
4745

46+
TypeName parameterType = ctx.parameterType();
47+
TypeName realType = parameterType.isOptional() ? parameterType.typeArguments().getFirst() : parameterType;
48+
Optional<DefaultsCodegen.DefaultCode> defaultCode = DefaultsCodegen.findDefault(ctx.annotations(), realType);
49+
4850
ContentBuilder<?> contentBuilder = ctx.contentBuilder();
4951
contentBuilder.addContent(ctx.serverRequestParamName())
5052
.addContent(".query()");
5153

5254
codegenFromParameters(contentBuilder,
53-
ctx.parameterType(),
55+
parameterType,
5456
queryParamName,
55-
ctx.parameterType().isOptional() || defaultCode.isPresent());
57+
parameterType.isOptional() || defaultCode.isPresent());
5658

5759
if (defaultCode.isPresent()) {
5860
var defaultInfo = defaultCode.get();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright (c) 2026 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.declarative.codegen.http.webserver;
18+
19+
import java.io.IOException;
20+
import java.nio.charset.StandardCharsets;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.util.List;
24+
25+
import io.helidon.builder.api.Prototype;
26+
import io.helidon.codegen.apt.AptProcessor;
27+
import io.helidon.codegen.testing.TestCompiler;
28+
import io.helidon.common.Default;
29+
import io.helidon.common.Generated;
30+
import io.helidon.common.GenericType;
31+
import io.helidon.common.mapper.Mappers;
32+
import io.helidon.common.parameters.Parameters;
33+
import io.helidon.common.types.Annotation;
34+
import io.helidon.common.uri.UriQuery;
35+
import io.helidon.config.Config;
36+
import io.helidon.http.Http;
37+
import io.helidon.service.registry.Dependency;
38+
import io.helidon.service.registry.Service;
39+
import io.helidon.service.registry.ServiceDescriptor;
40+
import io.helidon.webserver.http.Handler;
41+
import io.helidon.webserver.http.HttpEntryPoint;
42+
import io.helidon.webserver.http.HttpFeature;
43+
import io.helidon.webserver.http.HttpRoute;
44+
import io.helidon.webserver.http.HttpRouting;
45+
import io.helidon.webserver.http.HttpRules;
46+
import io.helidon.webserver.http.RestServer;
47+
import io.helidon.webserver.http.ServerRequest;
48+
import io.helidon.webserver.http.ServerResponse;
49+
50+
import org.junit.jupiter.api.Test;
51+
52+
import static org.hamcrest.CoreMatchers.containsString;
53+
import static org.hamcrest.CoreMatchers.is;
54+
import static org.hamcrest.MatcherAssert.assertThat;
55+
56+
class QueryParamDefaultValueCodegenTest {
57+
private static final List<Class<?>> CLASSPATH = List.of(
58+
Annotation.class,
59+
Config.class,
60+
Default.class,
61+
Dependency.class,
62+
Generated.class,
63+
GenericType.class,
64+
Handler.class,
65+
Http.class,
66+
HttpEntryPoint.class,
67+
HttpFeature.class,
68+
HttpRoute.class,
69+
HttpRouting.class,
70+
HttpRules.class,
71+
Mappers.class,
72+
Parameters.class,
73+
Prototype.class,
74+
RestServer.class,
75+
ServerRequest.class,
76+
ServerResponse.class,
77+
Service.class,
78+
ServiceDescriptor.class,
79+
UriQuery.class
80+
);
81+
82+
@Test
83+
void generatedQueryDefaultUsesParameterType() throws IOException {
84+
var result = TestCompiler.builder()
85+
.currentRelease()
86+
.addClasspath(CLASSPATH)
87+
.addProcessor(AptProcessor::new)
88+
.workDir(Path.of("target/test-compiler/http-query-default-value"))
89+
.addSource("DefaultQueryEndpoint.java", """
90+
package com.example;
91+
92+
import io.helidon.common.Default;
93+
import io.helidon.http.Http;
94+
import io.helidon.service.registry.Service;
95+
import io.helidon.webserver.http.RestServer;
96+
97+
@SuppressWarnings("deprecation")
98+
@RestServer.Listener("@default")
99+
@RestServer.Endpoint
100+
@Service.Singleton
101+
@Http.Path("/default-query")
102+
class DefaultQueryEndpoint {
103+
@Http.GET
104+
String limit(@Http.QueryParam("limit") @Default.Value("13") Integer limit) {
105+
return Integer.toString(limit);
106+
}
107+
}
108+
""")
109+
.addSource("Main.java", """
110+
package com.example;
111+
112+
import io.helidon.service.registry.Service;
113+
114+
@Service.GenerateBinding
115+
class Main {
116+
}
117+
""")
118+
.build()
119+
.compile();
120+
121+
String diagnostics = String.join("\n", result.diagnostics());
122+
assertThat(diagnostics, result.success(), is(true));
123+
124+
var generatedSources = Files.walk(result.sourceOutput())
125+
.filter(it -> it.getFileName().toString().endsWith(".java"))
126+
.toList();
127+
128+
StringBuilder generatedContent = new StringBuilder();
129+
for (Path generatedSource : generatedSources) {
130+
generatedContent.append(Files.readString(generatedSource, StandardCharsets.UTF_8));
131+
generatedContent.append('\n');
132+
}
133+
134+
assertThat(generatedContent.toString(), containsString("GenericType.create(Integer.class)"));
135+
}
136+
}

declarative/tests/http/src/main/java/io/helidon/declarative/tests/http/GreetServiceEndpoint.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ String queryParam(@Http.QueryParam("param") String queryParam) {
9393
return queryParam;
9494
}
9595

96+
@Http.GET
97+
@Http.Path("/query-default")
98+
String queryParamDefault(@Http.QueryParam("limit") @Default.Value("13") Integer limit) {
99+
return Integer.toString(limit);
100+
}
101+
96102
/**
97103
* Return a worldly greeting message.
98104
*/

declarative/tests/http/src/test/java/io/helidon/declarative/tests/http/DeclarativeHttpTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,15 @@ void testQueryParamFailure() {
158158
assertThat(response.entity(), is("Query parameter param is not present in the request."));
159159
}
160160

161+
@Test
162+
void testQueryParamDefaultValue() {
163+
var response = client.get("/greet/query-default")
164+
.request(String.class);
165+
166+
assertThat(response.status(), is(Status.OK_200));
167+
assertThat(response.entity(), is("13"));
168+
}
169+
161170
@Test
162171
void testTypedClient() {
163172
GreetServiceClient typedClient = registry.get(Lookup.builder()

0 commit comments

Comments
 (0)