Skip to content

Commit

Permalink
rest-template: allow array parameters in path using collectionFormat (#…
Browse files Browse the repository at this point in the history
…2177)

* 2125: java-resttemplate: Support collection formats in pathParams

* run ./bin/java-petstore-resttemplate.sh
run ./bin/java-petstore-resttemplate-withxml.sh
  • Loading branch information
victor-xplore authored and wing328 committed Feb 19, 2019
1 parent 61b6f19 commit 1dadd45
Show file tree
Hide file tree
Showing 16 changed files with 227 additions and 161 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@ public class ApiClient {
return StringUtils.collectionToDelimitedString(collection, separator);
}
}

private boolean debugging = false;

private HttpHeaders defaultHeaders = new HttpHeaders();

private String basePath = "{{basePath}}";

private RestTemplate restTemplate;
Expand All @@ -99,20 +99,20 @@ public class ApiClient {

private HttpStatus statusCode;
private MultiValueMap<String, String> responseHeaders;

private DateFormat dateFormat;

public ApiClient() {
this.restTemplate = buildRestTemplate();
init();
}

@Autowired
public ApiClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
init();
}

protected void init() {
// Use RFC3339 format for date and datetime.
// See http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14
Expand All @@ -132,7 +132,7 @@ public class ApiClient {
// Prevent the authentications from being modified.
authentications = Collections.unmodifiableMap(authentications);
}

/**
* Get the current base path
* @return String the base path
Expand Down Expand Up @@ -281,7 +281,7 @@ public class ApiClient {
defaultHeaders.add(name, value);
return this;
}

public void setDebugging(boolean debugging) {
List<ClientHttpRequestInterceptor> currentInterceptors = this.restTemplate.getInterceptors();
if(debugging) {
Expand Down Expand Up @@ -339,7 +339,7 @@ public class ApiClient {
{{/threetenbp}}
return this;
}

/**
* Parse the given string into Date object.
*/
Expand Down Expand Up @@ -382,6 +382,28 @@ public class ApiClient {
}
}

/**
* Formats the specified collection path parameter to a string value.
*
* @param collectionFormat The collection format of the parameter.
* @param value The value of the parameter.
* @return String representation of the parameter
*/
public String collectionPathParameterToString(CollectionFormat collectionFormat, Collection<? extends CharSequence> values) {
// create the value based on the collection format
if (CollectionFormat.MULTI.equals(collectionFormat)) {
// not valid for path params
return parameterToString(values);
}

// collectionFormat is assumed to be "csv" by default
if(collectionFormat == null) {
collectionFormat = CollectionFormat.CSV;
}

return collectionFormat.collectionToString(values);
}

/**
* Converts a parameter to a {@link MultiValueMap} for use in REST requests
* @param collectionFormat The format to convert to
Expand Down Expand Up @@ -531,7 +553,7 @@ public class ApiClient {
*/
public <T> T invokeAPI(String path, HttpMethod method, MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams, MultiValueMap<String, Object> formParams, List<MediaType> accept, MediaType contentType, String[] authNames, ParameterizedTypeReference<T> returnType) throws RestClientException {
updateParamsForAuth(authNames, queryParams, headerParams);
final UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(basePath).path(path);
if (queryParams != null) {
//encode the query parameters in case they contain unsafe characters
Expand All @@ -548,29 +570,29 @@ public class ApiClient {
}
builder.queryParams(queryParams);
}

URI uri;
try {
uri = new URI(builder.build().toUriString());
} catch(URISyntaxException ex) {
throw new RestClientException("Could not build URL: " + builder.toUriString(), ex);
}

final BodyBuilder requestBuilder = RequestEntity.method(method, uri);
if(accept != null) {
requestBuilder.accept(accept.toArray(new MediaType[accept.size()]));
}
if(contentType != null) {
requestBuilder.contentType(contentType);
}

addHeadersToRequest(headerParams, requestBuilder);
addHeadersToRequest(defaultHeaders, requestBuilder);

RequestEntity<Object> requestEntity = requestBuilder.body(selectBody(body, formParams, contentType));

ResponseEntity<T> responseEntity = restTemplate.exchange(requestEntity, returnType);

statusCode = responseEntity.getStatusCode();
responseHeaders = responseEntity.getHeaders();

Expand All @@ -586,7 +608,7 @@ public class ApiClient {
throw new RestClientException("API returned " + statusCode + " and it wasn't handled by the RestTemplate error handler");
}
}

