Skip to content

Commit 6340647

Browse files
authored
Add encoding support in compile time codegen (Azure#44774)
1 parent ab3c287 commit 6340647

File tree

20 files changed

+377
-127
lines changed

20 files changed

+377
-127
lines changed

sdk/clientcore/annotation-processor-test/src/main/java/io/clientcore/annotation/processor/test/implementation/TestInterfaceClientImpl.java

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
import io.clientcore.core.http.pipeline.HttpPipeline;
2121
import io.clientcore.core.implementation.http.ContentType;
2222
import io.clientcore.core.models.binarydata.BinaryData;
23-
24-
import java.io.InputStream;
2523
import java.lang.reflect.InvocationTargetException;
2624
import java.nio.ByteBuffer;
2725
import java.util.List;
@@ -62,29 +60,11 @@ Response<Void> testMethod(@BodyParam("application/octet-stream") BinaryData data
6260
@HttpRequestInformation(method = HttpMethod.GET, path = "my/uri/path", expectedStatusCodes = { 200 })
6361
Void testMethodReturnsVoid();
6462

65-
@HttpRequestInformation(method = HttpMethod.HEAD, path = "my/uri/path", expectedStatusCodes = { 200 })
66-
void testHeadMethod();
67-
68-
69-
@HttpRequestInformation(method = HttpMethod.HEAD, path = "my/uri/path", expectedStatusCodes = { 200, 207 })
70-
boolean testBooleanHeadMethod();
71-
72-
@HttpRequestInformation(method = HttpMethod.GET, path = "my/uri/path", expectedStatusCodes = { 200 })
73-
Response<Void> testMethodReturnsResponseVoid();
74-
75-
@HttpRequestInformation(method = HttpMethod.GET, path = "my/uri/path", expectedStatusCodes = { 200 })
76-
Response<InputStream> testDownload();
77-
78-
@HttpRequestInformation(method = HttpMethod.GET, path = "/kv/{key}", expectedStatusCodes = { 200 })
63+
@HttpRequestInformation(method = HttpMethod.GET, path = "kv/{key}", expectedStatusCodes = { 200 })
7964
@UnexpectedResponseExceptionDetail(exceptionBodyClass = Error.class)
8065
Response<Foo> getFoo(@PathParam("key") String key, @QueryParam("label") String label,
8166
@HeaderParam("Sync-Token") String syncToken);
8267

83-
@HttpRequestInformation(method = HttpMethod.DELETE, path = "/kv/{key}", expectedStatusCodes = { 204, 404 })
84-
Response<Void> deleteFoo(@PathParam("key") String key, @QueryParam("label") String label,
85-
@HeaderParam("Sync-Token") String syncToken);
86-
87-
8868
@HttpRequestInformation(method = HttpMethod.GET, path = "foos", expectedStatusCodes = { 200 })
8969
Response<FooListResult> listFooListResult(@HostParam("uri") String uri, RequestOptions requestOptions);
9070

@@ -129,10 +109,30 @@ Response<HttpBinJSON> postStreamResponse(@HostParam("uri") String uri,
129109
// Service 2
130110
@HttpRequestInformation(method = HttpMethod.GET, path = "bytes/{numberOfBytes}", expectedStatusCodes = { 200 })
131111
byte[] getByteArray(@HostParam("scheme") String scheme, @HostParam("hostName") String hostName,
132-
@PathParam("numberOfBytes") int numberOfBytes);
112+
@PathParam(value = "numberOfBytes", encoded = true) int numberOfBytes);
133113

134114
// Service 3
135115
@HttpRequestInformation(method = HttpMethod.GET, path = "bytes/100", expectedStatusCodes = { 200 })
136116
void getNothing(@HostParam("uri") String uri);
117+
118+
@HttpRequestInformation(method = HttpMethod.GET, path = "anything", expectedStatusCodes = { 200 })
119+
HttpBinJSON getAnything(@HostParam("uri") String uri);
120+
121+
@HttpRequestInformation(method = HttpMethod.GET, path = "anything/with+plus", expectedStatusCodes = { 200 })
122+
HttpBinJSON getAnythingWithPlus(@HostParam("uri") String uri);
123+
124+
@HttpRequestInformation(method = HttpMethod.GET, path = "anything/{path}", expectedStatusCodes = { 200 })
125+
HttpBinJSON getAnythingWithPathParam(@HostParam("uri") String uri, @PathParam("path") String pathParam);
126+
127+
@HttpRequestInformation(method = HttpMethod.GET, path = "anything/{path}", expectedStatusCodes = { 200 })
128+
HttpBinJSON getAnythingWithEncodedPathParam(@HostParam("uri") String uri,
129+
@PathParam(value = "path", encoded = true) String pathParam);
130+
131+
@HttpRequestInformation(method = HttpMethod.GET, path = "anything", expectedStatusCodes = { 200 })
132+
HttpBinJSON getAnything(@HostParam("uri") String uri, @QueryParam("a") String a, @QueryParam("b") int b);
133+
134+
@HttpRequestInformation(method = HttpMethod.GET, path = "anything", expectedStatusCodes = { 200 })
135+
HttpBinJSON getAnythingWithEncoded(@HostParam("uri") String uri,
136+
@QueryParam(value = "a", encoded = true) String a, @QueryParam("b") int b);
137137
}
138138
}

