github.com/phrase/openapi@v0.0.0-20240514140800-49e8a106740e/openapi-generator/templates/java/libraries/resttemplate/ApiClient.mustache (about)

     1  package {{invokerPackage}};
     2  
     3  {{#withXml}}
     4  import com.fasterxml.jackson.dataformat.xml.XmlMapper;
     5  import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
     6  {{/withXml}}
     7  import org.apache.commons.logging.Log;
     8  import org.apache.commons.logging.LogFactory;
     9  import org.springframework.beans.factory.annotation.Autowired;
    10  import org.springframework.core.ParameterizedTypeReference;
    11  import org.springframework.http.HttpHeaders;
    12  import org.springframework.http.HttpMethod;
    13  import org.springframework.http.HttpRequest;
    14  import org.springframework.http.HttpStatus;
    15  import org.springframework.http.InvalidMediaTypeException;
    16  import org.springframework.http.MediaType;
    17  import org.springframework.http.RequestEntity;
    18  import org.springframework.http.RequestEntity.BodyBuilder;
    19  import org.springframework.http.ResponseEntity;
    20  import org.springframework.http.client.BufferingClientHttpRequestFactory;
    21  import org.springframework.http.client.ClientHttpRequestExecution;
    22  import org.springframework.http.client.ClientHttpRequestInterceptor;
    23  import org.springframework.http.client.ClientHttpResponse;
    24  {{#withXml}}
    25  import org.springframework.http.converter.HttpMessageConverter;
    26  import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
    27  import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
    28  {{/withXml}}
    29  import org.springframework.stereotype.Component;
    30  import org.springframework.util.LinkedMultiValueMap;
    31  import org.springframework.util.MultiValueMap;
    32  import org.springframework.util.StringUtils;
    33  import org.springframework.web.client.RestClientException;
    34  import org.springframework.web.client.RestTemplate;
    35  import org.springframework.web.util.UriComponentsBuilder;
    36  {{#threetenbp}}
    37  import org.threeten.bp.*;
    38  import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule;
    39  import org.springframework.http.converter.HttpMessageConverter;
    40  import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
    41  import com.fasterxml.jackson.databind.ObjectMapper;
    42  import org.openapitools.jackson.nullable.JsonNullableModule;
    43  {{/threetenbp}}
    44  
    45  import java.io.BufferedReader;
    46  import java.io.IOException;
    47  import java.io.InputStream;
    48  import java.io.InputStreamReader;
    49  import java.io.UnsupportedEncodingException;
    50  import java.net.URI;
    51  import java.net.URISyntaxException;
    52  import java.net.URLEncoder;
    53  import java.nio.charset.StandardCharsets;
    54  import java.text.DateFormat;
    55  import java.text.ParseException;
    56  import java.util.Arrays;
    57  import java.util.ArrayList;
    58  import java.util.Collection;
    59  import java.util.Collections;
    60  import java.util.Date;
    61  import java.util.HashMap;
    62  import java.util.Iterator;
    63  import java.util.List;
    64  import java.util.Map;
    65  import java.util.Map.Entry;
    66  import java.util.TimeZone;
    67  
    68  import {{invokerPackage}}.auth.Authentication;
    69  import {{invokerPackage}}.auth.HttpBasicAuth;
    70  import {{invokerPackage}}.auth.HttpBearerAuth;
    71  import {{invokerPackage}}.auth.ApiKeyAuth;
    72  {{#hasOAuthMethods}}
    73  import {{invokerPackage}}.auth.OAuth;
    74  {{/hasOAuthMethods}}
    75  
    76  {{>generatedAnnotation}}
    77  @Component("{{invokerPackage}}.ApiClient")
    78  public class ApiClient {
    79      public enum CollectionFormat {
    80          CSV(","), TSV("\t"), SSV(" "), PIPES("|"), MULTI(null);
    81  
    82          private final String separator;
    83          private CollectionFormat(String separator) {
    84              this.separator = separator;
    85          }
    86  
    87          private String collectionToString(Collection<?> collection) {
    88              return StringUtils.collectionToDelimitedString(collection, separator);
    89          }
    90      }
    91  
    92      private boolean debugging = false;
    93  
    94      private HttpHeaders defaultHeaders = new HttpHeaders();
    95      private MultiValueMap<String, String> defaultCookies = new LinkedMultiValueMap<String, String>();
    96  
    97      private String basePath = "{{basePath}}";
    98  
    99      private RestTemplate restTemplate;
   100  
   101      private Map<String, Authentication> authentications;
   102  
   103      private DateFormat dateFormat;
   104  
   105      public ApiClient() {
   106          this.restTemplate = buildRestTemplate();
   107          init();
   108      }
   109  
   110      @Autowired
   111      public ApiClient(RestTemplate restTemplate) {
   112          this.restTemplate = restTemplate;
   113          init();
   114      }
   115  
   116      protected void init() {
   117          // Use RFC3339 format for date and datetime.
   118          // See http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14
   119          this.dateFormat = new RFC3339DateFormat();
   120  
   121          // Use UTC as the default time zone.
   122          this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
   123  
   124          // Set default User-Agent.
   125          setUserAgent("Java-SDK");
   126  
   127          // Setup authentications (key: authentication name, value: authentication).
   128          authentications = new HashMap<String, Authentication>();{{#authMethods}}{{#isBasic}}{{#isBasicBasic}}
   129          authentications.put("{{name}}", new HttpBasicAuth());{{/isBasicBasic}}{{^isBasicBasic}}
   130          authentications.put("{{name}}", new HttpBearerAuth("{{scheme}}"));{{/isBasicBasic}}{{/isBasic}}{{#isApiKey}}
   131          authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}}
   132          authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}}
   133          // Prevent the authentications from being modified.
   134          authentications = Collections.unmodifiableMap(authentications);
   135      }
   136  
   137      /**
   138       * Get the current base path
   139       * @return String the base path
   140       */
   141      public String getBasePath() {
   142          return basePath;
   143      }
   144  
   145      /**
   146       * Set the base path, which should include the host
   147       * @param basePath the base path
   148       * @return ApiClient this client
   149       */
   150      public ApiClient setBasePath(String basePath) {
   151          this.basePath = basePath;
   152          return this;
   153      }
   154  
   155      /**
   156       * Get authentications (key: authentication name, value: authentication).
   157       * @return Map the currently configured authentication types
   158       */
   159      public Map<String, Authentication> getAuthentications() {
   160          return authentications;
   161      }
   162  
   163      /**
   164       * Get authentication for the given name.
   165       *
   166       * @param authName The authentication name
   167       * @return The authentication, null if not found
   168       */
   169      public Authentication getAuthentication(String authName) {
   170          return authentications.get(authName);
   171      }
   172  
   173      /**
   174       * Helper method to set token for HTTP bearer authentication.
   175       * @param bearerToken the token
   176       */
   177      public void setBearerToken(String bearerToken) {
   178        for (Authentication auth : authentications.values()) {
   179          if (auth instanceof HttpBearerAuth) {
   180            ((HttpBearerAuth) auth).setBearerToken(bearerToken);
   181            return;
   182          }
   183        }
   184        throw new RuntimeException("No Bearer authentication configured!");
   185      }
   186  
   187      /**
   188       * Helper method to set username for the first HTTP basic authentication.
   189       * @param username the username
   190       */
   191      public void setUsername(String username) {
   192          for (Authentication auth : authentications.values()) {
   193              if (auth instanceof HttpBasicAuth) {
   194                  ((HttpBasicAuth) auth).setUsername(username);
   195                  return;
   196              }
   197          }
   198          throw new RuntimeException("No HTTP basic authentication configured!");
   199      }
   200  
   201      /**
   202       * Helper method to set password for the first HTTP basic authentication.
   203       * @param password the password
   204       */
   205      public void setPassword(String password) {
   206          for (Authentication auth : authentications.values()) {
   207              if (auth instanceof HttpBasicAuth) {
   208                  ((HttpBasicAuth) auth).setPassword(password);
   209                  return;
   210              }
   211          }
   212          throw new RuntimeException("No HTTP basic authentication configured!");
   213      }
   214  
   215      /**
   216       * Helper method to set API key value for the first API key authentication.
   217       * @param apiKey the API key
   218       */
   219      public void setApiKey(String apiKey) {
   220          for (Authentication auth : authentications.values()) {
   221              if (auth instanceof ApiKeyAuth) {
   222                  ((ApiKeyAuth) auth).setApiKey(apiKey);
   223                  return;
   224              }
   225          }
   226          throw new RuntimeException("No API key authentication configured!");
   227      }
   228  
   229      /**
   230       * Helper method to set API key prefix for the first API key authentication.
   231       * @param apiKeyPrefix the API key prefix
   232       */
   233      public void setApiKeyPrefix(String apiKeyPrefix) {
   234          for (Authentication auth : authentications.values()) {
   235              if (auth instanceof ApiKeyAuth) {
   236                  ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix);
   237                  return;
   238              }
   239          }
   240          throw new RuntimeException("No API key authentication configured!");
   241      }
   242  
   243      {{#hasOAuthMethods}}
   244      /**
   245       * Helper method to set access token for the first OAuth2 authentication.
   246       * @param accessToken the access token
   247       */
   248      public void setAccessToken(String accessToken) {
   249          for (Authentication auth : authentications.values()) {
   250              if (auth instanceof OAuth) {
   251                  ((OAuth) auth).setAccessToken(accessToken);
   252                  return;
   253              }
   254          }
   255          throw new RuntimeException("No OAuth2 authentication configured!");
   256      }
   257  
   258      {{/hasOAuthMethods}}
   259      /**
   260       * Set the User-Agent header's value (by adding to the default header map).
   261       * @param userAgent the user agent string
   262       * @return ApiClient this client
   263       */
   264      public ApiClient setUserAgent(String userAgent) {
   265          addDefaultHeader("User-Agent", userAgent);
   266          return this;
   267      }
   268  
   269      /**
   270       * Add a default header.
   271       *
   272       * @param name The header's name
   273       * @param value The header's value
   274       * @return ApiClient this client
   275       */
   276      public ApiClient addDefaultHeader(String name, String value) {
   277          if (defaultHeaders.containsKey(name)) {
   278              defaultHeaders.remove(name);
   279          }
   280          defaultHeaders.add(name, value);
   281          return this;
   282      }
   283  
   284      /**
   285       * Add a default cookie.
   286       *
   287       * @param name The cookie's name
   288       * @param value The cookie's value
   289       * @return ApiClient this client
   290       */
   291      public ApiClient addDefaultCookie(String name, String value) {
   292          if (defaultCookies.containsKey(name)) {
   293              defaultCookies.remove(name);
   294          }
   295          defaultCookies.add(name, value);
   296          return this;
   297      }
   298  
   299      public void setDebugging(boolean debugging) {
   300          List<ClientHttpRequestInterceptor> currentInterceptors = this.restTemplate.getInterceptors();
   301          if(debugging) {
   302              if (currentInterceptors == null) {
   303                  currentInterceptors = new ArrayList<ClientHttpRequestInterceptor>();
   304              }
   305              ClientHttpRequestInterceptor interceptor = new ApiClientHttpRequestInterceptor();
   306              currentInterceptors.add(interceptor);
   307              this.restTemplate.setInterceptors(currentInterceptors);
   308          } else {
   309              if (currentInterceptors != null && !currentInterceptors.isEmpty()) {
   310                  Iterator<ClientHttpRequestInterceptor> iter = currentInterceptors.iterator();
   311                  while (iter.hasNext()) {
   312                      ClientHttpRequestInterceptor interceptor = iter.next();
   313                      if (interceptor instanceof ApiClientHttpRequestInterceptor) {
   314                          iter.remove();
   315                      }
   316                  }
   317                  this.restTemplate.setInterceptors(currentInterceptors);
   318              }
   319          }
   320          this.debugging = debugging;
   321      }
   322  
   323      /**
   324       * Check that whether debugging is enabled for this API client.
   325       * @return boolean true if this client is enabled for debugging, false otherwise
   326       */
   327      public boolean isDebugging() {
   328          return debugging;
   329      }
   330  
   331      /**
   332       * Get the date format used to parse/format date parameters.
   333       * @return DateFormat format
   334       */
   335      public DateFormat getDateFormat() {
   336          return dateFormat;
   337      }
   338  
   339      /**
   340       * Set the date format used to parse/format date parameters.
   341       * @param dateFormat Date format
   342       * @return API client
   343       */
   344      public ApiClient setDateFormat(DateFormat dateFormat) {
   345          this.dateFormat = dateFormat;
   346          {{#threetenbp}}
   347          for(HttpMessageConverter converter:restTemplate.getMessageConverters()){
   348              if(converter instanceof AbstractJackson2HttpMessageConverter){
   349                  ObjectMapper mapper = ((AbstractJackson2HttpMessageConverter)converter).getObjectMapper();
   350                  mapper.setDateFormat(dateFormat);
   351              }
   352          }
   353          {{/threetenbp}}
   354          return this;
   355      }
   356  
   357      /**
   358       * Parse the given string into Date object.
   359       * @param str the string to parse
   360       * @return the Date parsed from the string
   361       */
   362      public Date parseDate(String str) {
   363          try {
   364              return dateFormat.parse(str);
   365          } catch (ParseException e) {
   366              throw new RuntimeException(e);
   367          }
   368      }
   369  
   370      /**
   371       * Format the given Date object into string.
   372       * @param date the date to format
   373       * @return the formatted date as string
   374       */
   375      public String formatDate(Date date) {
   376          return dateFormat.format(date);
   377      }
   378  
   379      /**
   380       * Format the given parameter object into string.
   381       * @param param the object to convert
   382       * @return String the parameter represented as a String
   383       */
   384      public String parameterToString(Object param) {
   385          if (param == null) {
   386              return "";
   387          } else if (param instanceof Date) {
   388              return formatDate( (Date) param);
   389          } else if (param instanceof Collection) {
   390              StringBuilder b = new StringBuilder();
   391              for(Object o : (Collection<?>) param) {
   392                  if(b.length() > 0) {
   393                      b.append(",");
   394                  }
   395                  b.append(String.valueOf(o));
   396              }
   397              return b.toString();
   398          } else {
   399              return String.valueOf(param);
   400          }
   401      }
   402  
   403      /**
   404      * Formats the specified collection path parameter to a string value.
   405      *
   406      * @param collectionFormat The collection format of the parameter.
   407      * @param values The values of the parameter.
   408      * @return String representation of the parameter
   409      */
   410      public String collectionPathParameterToString(CollectionFormat collectionFormat, Collection<?> values) {
   411          // create the value based on the collection format
   412          if (CollectionFormat.MULTI.equals(collectionFormat)) {
   413              // not valid for path params
   414              return parameterToString(values);
   415          }
   416  
   417          // collectionFormat is assumed to be "csv" by default
   418          if(collectionFormat == null) {
   419              collectionFormat = CollectionFormat.CSV;
   420          }
   421  
   422          return collectionFormat.collectionToString(values);
   423      }
   424  
   425      /**
   426       * Converts a parameter to a {@link MultiValueMap} for use in REST requests
   427       * @param collectionFormat The format to convert to
   428       * @param name The name of the parameter
   429       * @param value The parameter's value
   430       * @return a Map containing the String value(s) of the input parameter
   431       */
   432      public MultiValueMap<String, String> parameterToMultiValueMap(CollectionFormat collectionFormat, String name, Object value) {
   433          final MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
   434  
   435          if (name == null || name.isEmpty() || value == null) {
   436              return params;
   437          }
   438  
   439          if(collectionFormat == null) {
   440              collectionFormat = CollectionFormat.CSV;
   441          }
   442  
   443          Collection<?> valueCollection = null;
   444          if (value instanceof Collection) {
   445              valueCollection = (Collection<?>) value;
   446          } else {
   447              params.add(name, parameterToString(value));
   448              return params;
   449          }
   450  
   451          if (valueCollection.isEmpty()){
   452              return params;
   453          }
   454  
   455          if (collectionFormat.equals(CollectionFormat.MULTI)) {
   456              for (Object item : valueCollection) {
   457                  params.add(name, parameterToString(item));
   458              }
   459              return params;
   460          }
   461  
   462          List<String> values = new ArrayList<String>();
   463          for(Object o : valueCollection) {
   464              values.add(parameterToString(o));
   465          }
   466          params.add(name, collectionFormat.collectionToString(values));
   467  
   468          return params;
   469      }
   470  
   471      /**
   472      * Check if the given {@code String} is a JSON MIME.
   473      * @param mediaType the input MediaType
   474      * @return boolean true if the MediaType represents JSON, false otherwise
   475      */
   476      public boolean isJsonMime(String mediaType) {
   477          // "* / *" is default to JSON
   478          if ("*/*".equals(mediaType)) {
   479              return true;
   480          }
   481  
   482          try {
   483              return isJsonMime(MediaType.parseMediaType(mediaType));
   484          } catch (InvalidMediaTypeException e) {
   485          }
   486          return false;
   487      }
   488  
   489      /**
   490       * Check if the given MIME is a JSON MIME.
   491       * JSON MIME examples:
   492       *     application/json
   493       *     application/json; charset=UTF8
   494       *     APPLICATION/JSON
   495       * @param mediaType the input MediaType
   496       * @return boolean true if the MediaType represents JSON, false otherwise
   497       */
   498      public boolean isJsonMime(MediaType mediaType) {
   499          return mediaType != null && (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType) || mediaType.getSubtype().matches("^.*\\+json[;]?\\s*$"));
   500      }
   501  
   502      /**
   503       * Select the Accept header's value from the given accepts array:
   504       *     if JSON exists in the given array, use it;
   505       *     otherwise use all of them (joining into a string)
   506       *
   507       * @param accepts The accepts array to select from
   508       * @return List The list of MediaTypes to use for the Accept header
   509       */
   510      public List<MediaType> selectHeaderAccept(String[] accepts) {
   511          if (accepts.length == 0) {
   512              return null;
   513          }
   514          for (String accept : accepts) {
   515              MediaType mediaType = MediaType.parseMediaType(accept);
   516              if (isJsonMime(mediaType)) {
   517                  return Collections.singletonList(mediaType);
   518              }
   519          }
   520          return MediaType.parseMediaTypes(StringUtils.arrayToCommaDelimitedString(accepts));
   521      }
   522  
   523      /**
   524       * Select the Content-Type header's value from the given array:
   525       *     if JSON exists in the given array, use it;
   526       *     otherwise use the first one of the array.
   527       *
   528       * @param contentTypes The Content-Type array to select from
   529       * @return MediaType The Content-Type header to use. If the given array is empty, JSON will be used.
   530       */
   531      public MediaType selectHeaderContentType(String[] contentTypes) {
   532          if (contentTypes.length == 0) {
   533              return MediaType.APPLICATION_JSON;
   534          }
   535          for (String contentType : contentTypes) {
   536              MediaType mediaType = MediaType.parseMediaType(contentType);
   537              if (isJsonMime(mediaType)) {
   538                  return mediaType;
   539              }
   540          }
   541          return MediaType.parseMediaType(contentTypes[0]);
   542      }
   543  
   544      /**
   545       * Select the body to use for the request
   546       * @param obj the body object
   547       * @param formParams the form parameters
   548       * @param contentType the content type of the request
   549       * @return Object the selected body
   550       */
   551      protected Object selectBody(Object obj, MultiValueMap<String, Object> formParams, MediaType contentType) {
   552          boolean isForm = MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType) || MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType);
   553          return isForm ? formParams : obj;
   554      }
   555  
   556      /**
   557       * Expand path template with variables
   558       * @param pathTemplate path template with placeholders
   559       * @param variables variables to replace
   560       * @return path with placeholders replaced by variables
   561       */
   562      public String expandPath(String pathTemplate, Map<String, Object> variables) {
   563          return restTemplate.getUriTemplateHandler().expand(pathTemplate, variables).toString();
   564      }
   565  
   566      /**
   567       * Invoke API by sending HTTP request with the given options.
   568       *
   569       * @param <T> the return type to use
   570       * @param path The sub-path of the HTTP URL
   571       * @param method The request method
   572       * @param queryParams The query parameters
   573       * @param body The request body object
   574       * @param headerParams The header parameters
   575       * @param cookieParams The cookie parameters
   576       * @param formParams The form parameters
   577       * @param accept The request's Accept header
   578       * @param contentType The request's Content-Type header
   579       * @param authNames The authentications to apply
   580       * @param returnType The return type into which to deserialize the response
   581       * @return ResponseEntity&lt;T&gt; The response of the chosen type
   582       */
   583      public <T> ResponseEntity<T> invokeAPI(String path, HttpMethod method, MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams, MultiValueMap<String, String> cookieParams, MultiValueMap<String, Object> formParams, List<MediaType> accept, MediaType contentType, String[] authNames, ParameterizedTypeReference<T> returnType) throws RestClientException {
   584          updateParamsForAuth(authNames, queryParams, headerParams, cookieParams);
   585  
   586          final UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(basePath).path(path);
   587          if (queryParams != null) {
   588              //encode the query parameters in case they contain unsafe characters
   589              for (List<String> values : queryParams.values()) {
   590                  if (values != null) {
   591                      for (int i = 0; i < values.size(); i++) {
   592                          try {
   593                              values.set(i, URLEncoder.encode(values.get(i), "utf8"));
   594                          } catch (UnsupportedEncodingException e) {
   595  
   596                          }
   597                      }
   598                  }
   599              }
   600              builder.queryParams(queryParams);
   601          }
   602  
   603  		URI uri;
   604          try {
   605              uri = new URI(builder.build().toUriString());
   606          } catch(URISyntaxException ex)  {
   607              throw new RestClientException("Could not build URL: " + builder.toUriString(), ex);
   608          }
   609  
   610          final BodyBuilder requestBuilder = RequestEntity.method(method, uri);
   611          if(accept != null) {
   612              requestBuilder.accept(accept.toArray(new MediaType[accept.size()]));
   613          }
   614          if(contentType != null) {
   615              requestBuilder.contentType(contentType);
   616          }
   617  
   618          addHeadersToRequest(headerParams, requestBuilder);
   619          addHeadersToRequest(defaultHeaders, requestBuilder);
   620          addCookiesToRequest(cookieParams, requestBuilder);
   621          addCookiesToRequest(defaultCookies, requestBuilder);
   622  
   623          RequestEntity<Object> requestEntity = requestBuilder.body(selectBody(body, formParams, contentType));
   624  
   625          ResponseEntity<T> responseEntity = restTemplate.exchange(requestEntity, returnType);
   626  
   627          if (responseEntity.getStatusCode().is2xxSuccessful()) {
   628              return responseEntity;
   629          } else {
   630              // The error handler built into the RestTemplate should handle 400 and 500 series errors.
   631              throw new RestClientException("API returned " + responseEntity.getStatusCode() + " and it wasn't handled by the RestTemplate error handler");
   632          }
   633      }
   634  
   635      /**
   636       * Add headers to the request that is being built
   637       * @param headers The headers to add
   638       * @param requestBuilder The current request
   639       */
   640      protected void addHeadersToRequest(HttpHeaders headers, BodyBuilder requestBuilder) {
   641          for (Entry<String, List<String>> entry : headers.entrySet()) {
   642              List<String> values = entry.getValue();
   643              for(String value : values) {
   644                  if (value != null) {
   645                      requestBuilder.header(entry.getKey(), value);
   646                  }
   647              }
   648          }
   649      }
   650  
   651      /**
   652       * Add cookies to the request that is being built
   653       * @param cookies The cookies to add
   654       * @param requestBuilder The current request
   655       */
   656      protected void addCookiesToRequest(MultiValueMap<String, String> cookies, BodyBuilder requestBuilder) {
   657          if (!cookies.isEmpty()) {
   658              requestBuilder.header("Cookie", buildCookieHeader(cookies));
   659          }
   660      }
   661  
   662      /**
   663       * Build cookie header. Keeps a single value per cookie (as per <a href="https://tools.ietf.org/html/rfc6265#section-5.3">
   664       * RFC6265 section 5.3</a>).
   665       *
   666       * @param cookies map all cookies
   667       * @return header string for cookies.
   668       */
   669      private String buildCookieHeader(MultiValueMap<String, String> cookies) {
   670          final StringBuilder cookieValue = new StringBuilder();
   671          String delimiter = "";
   672          for (final Map.Entry<String, List<String>> entry : cookies.entrySet()) {
   673              final String value = entry.getValue().get(entry.getValue().size() - 1);
   674              cookieValue.append(String.format("%s%s=%s", delimiter, entry.getKey(), value));
   675              delimiter = "; ";
   676          }
   677          return cookieValue.toString();
   678      }
   679  
   680      /**
   681       * Build the RestTemplate used to make HTTP requests.
   682       * @return RestTemplate
   683       */
   684      protected RestTemplate buildRestTemplate() {
   685          {{#withXml}}List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
   686          messageConverters.add(new MappingJackson2HttpMessageConverter());
   687          XmlMapper xmlMapper = new XmlMapper();
   688          xmlMapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true);
   689          xmlMapper.registerModule(new JsonNullableModule());
   690          messageConverters.add(new MappingJackson2XmlHttpMessageConverter(xmlMapper));
   691  
   692          RestTemplate restTemplate = new RestTemplate(messageConverters);
   693          {{/withXml}}{{^withXml}}RestTemplate restTemplate = new RestTemplate();{{/withXml}}
   694          {{#threetenbp}}
   695          for(HttpMessageConverter converter:restTemplate.getMessageConverters()){
   696              if(converter instanceof AbstractJackson2HttpMessageConverter){
   697                  ObjectMapper mapper = ((AbstractJackson2HttpMessageConverter)converter).getObjectMapper();
   698                  ThreeTenModule module = new ThreeTenModule();
   699                  module.addDeserializer(Instant.class, CustomInstantDeserializer.INSTANT);
   700                  module.addDeserializer(OffsetDateTime.class, CustomInstantDeserializer.OFFSET_DATE_TIME);
   701                  module.addDeserializer(ZonedDateTime.class, CustomInstantDeserializer.ZONED_DATE_TIME);
   702                  mapper.registerModule(module);
   703                  mapper.registerModule(new JsonNullableModule());
   704              }
   705          }
   706          {{/threetenbp}}
   707          // This allows us to read the response more than once - Necessary for debugging.
   708          restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory()));
   709          return restTemplate;
   710      }
   711  
   712      /**
   713       * Update query and header parameters based on authentication settings.
   714       *
   715       * @param authNames The authentications to apply
   716       * @param queryParams The query parameters
   717       * @param headerParams The header parameters
   718       */
   719      protected void updateParamsForAuth(String[] authNames, MultiValueMap<String, String> queryParams, HttpHeaders headerParams, MultiValueMap<String, String> cookieParams) {
   720          for (String authName : authNames) {
   721              Authentication auth = authentications.get(authName);
   722              if (auth == null) {
   723                  throw new RestClientException("Authentication undefined: " + authName);
   724              }
   725              auth.applyToParams(queryParams, headerParams, cookieParams);
   726          }
   727      }
   728  
   729      private class ApiClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
   730          private final Log log = LogFactory.getLog(ApiClientHttpRequestInterceptor.class);
   731  
   732          @Override
   733          public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
   734              logRequest(request, body);
   735              ClientHttpResponse response = execution.execute(request, body);
   736              logResponse(response);
   737              return response;
   738          }
   739  
   740          private void logRequest(HttpRequest request, byte[] body) throws UnsupportedEncodingException {
   741              log.info("URI: " + request.getURI());
   742              log.info("HTTP Method: " + request.getMethod());
   743              log.info("HTTP Headers: " + headersToString(request.getHeaders()));
   744              log.info("Request Body: " + new String(body, StandardCharsets.UTF_8));
   745          }
   746  
   747          private void logResponse(ClientHttpResponse response) throws IOException {
   748              log.info("HTTP Status Code: " + response.getRawStatusCode());
   749              log.info("Status Text: " + response.getStatusText());
   750              log.info("HTTP Headers: " + headersToString(response.getHeaders()));
   751              log.info("Response Body: " + bodyToString(response.getBody()));
   752          }
   753  
   754          private String headersToString(HttpHeaders headers) {
   755              StringBuilder builder = new StringBuilder();
   756              for(Entry<String, List<String>> entry : headers.entrySet()) {
   757                  builder.append(entry.getKey()).append("=[");
   758                  for(String value : entry.getValue()) {
   759                      builder.append(value).append(",");
   760                  }
   761                  builder.setLength(builder.length() - 1); // Get rid of trailing comma
   762                  builder.append("],");
   763              }
   764              builder.setLength(builder.length() - 1); // Get rid of trailing comma
   765              return builder.toString();
   766          }
   767  
   768          private String bodyToString(InputStream body) throws IOException {
   769              StringBuilder builder = new StringBuilder();
   770              BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(body, StandardCharsets.UTF_8));
   771              String line = bufferedReader.readLine();
   772              while (line != null) {
   773                  builder.append(line).append(System.lineSeparator());
   774                  line = bufferedReader.readLine();
   775              }
   776              bufferedReader.close();
   777              return builder.toString();
   778          }
   779      }
   780  }