github.com/phrase/openapi@v0.0.0-20240514140800-49e8a106740e/openapi-generator/templates/java/libraries/jersey2-experimental/ApiClient.mustache (about) 1 package {{invokerPackage}}; 2 3 import javax.ws.rs.client.Client; 4 import javax.ws.rs.client.ClientBuilder; 5 import javax.ws.rs.client.Entity; 6 import javax.ws.rs.client.Invocation; 7 import javax.ws.rs.client.WebTarget; 8 import javax.ws.rs.core.Form; 9 import javax.ws.rs.core.GenericType; 10 import javax.ws.rs.core.MediaType; 11 import javax.ws.rs.core.Response; 12 import javax.ws.rs.core.Response.Status; 13 14 import org.glassfish.jersey.client.ClientConfig; 15 import org.glassfish.jersey.client.ClientProperties; 16 import org.glassfish.jersey.client.HttpUrlConnectorProvider; 17 import org.glassfish.jersey.jackson.JacksonFeature; 18 import org.glassfish.jersey.media.multipart.FormDataBodyPart; 19 import org.glassfish.jersey.media.multipart.FormDataContentDisposition; 20 import org.glassfish.jersey.media.multipart.MultiPart; 21 import org.glassfish.jersey.media.multipart.MultiPartFeature; 22 23 import java.io.IOException; 24 import java.io.InputStream; 25 26 import java.net.URI; 27 {{^supportJava6}} 28 import java.nio.file.Files; 29 import java.nio.file.StandardCopyOption; 30 import org.glassfish.jersey.logging.LoggingFeature; 31 {{/supportJava6}} 32 {{#supportJava6}} 33 import org.apache.commons.io.FileUtils; 34 import org.glassfish.jersey.filter.LoggingFilter; 35 {{/supportJava6}} 36 import java.util.Collection; 37 import java.util.Collections; 38 import java.util.Map; 39 import java.util.Map.Entry; 40 import java.util.HashMap; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Arrays; 44 import java.util.ArrayList; 45 import java.util.Date; 46 import java.util.TimeZone; 47 48 import java.net.URLEncoder; 49 50 import java.io.File; 51 import java.io.UnsupportedEncodingException; 52 53 import java.text.DateFormat; 54 import java.util.regex.Matcher; 55 import java.util.regex.Pattern; 56 57 import {{invokerPackage}}.auth.Authentication; 58 import {{invokerPackage}}.auth.HttpBasicAuth; 59 import {{invokerPackage}}.auth.HttpBearerAuth; 60 import {{invokerPackage}}.auth.HttpSignatureAuth; 61 import {{invokerPackage}}.auth.ApiKeyAuth; 62 import {{invokerPackage}}.model.AbstractOpenApiSchema; 63 64 {{#hasOAuthMethods}} 65 import {{invokerPackage}}.auth.OAuth; 66 {{/hasOAuthMethods}} 67 68 {{>generatedAnnotation}} 69 public class ApiClient { 70 protected Map<String, String> defaultHeaderMap = new HashMap<String, String>(); 71 protected Map<String, String> defaultCookieMap = new HashMap<String, String>(); 72 protected String basePath = "{{{basePath}}}"; 73 protected List<ServerConfiguration> servers = new ArrayList<ServerConfiguration>({{#servers}}{{#-first}}Arrays.asList( 74 {{/-first}} new ServerConfiguration( 75 "{{{url}}}", 76 "{{{description}}}{{^description}}No description provided{{/description}}", 77 new HashMap<String, ServerVariable>(){{#variables}}{{#-first}} {{ 78 {{/-first}} put("{{{name}}}", new ServerVariable( 79 "{{{description}}}{{^description}}No description provided{{/description}}", 80 "{{{defaultValue}}}", 81 new HashSet<String>( 82 {{#enumValues}} 83 {{#-first}} 84 Arrays.asList( 85 {{/-first}} 86 "{{{.}}}"{{^-last}},{{/-last}} 87 {{#-last}} 88 ) 89 {{/-last}} 90 {{/enumValues}} 91 ) 92 )); 93 {{#-last}} 94 }}{{/-last}}{{/variables}} 95 ){{^-last}},{{/-last}} 96 {{#-last}} 97 ){{/-last}}{{/servers}}); 98 protected Integer serverIndex = 0; 99 protected Map<String, String> serverVariables = null; 100 protected Map<String, List<ServerConfiguration>> operationServers = new HashMap<String, List<ServerConfiguration>>() {{ 101 {{#apiInfo}} 102 {{#apis}} 103 {{#operations}} 104 {{#operation}} 105 {{#servers}} 106 {{#-first}} 107 put("{{{classname}}}.{{{operationId}}}", new ArrayList<ServerConfiguration>(Arrays.asList( 108 {{/-first}} 109 new ServerConfiguration( 110 "{{{url}}}", 111 "{{{description}}}{{^description}}No description provided{{/description}}", 112 new HashMap<String, ServerVariable>(){{#variables}}{{#-first}} {{ 113 {{/-first}} put("{{{name}}}", new ServerVariable( 114 "{{{description}}}{{^description}}No description provided{{/description}}", 115 "{{{defaultValue}}}", 116 new HashSet<String>( 117 {{#enumValues}} 118 {{#-first}} 119 Arrays.asList( 120 {{/-first}} 121 "{{{.}}}"{{^-last}},{{/-last}} 122 {{#-last}} 123 ) 124 {{/-last}} 125 {{/enumValues}} 126 ) 127 )); 128 {{#-last}} 129 }}{{/-last}}{{/variables}} 130 ){{^-last}},{{/-last}} 131 {{#-last}} 132 )));{{/-last}} 133 {{/servers}} 134 {{/operation}} 135 {{/operations}} 136 {{/apis}} 137 {{/apiInfo}} 138 }}; 139 protected Map<String, Integer> operationServerIndex = new HashMap<String, Integer>(); 140 protected Map<String, Map<String, String>> operationServerVariables = new HashMap<String, Map<String, String>>(); 141 protected boolean debugging = false; 142 protected int connectionTimeout = 0; 143 private int readTimeout = 0; 144 145 protected Client httpClient; 146 protected JSON json; 147 protected String tempFolderPath = null; 148 149 protected Map<String, Authentication> authentications; 150 protected Map<String, String> authenticationLookup; 151 152 protected DateFormat dateFormat; 153 154 public ApiClient() { 155 json = new JSON(); 156 httpClient = buildHttpClient(debugging); 157 158 this.dateFormat = new RFC3339DateFormat(); 159 160 // Set default User-Agent. 161 setUserAgent("{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{artifactVersion}}}/java{{/httpUserAgent}}"); 162 163 // Setup authentications (key: authentication name, value: authentication). 164 authentications = new HashMap<String, Authentication>(); 165 {{#authMethods}} 166 {{#isBasic}} 167 {{#isBasicBasic}} 168 authentications.put("{{name}}", new HttpBasicAuth()); 169 {{/isBasicBasic}} 170 {{#isBasicBearer}} 171 authentications.put("{{name}}", new HttpBearerAuth("{{scheme}}")); 172 {{/isBasicBearer}} 173 {{#isHttpSignature}} 174 authentications.put("{{name}}", new HttpSignatureAuth("{{name}}", null, null)); 175 {{/isHttpSignature}} 176 {{/isBasic}} 177 {{#isApiKey}} 178 authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{keyParamName}}")); 179 {{/isApiKey}} 180 {{#isOAuth}} 181 authentications.put("{{name}}", new OAuth()); 182 {{/isOAuth}} 183 {{/authMethods}} 184 // Prevent the authentications from being modified. 185 authentications = Collections.unmodifiableMap(authentications); 186 187 // Setup authentication lookup (key: authentication alias, value: authentication name) 188 authenticationLookup = new HashMap<String, String>();{{#authMethods}}{{#vendorExtensions.x-auth-id-alias}} 189 authenticationLookup.put("{{name}}", "{{.}}");{{/vendorExtensions.x-auth-id-alias}}{{/authMethods}} 190 } 191 192 /** 193 * Gets the JSON instance to do JSON serialization and deserialization. 194 * @return JSON 195 */ 196 public JSON getJSON() { 197 return json; 198 } 199 200 public Client getHttpClient() { 201 return httpClient; 202 } 203 204 public ApiClient setHttpClient(Client httpClient) { 205 this.httpClient = httpClient; 206 return this; 207 } 208 209 public String getBasePath() { 210 return basePath; 211 } 212 213 public ApiClient setBasePath(String basePath) { 214 this.basePath = basePath; 215 return this; 216 } 217 218 public List<ServerConfiguration> getServers() { 219 return servers; 220 } 221 222 public ApiClient setServers(List<ServerConfiguration> servers) { 223 this.servers = servers; 224 return this; 225 } 226 227 public Integer getServerIndex() { 228 return serverIndex; 229 } 230 231 public ApiClient setServerIndex(Integer serverIndex) { 232 this.serverIndex = serverIndex; 233 return this; 234 } 235 236 public Map<String, String> getServerVariables() { 237 return serverVariables; 238 } 239 240 public ApiClient setServerVariables(Map<String, String> serverVariables) { 241 this.serverVariables = serverVariables; 242 return this; 243 } 244 245 /** 246 * Get authentications (key: authentication name, value: authentication). 247 * @return Map of authentication object 248 */ 249 public Map<String, Authentication> getAuthentications() { 250 return authentications; 251 } 252 253 /** 254 * Get authentication for the given name. 255 * 256 * @param authName The authentication name 257 * @return The authentication, null if not found 258 */ 259 public Authentication getAuthentication(String authName) { 260 return authentications.get(authName); 261 } 262 263 /** 264 * Helper method to set username for the first HTTP basic authentication. 265 * @param username Username 266 */ 267 public void setUsername(String username) { 268 for (Authentication auth : authentications.values()) { 269 if (auth instanceof HttpBasicAuth) { 270 ((HttpBasicAuth) auth).setUsername(username); 271 return; 272 } 273 } 274 throw new RuntimeException("No HTTP basic authentication configured!"); 275 } 276 277 /** 278 * Helper method to set password for the first HTTP basic authentication. 279 * @param password Password 280 */ 281 public void setPassword(String password) { 282 for (Authentication auth : authentications.values()) { 283 if (auth instanceof HttpBasicAuth) { 284 ((HttpBasicAuth) auth).setPassword(password); 285 return; 286 } 287 } 288 throw new RuntimeException("No HTTP basic authentication configured!"); 289 } 290 291 /** 292 * Helper method to set API key value for the first API key authentication. 293 * @param apiKey API key 294 */ 295 public void setApiKey(String apiKey) { 296 for (Authentication auth : authentications.values()) { 297 if (auth instanceof ApiKeyAuth) { 298 ((ApiKeyAuth) auth).setApiKey(apiKey); 299 return; 300 } 301 } 302 throw new RuntimeException("No API key authentication configured!"); 303 } 304 305 /** 306 * Helper method to configure authentications which respects aliases of API keys. 307 * 308 * @param secrets Hash map from authentication name to its secret. 309 */ 310 public void configureApiKeys(HashMap<String, String> secrets) { 311 for (Map.Entry<String, Authentication> authEntry : authentications.entrySet()) { 312 Authentication auth = authEntry.getValue(); 313 if (auth instanceof ApiKeyAuth) { 314 String name = authEntry.getKey(); 315 // respect x-auth-id-alias property 316 name = authenticationLookup.getOrDefault(name, name); 317 if (secrets.containsKey(name)) { 318 ((ApiKeyAuth) auth).setApiKey(secrets.get(name)); 319 } 320 } 321 } 322 } 323 324 /** 325 * Helper method to set API key prefix for the first API key authentication. 326 * @param apiKeyPrefix API key prefix 327 */ 328 public void setApiKeyPrefix(String apiKeyPrefix) { 329 for (Authentication auth : authentications.values()) { 330 if (auth instanceof ApiKeyAuth) { 331 ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); 332 return; 333 } 334 } 335 throw new RuntimeException("No API key authentication configured!"); 336 } 337 338 /** 339 * Helper method to set bearer token for the first Bearer authentication. 340 * @param bearerToken Bearer token 341 */ 342 public void setBearerToken(String bearerToken) { 343 for (Authentication auth : authentications.values()) { 344 if (auth instanceof HttpBearerAuth) { 345 ((HttpBearerAuth) auth).setBearerToken(bearerToken); 346 return; 347 } 348 } 349 throw new RuntimeException("No Bearer authentication configured!"); 350 } 351 352 {{#hasOAuthMethods}} 353 /** 354 * Helper method to set access token for the first OAuth2 authentication. 355 * @param accessToken Access token 356 */ 357 public void setAccessToken(String accessToken) { 358 for (Authentication auth : authentications.values()) { 359 if (auth instanceof OAuth) { 360 ((OAuth) auth).setAccessToken(accessToken); 361 return; 362 } 363 } 364 throw new RuntimeException("No OAuth2 authentication configured!"); 365 } 366 367 {{/hasOAuthMethods}} 368 /** 369 * Set the User-Agent header's value (by adding to the default header map). 370 * @param userAgent Http user agent 371 * @return API client 372 */ 373 public ApiClient setUserAgent(String userAgent) { 374 addDefaultHeader("User-Agent", userAgent); 375 return this; 376 } 377 378 /** 379 * Add a default header. 380 * 381 * @param key The header's key 382 * @param value The header's value 383 * @return API client 384 */ 385 public ApiClient addDefaultHeader(String key, String value) { 386 defaultHeaderMap.put(key, value); 387 return this; 388 } 389 390 /** 391 * Add a default cookie. 392 * 393 * @param key The cookie's key 394 * @param value The cookie's value 395 * @return API client 396 */ 397 public ApiClient addDefaultCookie(String key, String value) { 398 defaultCookieMap.put(key, value); 399 return this; 400 } 401 402 /** 403 * Check that whether debugging is enabled for this API client. 404 * @return True if debugging is switched on 405 */ 406 public boolean isDebugging() { 407 return debugging; 408 } 409 410 /** 411 * Enable/disable debugging for this API client. 412 * 413 * @param debugging To enable (true) or disable (false) debugging 414 * @return API client 415 */ 416 public ApiClient setDebugging(boolean debugging) { 417 this.debugging = debugging; 418 // Rebuild HTTP Client according to the new "debugging" value. 419 this.httpClient = buildHttpClient(debugging); 420 return this; 421 } 422 423 /** 424 * The path of temporary folder used to store downloaded files from endpoints 425 * with file response. The default value is <code>null</code>, i.e. using 426 * the system's default tempopary folder. 427 * 428 * @return Temp folder path 429 */ 430 public String getTempFolderPath() { 431 return tempFolderPath; 432 } 433 434 /** 435 * Set temp folder path 436 * @param tempFolderPath Temp folder path 437 * @return API client 438 */ 439 public ApiClient setTempFolderPath(String tempFolderPath) { 440 this.tempFolderPath = tempFolderPath; 441 return this; 442 } 443 444 /** 445 * Connect timeout (in milliseconds). 446 * @return Connection timeout 447 */ 448 public int getConnectTimeout() { 449 return connectionTimeout; 450 } 451 452 /** 453 * Set the connect timeout (in milliseconds). 454 * A value of 0 means no timeout, otherwise values must be between 1 and 455 * {@link Integer#MAX_VALUE}. 456 * @param connectionTimeout Connection timeout in milliseconds 457 * @return API client 458 */ 459 public ApiClient setConnectTimeout(int connectionTimeout) { 460 this.connectionTimeout = connectionTimeout; 461 httpClient.property(ClientProperties.CONNECT_TIMEOUT, connectionTimeout); 462 return this; 463 } 464 465 /** 466 * read timeout (in milliseconds). 467 * @return Read timeout 468 */ 469 public int getReadTimeout() { 470 return readTimeout; 471 } 472 473 /** 474 * Set the read timeout (in milliseconds). 475 * A value of 0 means no timeout, otherwise values must be between 1 and 476 * {@link Integer#MAX_VALUE}. 477 * @param readTimeout Read timeout in milliseconds 478 * @return API client 479 */ 480 public ApiClient setReadTimeout(int readTimeout) { 481 this.readTimeout = readTimeout; 482 httpClient.property(ClientProperties.READ_TIMEOUT, readTimeout); 483 return this; 484 } 485 486 /** 487 * Get the date format used to parse/format date parameters. 488 * @return Date format 489 */ 490 public DateFormat getDateFormat() { 491 return dateFormat; 492 } 493 494 /** 495 * Set the date format used to parse/format date parameters. 496 * @param dateFormat Date format 497 * @return API client 498 */ 499 public ApiClient setDateFormat(DateFormat dateFormat) { 500 this.dateFormat = dateFormat; 501 // also set the date format for model (de)serialization with Date properties 502 this.json.setDateFormat((DateFormat) dateFormat.clone()); 503 return this; 504 } 505 506 /** 507 * Parse the given string into Date object. 508 * @param str String 509 * @return Date 510 */ 511 public Date parseDate(String str) { 512 try { 513 return dateFormat.parse(str); 514 } catch (java.text.ParseException e) { 515 throw new RuntimeException(e); 516 } 517 } 518 519 /** 520 * Format the given Date object into string. 521 * @param date Date 522 * @return Date in string format 523 */ 524 public String formatDate(Date date) { 525 return dateFormat.format(date); 526 } 527 528 /** 529 * Format the given parameter object into string. 530 * @param param Object 531 * @return Object in string format 532 */ 533 public String parameterToString(Object param) { 534 if (param == null) { 535 return ""; 536 } else if (param instanceof Date) { 537 return formatDate((Date) param); 538 } else if (param instanceof Collection) { 539 StringBuilder b = new StringBuilder(); 540 for(Object o : (Collection)param) { 541 if(b.length() > 0) { 542 b.append(','); 543 } 544 b.append(String.valueOf(o)); 545 } 546 return b.toString(); 547 } else { 548 return String.valueOf(param); 549 } 550 } 551 552 /* 553 * Format to {@code Pair} objects. 554 * @param collectionFormat Collection format 555 * @param name Name 556 * @param value Value 557 * @return List of pairs 558 */ 559 public List<Pair> parameterToPairs(String collectionFormat, String name, Object value){ 560 List<Pair> params = new ArrayList<Pair>(); 561 562 // preconditions 563 if (name == null || name.isEmpty() || value == null) return params; 564 565 Collection valueCollection; 566 if (value instanceof Collection) { 567 valueCollection = (Collection) value; 568 } else { 569 params.add(new Pair(name, parameterToString(value))); 570 return params; 571 } 572 573 if (valueCollection.isEmpty()){ 574 return params; 575 } 576 577 // get the collection format (default: csv) 578 String format = (collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat); 579 580 // create the params based on the collection format 581 if ("multi".equals(format)) { 582 for (Object item : valueCollection) { 583 params.add(new Pair(name, parameterToString(item))); 584 } 585 586 return params; 587 } 588 589 String delimiter = ","; 590 591 if ("csv".equals(format)) { 592 delimiter = ","; 593 } else if ("ssv".equals(format)) { 594 delimiter = " "; 595 } else if ("tsv".equals(format)) { 596 delimiter = "\t"; 597 } else if ("pipes".equals(format)) { 598 delimiter = "|"; 599 } 600 601 StringBuilder sb = new StringBuilder() ; 602 for (Object item : valueCollection) { 603 sb.append(delimiter); 604 sb.append(parameterToString(item)); 605 } 606 607 params.add(new Pair(name, sb.substring(1))); 608 609 return params; 610 } 611 612 /** 613 * Check if the given MIME is a JSON MIME. 614 * JSON MIME examples: 615 * application/json 616 * application/json; charset=UTF8 617 * APPLICATION/JSON 618 * application/vnd.company+json 619 * "* / *" is also default to JSON 620 * @param mime MIME 621 * @return True if the MIME type is JSON 622 */ 623 public boolean isJsonMime(String mime) { 624 String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; 625 return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); 626 } 627 628 /** 629 * Select the Accept header's value from the given accepts array: 630 * if JSON exists in the given array, use it; 631 * otherwise use all of them (joining into a string) 632 * 633 * @param accepts The accepts array to select from 634 * @return The Accept header to use. If the given array is empty, 635 * null will be returned (not to set the Accept header explicitly). 636 */ 637 public String selectHeaderAccept(String[] accepts) { 638 if (accepts.length == 0) { 639 return null; 640 } 641 for (String accept : accepts) { 642 if (isJsonMime(accept)) { 643 return accept; 644 } 645 } 646 return StringUtil.join(accepts, ","); 647 } 648 649 /** 650 * Select the Content-Type header's value from the given array: 651 * if JSON exists in the given array, use it; 652 * otherwise use the first one of the array. 653 * 654 * @param contentTypes The Content-Type array to select from 655 * @return The Content-Type header to use. If the given array is empty, 656 * JSON will be used. 657 */ 658 public String selectHeaderContentType(String[] contentTypes) { 659 if (contentTypes.length == 0) { 660 return "application/json"; 661 } 662 for (String contentType : contentTypes) { 663 if (isJsonMime(contentType)) { 664 return contentType; 665 } 666 } 667 return contentTypes[0]; 668 } 669 670 /** 671 * Escape the given string to be used as URL query value. 672 * @param str String 673 * @return Escaped string 674 */ 675 public String escapeString(String str) { 676 try { 677 return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); 678 } catch (UnsupportedEncodingException e) { 679 return str; 680 } 681 } 682 683 /** 684 * Serialize the given Java object into string entity according the given 685 * Content-Type (only JSON is supported for now). 686 * @param obj Object 687 * @param formParams Form parameters 688 * @param contentType Context type 689 * @return Entity 690 * @throws ApiException API exception 691 */ 692 public Entity<?> serialize(Object obj, Map<String, Object> formParams, String contentType) throws ApiException { 693 Entity<?> entity; 694 if (contentType.startsWith("multipart/form-data")) { 695 MultiPart multiPart = new MultiPart(); 696 for (Entry<String, Object> param: formParams.entrySet()) { 697 if (param.getValue() instanceof File) { 698 File file = (File) param.getValue(); 699 FormDataContentDisposition contentDisp = FormDataContentDisposition.name(param.getKey()) 700 .fileName(file.getName()).size(file.length()).build(); 701 multiPart.bodyPart(new FormDataBodyPart(contentDisp, file, MediaType.APPLICATION_OCTET_STREAM_TYPE)); 702 } else { 703 FormDataContentDisposition contentDisp = FormDataContentDisposition.name(param.getKey()).build(); 704 multiPart.bodyPart(new FormDataBodyPart(contentDisp, parameterToString(param.getValue()))); 705 } 706 } 707 entity = Entity.entity(multiPart, MediaType.MULTIPART_FORM_DATA_TYPE); 708 } else if (contentType.startsWith("application/x-www-form-urlencoded")) { 709 Form form = new Form(); 710 for (Entry<String, Object> param: formParams.entrySet()) { 711 form.param(param.getKey(), parameterToString(param.getValue())); 712 } 713 entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE); 714 } else { 715 // We let jersey handle the serialization 716 entity = Entity.entity(obj == null ? Entity.text("") : obj, contentType); 717 } 718 return entity; 719 } 720 721 /** 722 * Serialize the given Java object into string according the given 723 * Content-Type (only JSON, HTTP form is supported for now). 724 * @param obj Object 725 * @param formParams Form parameters 726 * @param contentType Context type 727 * @return String 728 * @throws ApiException API exception 729 */ 730 public String serializeToString(Object obj, Map<String, Object> formParams, String contentType) throws ApiException { 731 try { 732 if (contentType.startsWith("multipart/form-data")) { 733 throw new ApiException("multipart/form-data not yet supported for serializeToString (http signature authentication)"); 734 } else if (contentType.startsWith("application/x-www-form-urlencoded")) { 735 String formString = ""; 736 for (Entry<String, Object> param : formParams.entrySet()) { 737 formString = param.getKey() + "=" + URLEncoder.encode(parameterToString(param.getValue()), "UTF-8") + "&"; 738 } 739 740 if (formString.length() == 0) { // empty string 741 return formString; 742 } else { 743 return formString.substring(0, formString.length() - 1); 744 } 745 } else { 746 return json.getMapper().writeValueAsString(obj); 747 } 748 } catch (Exception ex) { 749 throw new ApiException("Failed to perform serializeToString: " + ex.toString()); 750 } 751 } 752 753 public AbstractOpenApiSchema deserializeSchemas(Response response, AbstractOpenApiSchema schema) throws ApiException{ 754 755 Object result = null; 756 int matchCounter = 0; 757 ArrayList<String> matchSchemas = new ArrayList<>(); 758 759 for (Map.Entry<String, GenericType> entry : schema.getSchemas().entrySet()) { 760 String schemaName = entry.getKey(); 761 GenericType schemaType = entry.getValue(); 762 763 if (schemaType instanceof GenericType) { // model 764 try { 765 Object deserializedObject = deserialize(response, schemaType); 766 if (deserializedObject != null) { 767 result = deserializedObject; 768 matchCounter++; 769 770 if ("anyOf".equals(schema.getSchemaType())) { 771 break; 772 } else if ("oneOf".equals(schema.getSchemaType())) { 773 matchSchemas.add(schemaName); 774 } else { 775 throw new ApiException("Unknowe type found while expecting anyOf/oneOf:" + schema.getSchemaType()); 776 } 777 } else { 778 // failed to deserialize the response in the schema provided, proceed to the next one if any 779 } 780 } catch (Exception ex) { 781 // failed to deserialize, do nothing and try next one (schema) 782 } 783 } else {// unknown type 784 throw new ApiException(schemaType.getClass() + " is not a GenericType and cannot be handled properly in deserialization."); 785 } 786 787 } 788 789 if (matchCounter > 1 && "oneOf".equals(schema.getSchemaType())) {// more than 1 match for oneOf 790 throw new ApiException("Response body is invalid as it matches more than one schema (" + String.join(", ", matchSchemas) + ") defined in the oneOf model: " + schema.getClass().getName()); 791 } else if (matchCounter == 0) { // fail to match any in oneOf/anyOf schemas 792 throw new ApiException("Response body is invalid as it doens't match any schemas (" + String.join(", ", schema.getSchemas().keySet()) + ") defined in the oneOf/anyOf model: " + schema.getClass().getName()); 793 } else { // only one matched 794 schema.setActualInstance(result); 795 return schema; 796 } 797 798 } 799 800 801 802 /** 803 * Deserialize response body to Java object according to the Content-Type. 804 * @param <T> Type 805 * @param response Response 806 * @param returnType Return type 807 * @return Deserialize object 808 * @throws ApiException API exception 809 */ 810 @SuppressWarnings("unchecked") 811 public <T> T deserialize(Response response, GenericType<T> returnType) throws ApiException { 812 if (response == null || returnType == null) { 813 return null; 814 } 815 816 if ("byte[]".equals(returnType.toString())) { 817 // Handle binary response (byte array). 818 return (T) response.readEntity(byte[].class); 819 } else if (returnType.getRawType() == File.class) { 820 // Handle file downloading. 821 T file = (T) downloadFileFromResponse(response); 822 return file; 823 } 824 825 String contentType = null; 826 List<Object> contentTypes = response.getHeaders().get("Content-Type"); 827 if (contentTypes != null && !contentTypes.isEmpty()) 828 contentType = String.valueOf(contentTypes.get(0)); 829 830 // read the entity stream multiple times 831 response.bufferEntity(); 832 833 return response.readEntity(returnType); 834 } 835 836 /** 837 * Download file from the given response. 838 * @param response Response 839 * @return File 840 * @throws ApiException If fail to read file content from response and write to disk 841 */ 842 public File downloadFileFromResponse(Response response) throws ApiException { 843 try { 844 File file = prepareDownloadFile(response); 845 {{^supportJava6}} 846 Files.copy(response.readEntity(InputStream.class), file.toPath(), StandardCopyOption.REPLACE_EXISTING); 847 {{/supportJava6}} 848 {{#supportJava6}} 849 // Java6 falls back to commons.io for file copying 850 FileUtils.copyToFile(response.readEntity(InputStream.class), file); 851 {{/supportJava6}} 852 return file; 853 } catch (IOException e) { 854 throw new ApiException(e); 855 } 856 } 857 858 public File prepareDownloadFile(Response response) throws IOException { 859 String filename = null; 860 String contentDisposition = (String) response.getHeaders().getFirst("Content-Disposition"); 861 if (contentDisposition != null && !"".equals(contentDisposition)) { 862 // Get filename from the Content-Disposition header. 863 Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?"); 864 Matcher matcher = pattern.matcher(contentDisposition); 865 if (matcher.find()) 866 filename = matcher.group(1); 867 } 868 869 String prefix; 870 String suffix = null; 871 if (filename == null) { 872 prefix = "download-"; 873 suffix = ""; 874 } else { 875 int pos = filename.lastIndexOf('.'); 876 if (pos == -1) { 877 prefix = filename + "-"; 878 } else { 879 prefix = filename.substring(0, pos) + "-"; 880 suffix = filename.substring(pos); 881 } 882 // File.createTempFile requires the prefix to be at least three characters long 883 if (prefix.length() < 3) 884 prefix = "download-"; 885 } 886 887 if (tempFolderPath == null) 888 return File.createTempFile(prefix, suffix); 889 else 890 return File.createTempFile(prefix, suffix, new File(tempFolderPath)); 891 } 892 893 /** 894 * Invoke API by sending HTTP request with the given options. 895 * 896 * @param <T> Type 897 * @param operation The qualified name of the operation 898 * @param path The sub-path of the HTTP URL 899 * @param method The request method, one of "GET", "POST", "PUT", "HEAD" and "DELETE" 900 * @param queryParams The query parameters 901 * @param body The request body object 902 * @param headerParams The header parameters 903 * @param cookieParams The cookie parameters 904 * @param formParams The form parameters 905 * @param accept The request's Accept header 906 * @param contentType The request's Content-Type header 907 * @param authNames The authentications to apply 908 * @param returnType The return type into which to deserialize the response 909 * @param schema An instance of the response that uses oneOf/anyOf 910 * @return The response body in type of string 911 * @throws ApiException API exception 912 */ 913 public <T> ApiResponse<T> invokeAPI(String operation, String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String accept, String contentType, String[] authNames, GenericType<T> returnType, AbstractOpenApiSchema schema) throws ApiException { 914 915 // Not using `.target(targetURL).path(path)` below, 916 // to support (constant) query string in `path`, e.g. "/posts?draft=1" 917 String targetURL; 918 if (serverIndex != null) { 919 Integer index; 920 List<ServerConfiguration> serverConfigurations; 921 Map<String, String> variables; 922 923 if (operationServers.containsKey(operation)) { 924 index = operationServerIndex.getOrDefault(operation, serverIndex); 925 variables = operationServerVariables.getOrDefault(operation, serverVariables); 926 serverConfigurations = operationServers.get(operation); 927 } else { 928 index = serverIndex; 929 variables = serverVariables; 930 serverConfigurations = servers; 931 } 932 if (index < 0 || index >= serverConfigurations.size()) { 933 throw new ArrayIndexOutOfBoundsException(String.format( 934 "Invalid index %d when selecting the host settings. Must be less than %d", index, serverConfigurations.size() 935 )); 936 } 937 targetURL = serverConfigurations.get(index).URL(variables) + path; 938 } else { 939 targetURL = this.basePath + path; 940 } 941 WebTarget target = httpClient.target(targetURL); 942 943 if (queryParams != null) { 944 for (Pair queryParam : queryParams) { 945 if (queryParam.getValue() != null) { 946 target = target.queryParam(queryParam.getName(), escapeString(queryParam.getValue())); 947 } 948 } 949 } 950 951 Invocation.Builder invocationBuilder = target.request().accept(accept); 952 953 for (Entry<String, String> entry : headerParams.entrySet()) { 954 String value = entry.getValue(); 955 if (value != null) { 956 invocationBuilder = invocationBuilder.header(entry.getKey(), value); 957 } 958 } 959 960 for (Entry<String, String> entry : cookieParams.entrySet()) { 961 String value = entry.getValue(); 962 if (value != null) { 963 invocationBuilder = invocationBuilder.cookie(entry.getKey(), value); 964 } 965 } 966 967 for (Entry<String, String> entry : defaultCookieMap.entrySet()) { 968 String value = entry.getValue(); 969 if (value != null) { 970 invocationBuilder = invocationBuilder.cookie(entry.getKey(), value); 971 } 972 } 973 974 for (Entry<String, String> entry : defaultHeaderMap.entrySet()) { 975 String key = entry.getKey(); 976 if (!headerParams.containsKey(key)) { 977 String value = entry.getValue(); 978 if (value != null) { 979 invocationBuilder = invocationBuilder.header(key, value); 980 } 981 } 982 } 983 984 Entity<?> entity = serialize(body, formParams, contentType); 985 986 // put all headers in one place 987 Map<String, String> allHeaderParams = new HashMap<>(); 988 allHeaderParams.putAll(defaultHeaderMap); 989 allHeaderParams.putAll(headerParams); 990 991 // update different parameters (e.g. headers) for authentication 992 updateParamsForAuth(authNames, queryParams, allHeaderParams, cookieParams, serializeToString(body, formParams, contentType), method, target.getUri()); 993 994 Response response = null; 995 996 try { 997 if ("GET".equals(method)) { 998 response = invocationBuilder.get(); 999 } else if ("POST".equals(method)) { 1000 response = invocationBuilder.post(entity); 1001 } else if ("PUT".equals(method)) { 1002 response = invocationBuilder.put(entity); 1003 } else if ("DELETE".equals(method)) { 1004 response = invocationBuilder.method("DELETE", entity); 1005 } else if ("PATCH".equals(method)) { 1006 response = invocationBuilder.method("PATCH", entity); 1007 } else if ("HEAD".equals(method)) { 1008 response = invocationBuilder.head(); 1009 } else if ("OPTIONS".equals(method)) { 1010 response = invocationBuilder.options(); 1011 } else if ("TRACE".equals(method)) { 1012 response = invocationBuilder.trace(); 1013 } else { 1014 throw new ApiException(500, "unknown method type " + method); 1015 } 1016 1017 int statusCode = response.getStatusInfo().getStatusCode(); 1018 Map<String, List<String>> responseHeaders = buildResponseHeaders(response); 1019 1020 if (response.getStatus() == Status.NO_CONTENT.getStatusCode()) { 1021 return new ApiResponse<{{#supportJava6}}T{{/supportJava6}}>(statusCode, responseHeaders); 1022 } else if (response.getStatusInfo().getFamily() == Status.Family.SUCCESSFUL) { 1023 if (returnType == null) 1024 return new ApiResponse<{{#supportJava6}}T{{/supportJava6}}>(statusCode, responseHeaders); 1025 else 1026 if (schema == null) { 1027 return new ApiResponse<>(statusCode, responseHeaders, deserialize(response, returnType)); 1028 } else { // oneOf/anyOf 1029 return new ApiResponse<>(statusCode, responseHeaders, (T)deserializeSchemas(response, schema)); 1030 } 1031 } else { 1032 String message = "error"; 1033 String respBody = null; 1034 if (response.hasEntity()) { 1035 try { 1036 respBody = String.valueOf(response.readEntity(String.class)); 1037 message = respBody; 1038 } catch (RuntimeException e) { 1039 // e.printStackTrace(); 1040 } 1041 } 1042 throw new ApiException( 1043 response.getStatus(), 1044 message, 1045 buildResponseHeaders(response), 1046 respBody); 1047 } 1048 } finally { 1049 try { 1050 response.close(); 1051 } catch (Exception e) { 1052 // it's not critical, since the response object is local in method invokeAPI; that's fine, just continue 1053 } 1054 } 1055 } 1056 1057 /** 1058 * @deprecated Add qualified name of the operation as a first parameter. 1059 */ 1060 @Deprecated 1061 public <T> ApiResponse<T> invokeAPI(String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String accept, String contentType, String[] authNames, GenericType<T> returnType, AbstractOpenApiSchema schema) throws ApiException { 1062 return invokeAPI(null, path, method, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames, returnType, schema); 1063 } 1064 1065 /** 1066 * Build the Client used to make HTTP requests. 1067 * @param debugging Debug setting 1068 * @return Client 1069 */ 1070 protected Client buildHttpClient(boolean debugging) { 1071 final ClientConfig clientConfig = new ClientConfig(); 1072 clientConfig.register(MultiPartFeature.class); 1073 clientConfig.register(json); 1074 clientConfig.register(JacksonFeature.class); 1075 clientConfig.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true); 1076 // turn off compliance validation to be able to send payloads with DELETE calls 1077 clientConfig.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true); 1078 if (debugging) { 1079 {{^supportJava6}} 1080 clientConfig.register(new LoggingFeature(java.util.logging.Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), java.util.logging.Level.INFO, LoggingFeature.Verbosity.PAYLOAD_ANY, 1024*50 /* Log payloads up to 50K */)); 1081 clientConfig.property(LoggingFeature.LOGGING_FEATURE_VERBOSITY, LoggingFeature.Verbosity.PAYLOAD_ANY); 1082 // Set logger to ALL 1083 java.util.logging.Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME).setLevel(java.util.logging.Level.ALL); 1084 {{/supportJava6}} 1085 {{#supportJava6}} 1086 clientConfig.register(new LoggingFilter(java.util.logging.Logger.getLogger(LoggingFilter.class.getName()), true)); 1087 {{/supportJava6}} 1088 } else { 1089 // suppress warnings for payloads with DELETE calls: 1090 java.util.logging.Logger.getLogger("org.glassfish.jersey.client").setLevel(java.util.logging.Level.SEVERE); 1091 } 1092 performAdditionalClientConfiguration(clientConfig); 1093 return ClientBuilder.newClient(clientConfig); 1094 } 1095 1096 protected void performAdditionalClientConfiguration(ClientConfig clientConfig) { 1097 // No-op extension point 1098 } 1099 1100 protected Map<String, List<String>> buildResponseHeaders(Response response) { 1101 Map<String, List<String>> responseHeaders = new HashMap<String, List<String>>(); 1102 for (Entry<String, List<Object>> entry: response.getHeaders().entrySet()) { 1103 List<Object> values = entry.getValue(); 1104 List<String> headers = new ArrayList<String>(); 1105 for (Object o : values) { 1106 headers.add(String.valueOf(o)); 1107 } 1108 responseHeaders.put(entry.getKey(), headers); 1109 } 1110 return responseHeaders; 1111 } 1112 1113 /** 1114 * Update query and header parameters based on authentication settings. 1115 * 1116 * @param authNames The authentications to apply 1117 * @param queryParams List of query parameters 1118 * @param headerParams Map of header parameters 1119 * @param cookieParams Map of cookie parameters 1120 * @param method HTTP method (e.g. POST) 1121 * @param uri HTTP URI 1122 */ 1123 protected void updateParamsForAuth(String[] authNames, List<Pair> queryParams, Map<String, String> headerParams, 1124 Map<String, String> cookieParams, String payload, String method, URI uri) throws ApiException { 1125 for (String authName : authNames) { 1126 Authentication auth = authentications.get(authName); 1127 if (auth == null) { 1128 throw new RuntimeException("Authentication undefined: " + authName); 1129 } 1130 auth.applyToParams(queryParams, headerParams, cookieParams, payload, method, uri); 1131 } 1132 } 1133 }