sdk/clientcore/annotation-processor-test/src/test/java/io/clientcore/annotation/processor/test/LocalHttpClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public final class LocalHttpClient implements HttpClient {
2323
@Override
2424
public Response<BinaryData> send(HttpRequest request) {
2525
lastHttpRequest = request;
26-
boolean success = request.getUri().getPath().equals("/my/uri/path");
26+
boolean success = request.getUri().getPath().equals("my/uri/path");
2727

2828
if (request.getHttpMethod().equals(HttpMethod.POST)) {
2929
success &= "application/json".equals(request.getHeaders().getValue(HttpHeaderName.CONTENT_TYPE));

sdk/clientcore/annotation-processor-test/src/test/java/io/clientcore/annotation/processor/test/TestInterfaceGenerationTests.java

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import io.clientcore.annotation.processor.test.implementation.TestInterfaceClientImpl;
77
import io.clientcore.annotation.processor.test.implementation.models.Foo;
8+
import io.clientcore.annotation.processor.test.implementation.models.HttpBinJSON;
89
import io.clientcore.core.http.client.HttpClient;
910
import io.clientcore.core.http.models.Response;
1011
import io.clientcore.core.http.pipeline.HttpPipeline;
@@ -21,6 +22,7 @@
2122
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
2223
import static org.junit.jupiter.api.Assertions.assertEquals;
2324
import static org.junit.jupiter.api.Assertions.assertNotNull;
25+
import static org.junit.jupiter.api.Assertions.fail;
2426

2527
public class TestInterfaceGenerationTests {
2628
private static LocalTestServer server;
@@ -115,6 +117,148 @@ public void getRequestWithNoReturn() {
115117
assertDoesNotThrow(() -> testInterface.getNothing(getServerUri(false)));
116118
}
117119

120+
@Test
121+
public void getRequestWithAnything() {
122+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
123+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
124+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
125+
final HttpBinJSON json = testInterface.getAnything(getServerUri(false));
126+
127+
assertNotNull(json);
128+
assertMatchWithHttpOrHttps("localhost/anything", json.uri());
129+
}
130+
131+
@Test
132+
public void getRequestWithAnythingWithPlus() {
133+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
134+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
135+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
136+
final HttpBinJSON json = testInterface.getAnythingWithPlus(getServerUri(false));
137+
138+
assertNotNull(json);
139+
assertMatchWithHttpOrHttps("localhost/anything/with+plus", json.uri());
140+
}
141+
142+
@Test
143+
public void getRequestWithAnythingWithPathParam() {
144+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
145+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
146+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
147+
final HttpBinJSON json
148+
= testInterface.getAnythingWithPathParam(getServerUri(false), "withpathparam");
149+
150+
assertNotNull(json);
151+
assertMatchWithHttpOrHttps("localhost/anything/withpathparam", json.uri());
152+
}
153+
154+
@Test
155+
public void getRequestWithAnythingWithPathParamWithSpace() {
156+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
157+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
158+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
159+
final HttpBinJSON json
160+
= testInterface.getAnythingWithPathParam(getServerUri(false), "with path param");
161+
162+
assertNotNull(json);
163+
assertMatchWithHttpOrHttps("localhost/anything/with path param", json.uri());
164+
}
165+
166+
@Test
167+
public void getRequestWithAnythingWithPathParamWithPlus() {
168+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
169+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
170+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
171+
final HttpBinJSON json
172+
= testInterface.getAnythingWithPathParam(getServerUri(false), "with+path+param");
173+
174+
assertNotNull(json);
175+
assertMatchWithHttpOrHttps("localhost/anything/with+path+param", json.uri());
176+
}
177+
178+
@Test
179+
public void getRequestWithAnythingWithEncodedPathParam() {
180+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
181+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
182+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
183+
final HttpBinJSON json
184+
= testInterface.getAnythingWithEncodedPathParam(getServerUri(false), "withpathparam");
185+
186+
assertNotNull(json);
187+
assertMatchWithHttpOrHttps("localhost/anything/withpathparam", json.uri());
188+
}
189+
190+
@Test
191+
public void getRequestWithAnythingWithEncodedPathParamWithPercent20() {
192+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
193+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
194+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
195+
final HttpBinJSON json
196+
= testInterface.getAnythingWithEncodedPathParam(getServerUri(false), "with%20path%20param");
197+
198+
assertNotNull(json);
199+
assertMatchWithHttpOrHttps("localhost/anything/with path param", json.uri());
200+
}
201+
202+
@Test
203+
public void getRequestWithAnythingWithEncodedPathParamWithPlus() {
204+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
205+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
206+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
207+
final HttpBinJSON json
208+
= testInterface.getAnythingWithEncodedPathParam(getServerUri(false), "with+path+param");
209+
210+
assertNotNull(json);
211+
assertMatchWithHttpOrHttps("localhost/anything/with+path+param", json.uri());
212+
}
213+
214+
@Test
215+
public void getRequestWithQueryParametersAndAnything() {
216+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
217+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
218+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
219+
final HttpBinJSON json
220+
= testInterface.getAnything(getServerUri(false), "A", 15);
221+
222+
assertNotNull(json);
223+
assertMatchWithHttpOrHttps("localhost/anything?a=A&b=15", json.uri());
224+
}
225+
226+
@Test
227+
public void getRequestWithQueryParametersAndAnythingWithPercent20() {
228+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
229+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
230+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
231+
final HttpBinJSON json
232+
= testInterface.getAnything(getServerUri(false), "A%20Z", 15);
233+
234+
assertNotNull(json);
235+
assertMatchWithHttpOrHttps("localhost/anything?a=A%2520Z&b=15", json.uri());
236+
}
237+
238+
@Test
239+
public void getRequestWithQueryParametersAndAnythingWithEncodedWithPercent20() {
240+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
241+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
242+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
243+
final HttpBinJSON json
244+
= testInterface.getAnythingWithEncoded(getServerUri(false), "x%20y", 15);
245+
246+
assertNotNull(json);
247+
assertMatchWithHttpOrHttps("localhost/anything?a=x y&b=15", json.uri());
248+
}
249+
250+
@Test
251+
public void getRequestWithNullQueryParameter() {
252+
HttpPipeline pipeline = new HttpPipelineBuilder().httpClient(getHttpClient()).build();
253+
TestInterfaceClientImpl.TestInterfaceClientService testInterface =
254+
TestInterfaceClientImpl.TestInterfaceClientService.getNewInstance(pipeline);
255+
final HttpBinJSON json
256+
= testInterface.getAnything(getServerUri(false), null, 15);
257+
258+
assertNotNull(json);
259+
assertMatchWithHttpOrHttps("localhost/anything?b=15", json.uri());
260+
}
261+
118262
private HttpClient getHttpClient() {
119263
return new OkHttpHttpClientProvider().getSharedInstance();
120264
}
@@ -123,4 +267,20 @@ private String getServerUri(boolean secure) {
123267
return secure ? server.getHttpsUri() : server.getHttpUri();
124268
}
125269

270+
private static void assertMatchWithHttpOrHttps(String uri1, String uri2) {
271+
final String s1 = "http://" + uri1;
272+
273+
if (s1.equalsIgnoreCase(uri2)) {
274+
return;
275+
}
276+
277+
final String s2 = "https://" + uri1;
278+
279+
if (s2.equalsIgnoreCase(uri2)) {
280+
return;
281+
}
282+
283+
fail("'" + uri2 + "' does not match with '" + s1 + "' or '" + s2 + "'.");
284+
}
285+
126286
}

sdk/clientcore/annotation-processor/pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,5 +154,4 @@
154154
</plugin>
155155
</plugins>
156156
</build>
157-
158157
</project>

sdk/clientcore/annotation-processor/src/main/java/io/clientcore/annotation/processor/AnnotationProcessor.java

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ private HttpRequestContext createHttpRequestContext(ExecutableElement requestMet
150150
method.setExpectedStatusCodes(httpRequestInfo.expectedStatusCodes());
151151

152152
method.setMethodReturnType(requestMethod.getReturnType());
153-
boolean isEncoded = false;
154153
// Process parameters
155154
for (VariableElement param : requestMethod.getParameters()) {
156155
// Cache annotations for each parameter
@@ -163,22 +162,19 @@ private HttpRequestContext createHttpRequestContext(ExecutableElement requestMet
163162
// Switch based on annotations
164163
if (hostParam != null) {
165164
method.addSubstitution(
166-
new Substitution(hostParam.value(), param.getSimpleName().toString(), hostParam.encoded()));
165+
new Substitution(hostParam.value(), param.getSimpleName().toString(), !hostParam.encoded()));
167166
} else if (pathParam != null) {
168-
if (pathParam.encoded()) {
169-
isEncoded = true;
170-
}
171167
if (pathParam.value() == null) {
172168
throw new IllegalArgumentException(
173169
"Path parameter '" + param.getSimpleName().toString() + "' must not be null.");
174170
}
175171
method.addSubstitution(
176-
new Substitution(pathParam.value(), param.getSimpleName().toString(), pathParam.encoded()));
172+
new Substitution(pathParam.value(), param.getSimpleName().toString(), !pathParam.encoded()));
177173
} else if (headerParam != null) {
178174
method.addHeader(headerParam.value(), param.getSimpleName().toString());
179175
} else if (queryParam != null) {
180176
method.addQueryParam(queryParam.value(), param.getSimpleName().toString(),
181-
queryParam.multipleQueryParams());
177+
queryParam.multipleQueryParams(), !queryParam.encoded());
182178
} else if (bodyParam != null) {
183179
method.setBody(new HttpRequestContext.Body(bodyParam.value(), param.asType().toString(),
184180
param.getSimpleName().toString()));
@@ -191,24 +187,27 @@ private HttpRequestContext createHttpRequestContext(ExecutableElement requestMet
191187
}
192188

193189
// Pre-compute host substitutions
194-
method.setHost(getHost(templateInput, method, isEncoded));
190+
method.setHost(getHost(method));
195191

196192
return method;
197193
}
198194

199-
private static String getHost(TemplateInput templateInput, HttpRequestContext method, boolean isEncoded) {
200-
String rawHost;
201-
if (isEncoded) {
202-
rawHost = method.getPath();
203-
} else {
204-
String host = templateInput.getHost();
205-
String path = method.getPath();
206-
if (!host.endsWith("/") && !path.startsWith("/")) {
207-
rawHost = host + "/" + path;
195+
private static String getHost(HttpRequestContext method) {
196+
String path = method.getPath();
197+
// Set the path after host, concatenating the path segment in the host.
198+
if (path != null && !path.isEmpty() && !"/".equals(path)) {
199+
String hostPath = method.getHost();
200+
if (hostPath == null || hostPath.isEmpty() || "/".equals(hostPath) || path.contains("://")) {
201+
method.setPath(path);
208202
} else {
209-
rawHost = host + path;
203+
if (path.startsWith("/")) {
204+
method.setPath(hostPath + path);
205+
} else {
206+
method.setPath(hostPath + "/" + path);
207+
}
210208
}
211209
}
212-
return PathBuilder.buildPath(rawHost, method);
210+
211+
return PathBuilder.buildPath(method.getPath(), method);
213212
}
214213
}

0 commit comments

Comments
 (0)