/**
* Add headers to the request that is being built
* @param headers The headers to add
Expand Down Expand Up @@ -649,7 +671,7 @@ public class ApiClient {
auth.applyToParams(queryParams, headerParams);
}
}

private class ApiClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
private final Log log = LogFactory.getLog(ApiClientHttpRequestInterceptor.class);
Expand Down Expand Up @@ -688,7 +710,7 @@ public class ApiClient {
builder.setLength(builder.length() - 1); // Get rid of trailing comma
return builder.toString();
}

private String bodyToString(InputStream body) throws IOException {
StringBuilder builder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(body, StandardCharsets.UTF_8));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,20 @@ public class {{classname}} {
{{/required}}{{/allParams}}{{#hasPathParams}}
// create path and map variables
final Map<String, Object> uriVariables = new HashMap<String, Object>();{{#pathParams}}
uriVariables.put("{{baseName}}", {{{paramName}}});{{/pathParams}}{{/hasPathParams}}
uriVariables.put("{{baseName}}", {{#collectionFormat}}apiClient.collectionPathParameterToString(ApiClient.CollectionFormat.valueOf("{{{collectionFormat}}}".toUpperCase()), {{{paramName}}}){{/collectionFormat}}{{^collectionFormat}}{{{paramName}}}{{/collectionFormat}});{{/pathParams}}{{/hasPathParams}}
String {{localVariablePrefix}}path = UriComponentsBuilder.fromPath("{{{path}}}"){{#hasPathParams}}.buildAndExpand(uriVariables){{/hasPathParams}}{{^hasPathParams}}.build(){{/hasPathParams}}.toUriString();

final MultiValueMap<String, String> {{localVariablePrefix}}queryParams = new LinkedMultiValueMap<String, String>();
final HttpHeaders {{localVariablePrefix}}headerParams = new HttpHeaders();
final MultiValueMap<String, Object> {{localVariablePrefix}}formParams = new LinkedMultiValueMap<String, Object>();{{#hasQueryParams}}

{{#queryParams}}{{localVariablePrefix}}queryParams.putAll({{localVariablePrefix}}apiClient.parameterToMultiValueMap({{#collectionFormat}}ApiClient.CollectionFormat.valueOf("{{{collectionFormat}}}".toUpperCase(Locale.ROOT)){{/collectionFormat}}{{^collectionFormat}}null{{/collectionFormat}}, "{{baseName}}", {{paramName}}));{{#hasMore}}
{{/hasMore}}{{/queryParams}}{{/hasQueryParams}}{{#hasHeaderParams}}

{{#headerParams}}if ({{paramName}} != null)
{{localVariablePrefix}}headerParams.add("{{baseName}}", {{localVariablePrefix}}apiClient.parameterToString({{paramName}}));{{#hasMore}}
{{/hasMore}}{{/headerParams}}{{/hasHeaderParams}}{{#hasFormParams}}

{{#formParams}}if ({{paramName}} != null)
{{localVariablePrefix}}formParams.add("{{baseName}}", {{#isFile}}new FileSystemResource({{paramName}}){{/isFile}}{{^isFile}}{{paramName}}{{/isFile}});{{#hasMore}}
{{/hasMore}}{{/formParams}}{{/hasFormParams}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ private String collectionToString(Collection<? extends CharSequence> collection)
return StringUtils.collectionToDelimitedString(collection, separator);
}
}

private boolean debugging = false;

private HttpHeaders defaultHeaders = new HttpHeaders();

private String basePath = "http://petstore.swagger.io:80/v2";

private RestTemplate restTemplate;
Expand All @@ -91,20 +91,20 @@ private String collectionToString(Collection<? extends CharSequence> collection)

private HttpStatus statusCode;
private MultiValueMap<String, String> responseHeaders;

private DateFormat dateFormat;

public ApiClient() {
this.restTemplate = buildRestTemplate();
init();
}

@Autowired
public ApiClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
init();
}

protected void init() {
// Use RFC3339 format for date and datetime.
// See http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14
Expand All @@ -125,7 +125,7 @@ protected void init() {
// Prevent the authentications from being modified.
authentications = Collections.unmodifiableMap(authentications);
}

/**
* Get the current base path
* @return String the base path
Expand Down Expand Up @@ -272,7 +272,7 @@ public ApiClient addDefaultHeader(String name, String value) {
defaultHeaders.add(name, value);
return this;
}

public void setDebugging(boolean debugging) {
List<ClientHttpRequestInterceptor> currentInterceptors = this.restTemplate.getInterceptors();
if(debugging) {
Expand Down Expand Up @@ -328,7 +328,7 @@ public ApiClient setDateFormat(DateFormat dateFormat) {
}
return this;
}

/**
* Parse the given string into Date object.
*/
Expand Down Expand Up @@ -371,6 +371,28 @@ public String parameterToString(Object param) {
}
}

