Skip to content
This repository was archived by the owner on Dec 20, 2025. It is now read-only.

Commit bdf8922

Browse files
fix(retrofit2): fix retrofit2 issues (#1875)
* test(retrofit2): add test to demonstrate the following error when QueryMap is used without generics: `java.lang.IllegalArgumentException: Map must include generic types (e.g., Map<String, String>)` * fix(retrofit2): fix non-generics QueryMap which causes the following error: `java.lang.IllegalArgumentException: Map must include generic types (e.g., Map<String, String>)` * fix(retrofit2): fix `A @path parameter must not come after a @Query` error
1 parent 381f554 commit bdf8922

File tree

5 files changed

+95
-7
lines changed

5 files changed

+95
-7
lines changed

gate-core/gate-core.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ dependencies {
3838
implementation "io.cloudevents:cloudevents-http-basic:2.5.0"
3939
testImplementation "com.squareup.retrofit2:retrofit-mock"
4040
testImplementation "com.squareup.retrofit2:converter-jackson"
41+
testImplementation "com.github.tomakehurst:wiremock-jre8-standalone"
4142
}
4243

4344
sourceSets {

gate-core/src/main/java/com/netflix/spinnaker/gate/services/internal/ClouddriverService.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ Call<List<Map>> findImages(
227227
@Query("region") String region,
228228
@Query("account") String account,
229229
@Query("count") Integer count,
230-
@QueryMap Map additionalFilters);
230+
@QueryMap Map<String, String> additionalFilters);
231231

232232
@Headers("Accept: application/json")
233233
@GET("/{provider}/images/tags")
@@ -244,7 +244,7 @@ Call<List<Map>> search(
244244
@Query("platform") String platform,
245245
@Query("pageSize") Integer size,
246246
@Query("page") Integer offset,
247-
@QueryMap Map filters);
247+
@QueryMap Map<String, String> filters);
248248

249249
@GET("/securityGroups")
250250
Call<Map> getSecurityGroups();
@@ -303,7 +303,7 @@ Call<Map> getCloudMetricStatistics(
303303
@QueryMap Map<String, String> filters);
304304

305305
@GET("/tags")
306-
Call<List<Map>> listEntityTags(@QueryMap Map allParameters);
306+
Call<List<Map>> listEntityTags(@QueryMap Map<String, Object> allParameters);
307307

308308
@GET("/tags/{id}")
309309
Call<Map> getEntityTags(@Path("id") String id);
@@ -380,9 +380,9 @@ Call<List<Map>> getServerGroupEvents(
380380

381381
@GET("/servicebroker/{account}/services")
382382
Call<List<Map>> listServices(
383+
@Path(value = "account") String account,
383384
@Query(value = "cloudProvider") String cloudProvider,
384-
@Query(value = "region") String region,
385-
@Path(value = "account") String account);
385+
@Query(value = "region") String region);
386386

387387
@GET("/servicebroker/{account}/serviceInstance")
388388
Call<Map> getServiceInstance(
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2025 OpsMx, Inc.
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 com.netflix.spinnaker.gate.services.internal;
18+
19+
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
20+
import static com.github.tomakehurst.wiremock.client.WireMock.get;
21+
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
22+
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
23+
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
24+
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
25+
26+
import com.github.tomakehurst.wiremock.WireMockServer;
27+
import com.github.tomakehurst.wiremock.client.WireMock;
28+
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
29+
import com.netflix.spinnaker.kork.retrofit.ErrorHandlingExecutorCallAdapterFactory;
30+
import com.netflix.spinnaker.kork.retrofit.Retrofit2SyncCall;
31+
import java.util.Map;
32+
import okhttp3.OkHttpClient;
33+
import org.junit.jupiter.api.AfterEach;
34+
import org.junit.jupiter.api.BeforeEach;
35+
import org.junit.jupiter.api.Test;
36+
import retrofit2.Retrofit;
37+
import retrofit2.converter.jackson.JacksonConverterFactory;
38+
39+
public class ClouddriverServiceTest {
40+
WireMockServer wireMockServer;
41+
int port;
42+
ClouddriverService clouddriverService;
43+
String baseUrl = "http://localhost:PORT";
44+
45+
@BeforeEach
46+
void setUp() {
47+
wireMockServer = new WireMockServer(WireMockConfiguration.options().dynamicPort());
48+
wireMockServer.start();
49+
port = wireMockServer.port();
50+
WireMock.configureFor("localhost", port);
51+
52+
baseUrl = baseUrl.replaceFirst("PORT", String.valueOf(port));
53+
54+
clouddriverService =
55+
new Retrofit.Builder()
56+
.baseUrl(baseUrl)
57+
.client(new OkHttpClient())
58+
.addCallAdapterFactory(ErrorHandlingExecutorCallAdapterFactory.getInstance())
59+
.addConverterFactory(JacksonConverterFactory.create())
60+
.build()
61+
.create(ClouddriverService.class);
62+
}
63+
64+
@AfterEach
65+
void tearDown() {
66+
wireMockServer.stop();
67+
}
68+
69+
@Test
70+
void testClouddriverService_withQueryMap() {
71+
stubFor(
72+
get(urlEqualTo("/search?q=app1&type=securityGroups&platform=aws&pageSize=500&page=1"))
73+
.willReturn(
74+
aResponse()
75+
.withStatus(200)
76+
.withBody(
77+
"[{\"pageNumber\":1,\"pageSize\":500,\"platform\":\"aws\",\"query\":\"app1\",\"results\":[],\"totalMatches\":0}]")));
78+
79+
Retrofit2SyncCall.execute(
80+
clouddriverService.search("app1", "securityGroups", "aws", 500, 1, Map.of()));
81+
82+
verify(
83+
1,
84+
getRequestedFor(
85+
urlEqualTo("/search?q=app1&type=securityGroups&platform=aws&pageSize=500&page=1")));
86+
}
87+
}

gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ImageService.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class ImageService {
3737
Retrofit2SyncCall.execute(clouddriverServiceSelector.select().getImageDetails(provider, account, region, imageId))
3838
}
3939

40-
List<Map> search(String provider, String query, String region, String account, Integer count, Map<String, Object> additionalFilters, String selectorKey) {
40+
List<Map> search(String provider, String query, String region, String account, Integer count, Map<String, String> additionalFilters, String selectorKey) {
4141
Retrofit2SyncCall.execute(clouddriverServiceSelector.select().findImages(provider, query, region, account, count, additionalFilters))
4242
}
4343

gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ServiceBrokerService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class ServiceBrokerService {
3232

3333
public List<Map> listServices(String cloudProvider, String region, String account) {
3434
return Retrofit2SyncCall.execute(
35-
this.clouddriverService.listServices(cloudProvider, region, account));
35+
this.clouddriverService.listServices(account, cloudProvider, region));
3636
}
3737

3838
public Map getServiceInstance(

0 commit comments

Comments
 (0)