15
15
*/
16
16
package com .ibm .watson .data .client ;
17
17
18
+ import com .fasterxml .jackson .core .type .TypeReference ;
18
19
import com .fasterxml .jackson .databind .DeserializationFeature ;
19
20
import com .fasterxml .jackson .databind .ObjectMapper ;
20
21
import com .ibm .watson .data .client .api .AuthorizationApi ;
21
22
import com .ibm .watson .data .client .auth .Authentication ;
22
23
import com .ibm .watson .data .client .auth .HttpBearerAuth ;
23
24
24
25
import java .io .*;
26
+ import java .net .URI ;
27
+ import java .nio .file .FileSystems ;
25
28
import java .nio .file .Files ;
29
+ import java .nio .file .Path ;
26
30
import java .nio .file .Paths ;
27
31
import java .security .GeneralSecurityException ;
28
32
import java .security .cert .X509Certificate ;
41
45
import org .openapitools .jackson .nullable .JsonNullableModule ;
42
46
import org .springframework .core .ParameterizedTypeReference ;
43
47
import org .springframework .core .io .FileSystemResource ;
48
+ import org .springframework .core .io .buffer .DataBuffer ;
49
+ import org .springframework .core .io .buffer .DataBufferUtils ;
44
50
import org .springframework .http .*;
45
51
import org .springframework .http .client .reactive .ClientHttpConnector ;
46
52
import org .springframework .http .client .reactive .ClientHttpRequest ;
@@ -90,8 +96,7 @@ private String collectionToString(Collection<?> collection) {
90
96
}
91
97
92
98
private final HttpHeaders defaultHeaders = new HttpHeaders ();
93
- private final MultiValueMap <String , String > defaultCookies =
94
- new LinkedMultiValueMap <>();
99
+ private final MultiValueMap <String , String > defaultCookies = new LinkedMultiValueMap <>();
95
100
96
101
private String basePath = "http://localhost" ;
97
102
@@ -105,6 +110,8 @@ private String collectionToString(Collection<?> collection) {
105
110
106
111
private Authentication authentication ;
107
112
113
+ private final ObjectMapper mapper ;
114
+
108
115
/**
109
116
* Constructs a base ApiClient
110
117
* @param disableSSLVerification will disable SSL verification if set to true
@@ -119,7 +126,7 @@ public ApiClient(boolean disableSSLVerification) {
119
126
* @param bufferSizeInMb maximum size of buffer to allow for WebClient
120
127
*/
121
128
public ApiClient (boolean disableSSLVerification , int bufferSizeInMb ) {
122
- ObjectMapper mapper = new ObjectMapper ();
129
+ mapper = new ObjectMapper ();
123
130
mapper .enable (DeserializationFeature .ACCEPT_SINGLE_VALUE_AS_ARRAY );
124
131
mapper .disable (DeserializationFeature .ADJUST_DATES_TO_CONTEXT_TIME_ZONE );
125
132
mapper .configure (DeserializationFeature .FAIL_ON_UNKNOWN_PROPERTIES , false );
@@ -242,6 +249,36 @@ public ApiClient setCredentials(String username, String password) {
242
249
return this ;
243
250
}
244
251
252
+ /**
253
+ * Save the contents of a DataBuffer into a file using the path provided.
254
+ * @param path of the file to write
255
+ * @param contents that should be written to the file
256
+ * @return Path that was written, or null if no file was written
257
+ */
258
+ public static Path saveBufferAsFile (String path , Mono <DataBuffer > contents ) {
259
+ final Path location = FileSystems .getDefault ().getPath (path );
260
+ DataBufferUtils .write (contents , location ).block ();
261
+ return location ;
262
+ }
263
+
264
+ /**
265
+ * Retrieve the contents of a DataBuffer as an object of the specified type.
266
+ * Note: there is the potential for this to consume a very large amount of memory, depending on the size
267
+ * of the data buffer (object) being translated.
268
+ * @param contents that should be transformed into an object
269
+ * @param type of object into which to transform
270
+ * @param <T> of object into which to transform
271
+ * @return an object of the requested type
272
+ * @throws IOException if there is any problem during the transformation
273
+ */
274
+ public <T > T getBufferAsObject (Mono <DataBuffer > contents , TypeReference <T > type ) throws IOException {
275
+ DataBuffer buffer = contents .block ();
276
+ if (buffer != null ) {
277
+ return mapper .readValue (buffer .asInputStream (true ), type );
278
+ }
279
+ return null ;
280
+ }
281
+
245
282
private void setBearerFromUsernameAndPassword () {
246
283
AuthorizationApi auth = new AuthorizationApi (this );
247
284
LoginCredentials cred = new LoginCredentials ();
@@ -487,9 +524,39 @@ public <T> Mono<T> invokeAPI(
487
524
HttpHeaders headerParams , MultiValueMap <String , String > cookieParams ,
488
525
MultiValueMap <String , Object > formParams , List <MediaType > accept ,
489
526
MediaType contentType , ParameterizedTypeReference <T > returnType ) throws RestClientException {
527
+ return invokeAPI (path , method , pathParams , queryParams , body ,
528
+ headerParams , cookieParams , formParams , accept , contentType ,
529
+ returnType , false );
530
+ }
531
+
532
+ /**
533
+ * Invoke API by sending HTTP request with the given options.
534
+ *
535
+ * @param <T> the return type to use
536
+ * @param path The sub-path of the HTTP URL
537
+ * @param method The request method
538
+ * @param pathParams The path parameters
539
+ * @param queryParams The query parameters
540
+ * @param body The request body object
541
+ * @param headerParams The header parameters
542
+ * @param cookieParams The cookie parameters
543
+ * @param formParams The form parameters
544
+ * @param accept The request's Accept header
545
+ * @param contentType The request's Content-Type header
546
+ * @param returnType The return type into which to deserialize the response
547
+ * @param encoded true if the parameters are already URL-encoded, false (default) otherwise
548
+ * @return The response body in chosen type
549
+ */
550
+ public <T > Mono <T > invokeAPI (
551
+ String path , HttpMethod method , Map <String , Object > pathParams ,
552
+ MultiValueMap <String , String > queryParams , Object body ,
553
+ HttpHeaders headerParams , MultiValueMap <String , String > cookieParams ,
554
+ MultiValueMap <String , Object > formParams , List <MediaType > accept ,
555
+ MediaType contentType , ParameterizedTypeReference <T > returnType ,
556
+ boolean encoded ) throws RestClientException {
490
557
final WebClient .RequestBodySpec requestBuilder = prepareRequest (
491
558
path , method , pathParams , queryParams , body , headerParams , cookieParams ,
492
- formParams , accept , contentType );
559
+ formParams , accept , contentType , encoded );
493
560
return requestBuilder .retrieve ().bodyToMono (returnType );
494
561
}
495
562
@@ -645,6 +712,16 @@ private WebClient.RequestBodySpec prepareRequest(
645
712
HttpHeaders headerParams , MultiValueMap <String , String > cookieParams ,
646
713
MultiValueMap <String , Object > formParams , List <MediaType > accept ,
647
714
MediaType contentType ) {
715
+ return prepareRequest (path , method , pathParams , queryParams , body ,
716
+ headerParams , cookieParams , formParams , accept , contentType , false );
717
+ }
718
+
719
+ private WebClient .RequestBodySpec prepareRequest (
720
+ String path , HttpMethod method , Map <String , Object > pathParams ,
721
+ MultiValueMap <String , String > queryParams , Object body ,
722
+ HttpHeaders headerParams , MultiValueMap <String , String > cookieParams ,
723
+ MultiValueMap <String , Object > formParams , List <MediaType > accept ,
724
+ MediaType contentType , boolean encoded ) {
648
725
updateParamsForAuth (queryParams , headerParams , cookieParams );
649
726
650
727
final UriComponentsBuilder builder =
@@ -653,9 +730,18 @@ private WebClient.RequestBodySpec prepareRequest(
653
730
builder .queryParams (queryParams );
654
731
}
655
732
656
- final WebClient .RequestBodySpec requestBuilder =
657
- webClient .method (method ).uri (builder .build (false ).toUriString (),
658
- pathParams );
733
+ // Spring / URL encoding handling is an absolute nightmare -- just expecting anyone that passes through
734
+ // already-encoded query parameters to have already done their own replacement of any path parameters, as
735
+ // mixing the two basically appears to be impossible without writing your own string parsing logic.
736
+ WebClient .RequestBodySpec requestBuilder ;
737
+ if (encoded ) {
738
+ URI uri = builder .build (true ).toUri ();
739
+ requestBuilder = webClient .method (method ).uri (uri );
740
+ } else {
741
+ String uri = builder .build (false ).toUriString ();
742
+ requestBuilder = webClient .method (method ).uri (uri , pathParams );
743
+ }
744
+
659
745
if (accept != null ) {
660
746
requestBuilder .accept (accept .toArray (new MediaType [accept .size ()]));
661
747
}
0 commit comments