/**
* Formats the specified collection path parameter to a string value.
*
* @param collectionFormat The collection format of the parameter.
* @param value The value of the parameter.
* @return String representation of the parameter
*/
public String collectionPathParameterToString(CollectionFormat collectionFormat, Collection<? extends CharSequence> values) {
// create the value based on the collection format
if (CollectionFormat.MULTI.equals(collectionFormat)) {
// not valid for path params
return parameterToString(values);
}

// collectionFormat is assumed to be "csv" by default
if(collectionFormat == null) {
collectionFormat = CollectionFormat.CSV;
}

return collectionFormat.collectionToString(values);
}

/**
* Converts a parameter to a {@link MultiValueMap} for use in REST requests
* @param collectionFormat The format to convert to
Expand Down Expand Up @@ -520,7 +542,7 @@ protected Object selectBody(Object obj, MultiValueMap<String, Object> formParams
*/
public <T> T invokeAPI(String path, HttpMethod method, MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams, MultiValueMap<String, Object> formParams, List<MediaType> accept, MediaType contentType, String[] authNames, ParameterizedTypeReference<T> returnType) throws RestClientException {
updateParamsForAuth(authNames, queryParams, headerParams);

final UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(basePath).path(path);
if (queryParams != null) {
//encode the query parameters in case they contain unsafe characters
Expand All @@ -537,29 +559,29 @@ public <T> T invokeAPI(String path, HttpMethod method, MultiValueMap<String, Str
}
builder.queryParams(queryParams);
}

URI uri;
try {
uri = new URI(builder.build().toUriString());
} catch(URISyntaxException ex) {
throw new RestClientException("Could not build URL: " + builder.toUriString(), ex);
}

final BodyBuilder requestBuilder = RequestEntity.method(method, uri);
if(accept != null) {
requestBuilder.accept(accept.toArray(new MediaType[accept.size()]));
}
if(contentType != null) {
requestBuilder.contentType(contentType);
}

addHeadersToRequest(headerParams, requestBuilder);
addHeadersToRequest(defaultHeaders, requestBuilder);

RequestEntity<Object> requestEntity = requestBuilder.body(selectBody(body, formParams, contentType));

ResponseEntity<T> responseEntity = restTemplate.exchange(requestEntity, returnType);

statusCode = responseEntity.getStatusCode();
responseHeaders = responseEntity.getHeaders();

Expand All @@ -575,7 +597,7 @@ public <T> T invokeAPI(String path, HttpMethod method, MultiValueMap<String, Str
throw new RestClientException("API returned " + statusCode + " and it wasn't handled by the RestTemplate error handler");
}
}

/**
* Add headers to the request that is being built
* @param headers The headers to add
Expand Down Expand Up @@ -636,7 +658,7 @@ private void updateParamsForAuth(String[] authNames, MultiValueMap<String, Strin
auth.applyToParams(queryParams, headerParams);
}
}

private class ApiClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
private final Log log = LogFactory.getLog(ApiClientHttpRequestInterceptor.class);

Expand Down Expand Up @@ -675,7 +697,7 @@ private String headersToString(HttpHeaders headers) {
builder.setLength(builder.length() - 1); // Get rid of trailing comma
return builder.toString();
}

private String bodyToString(InputStream body) throws IOException {
StringBuilder builder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(body, StandardCharsets.UTF_8));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public Client call123testSpecialTags(Client body) throws RestClientException {
}

String path = UriComponentsBuilder.fromPath("/another-fake/dummy").build().toUriString();

final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
final HttpHeaders headerParams = new HttpHeaders();
final MultiValueMap<String, Object> formParams = new LinkedMultiValueMap<String, Object>();
Expand Down
Loading

0 comments on commit 1dadd45

Please sign in to comment.