@@ -17,6 +17,7 @@ package au.org.ala.ecodata.forms
17
17
18
18
19
19
import au.org.ala.ws.tokens.TokenService
20
+ import com.fasterxml.jackson.databind.ObjectMapper
20
21
import grails.converters.JSON
21
22
import grails.core.GrailsApplication
22
23
import grails.web.http.HttpHeaders
@@ -32,6 +33,8 @@ import org.apache.http.entity.mime.content.StringBody
32
33
import org.grails.web.converters.exceptions.ConverterException
33
34
import org.springframework.http.MediaType
34
35
import org.springframework.web.multipart.MultipartFile
36
+ import okhttp3.*
37
+ import java.util.concurrent.TimeUnit
35
38
36
39
import javax.annotation.PostConstruct
37
40
import javax.servlet.http.Cookie
@@ -56,13 +59,19 @@ class EcpWebService {
56
59
static String AUTHORIZATION_HEADER_TYPE_EXTERNAL_TOKEN = ' externalToken'
57
60
58
61
static String AUTHORIZATION_HEADER_TYPE_NONE = ' none'
62
+ OkHttpClient client
59
63
List WHITE_LISTED_DOMAINS = []
60
64
61
65
TokenService tokenService
62
66
@PostConstruct
63
67
void init () {
64
68
String whiteListed = grailsApplication. config. getProperty(' app.domain.whiteList' , " " )
65
69
WHITE_LISTED_DOMAINS = Arrays . asList(whiteListed. split(' ,' ))
70
+ client = new OkHttpClient.Builder ()
71
+ .connectTimeout(connectTimeout(), TimeUnit . MILLISECONDS )
72
+ .readTimeout(defaultTimeout(), TimeUnit . MILLISECONDS )
73
+ .writeTimeout(defaultTimeout(), TimeUnit . MILLISECONDS )
74
+ .build()
66
75
}
67
76
68
77
// Used to avoid a circular dependency during initialisation
@@ -382,7 +391,7 @@ class EcpWebService {
382
391
return urlConnection. content. getText(getCharset(urlConnection))
383
392
}
384
393
385
- def doPostWithParams (String url , Map params ) {
394
+ def doPostWithParams (String url , Map params , boolean userToken = false ) {
386
395
def conn = null
387
396
def charEncoding = ' utf-8'
388
397
try {
@@ -399,7 +408,7 @@ class EcpWebService {
399
408
conn. setRequestProperty(" Content-Type" , " application/x-www-form-urlencoded" )
400
409
if (canAddSecret(url)) {
401
410
if (useJWT())
402
- addTokenHeader(conn)
411
+ addTokenHeader(conn, userToken )
403
412
else
404
413
conn. setRequestProperty(" Authorization" , grailsApplication. config. getProperty(' api_key' ))
405
414
}
@@ -414,13 +423,13 @@ class EcpWebService {
414
423
wr. flush()
415
424
def resp = conn. inputStream. text
416
425
wr. close()
417
- return [resp : JSON . parse(resp?: " {}" )] // fail over to empty json object if empty response string otherwise JSON.parse fails
426
+ return [resp : JSON . parse(resp?: " {}" ), statusCode : conn . responseCode ] // fail over to empty json object if empty response string otherwise JSON.parse fails
418
427
} catch (SocketTimeoutException e) {
419
- def error = [error : " Timed out calling web service. URL= ${ url} ." ]
428
+ def error = [error : " Timed out calling web service. URL= ${ url} ." , statusCode : conn . responseCode ]
420
429
log. error(error as String , e)
421
430
return error
422
431
} catch (SocketException se) {
423
- def error = [error : " Socket connection closed. ${ se.getMessage()} URL= ${ url} ." ]
432
+ def error = [error : " Socket connection closed. ${ se.getMessage()} URL= ${ url} ." , statusCode : conn . responseCode ]
424
433
log. error(error as String , se)
425
434
return error
426
435
} catch (Exception e) {
@@ -433,7 +442,7 @@ class EcpWebService {
433
442
}
434
443
}
435
444
436
- def doPost (String url , Map postBody , boolean useToken = false ) {
445
+ def doPost (String url , Map postBody , boolean useToken = false , boolean userToken = false ) {
437
446
useToken = useToken || useJWT()
438
447
def conn = null
439
448
def charEncoding = ' utf-8'
@@ -443,7 +452,7 @@ class EcpWebService {
443
452
conn. setRequestProperty(" Content-Type" , " application/json;charset=${ charEncoding} " );
444
453
if (canAddSecret(url)) {
445
454
if (useToken) {
446
- addTokenHeader(conn)
455
+ addTokenHeader(conn, userToken )
447
456
} else {
448
457
conn. setRequestProperty(" Authorization" , grailsApplication. config. getProperty(' api_key' ));
449
458
}
@@ -511,6 +520,87 @@ class EcpWebService {
511
520
}
512
521
}
513
522
523
+ /**
524
+ * Uses OkHttp3 to perform a POST request with multipart form data if file provided.
525
+ * @param url
526
+ * @param params
527
+ * @param file
528
+ * @param contentType
529
+ * @param originalFilename
530
+ * @param fileParamName
531
+ * @param useToken
532
+ * @param userToken
533
+ * @return
534
+ */
535
+ Map postMultipartWithOkhttp3 (String url , Map params , File file ,
536
+ String contentType , String originalFilename ,
537
+ String fileParamName = ' files' ,
538
+ boolean useToken = false ,
539
+ boolean userToken = false ) {
540
+ useToken = useToken || useJWT()
541
+ Map result = [:]
542
+ def user = userService. getUser()
543
+ String userIdHeader = grailsApplication. config. getProperty(' app.http.header.userId' )
544
+
545
+ MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder ()
546
+ .setType(MultipartBody . FORM )
547
+
548
+ // Add file part
549
+ if (file) {
550
+ RequestBody fileBody = RequestBody . create(okhttp3.MediaType . parse(contentType), file)
551
+ multipartBodyBuilder. addFormDataPart(fileParamName, originalFilename ?: fileParamName, fileBody)
552
+ }
553
+
554
+ // Add form fields
555
+ params. each { key , value ->
556
+ if (value) multipartBodyBuilder. addFormDataPart(key, value. toString())
557
+ }
558
+
559
+ // Build request
560
+ Request.Builder requestBuilder = new Request.Builder ()
561
+ .url(url)
562
+ .post(multipartBodyBuilder. build())
563
+
564
+
565
+ // Add Authorization headers
566
+ if (canAddSecret(url)) {
567
+ if (useToken) {
568
+ if (useJWT()) {
569
+ requestBuilder. addHeader(" Authorization" , getToken(userToken))
570
+ } else {
571
+ requestBuilder. addHeader(" apiKey" , grailsApplication. config. getProperty(' api_key' ))
572
+ }
573
+ } else {
574
+ requestBuilder. addHeader(" Authorization" , grailsApplication. config. getProperty(' api_key' ))
575
+ }
576
+ }
577
+
578
+ // Add user ID if available
579
+ if (user) {
580
+ requestBuilder. addHeader(userIdHeader, user. userId)
581
+ } else {
582
+ log. warn(" No user associated with request: ${ url} " )
583
+ }
584
+
585
+ // Execute request
586
+ try (Response response = client. newCall(requestBuilder. build()). execute()) {
587
+ String responseBody = response. body(). string()
588
+ ObjectMapper objectMapper = new ObjectMapper ()
589
+ Object responseData = objectMapper. readValue(responseBody, Object )
590
+ result. status = response. code()
591
+ result. statusCode = response. code()
592
+ result. content = responseData
593
+ result. resp = responseData
594
+ } catch (Exception e) {
595
+ result. status = 500
596
+ result. statusCode = 500
597
+ result. error = " Error POSTing to ${ url} "
598
+ result. details = e. message
599
+ }
600
+
601
+ return result
602
+ }
603
+
514
604
/**
515
605
* Forwards a HTTP multipart/form-data request to ecodata.
516
606
* @param url the URL to forward to.
@@ -532,9 +622,11 @@ class EcpWebService {
532
622
* @param originalFilename the original file name of the data to be posted
533
623
* @param fileParamName the name of the HTTP parameter that will be used for the post.
534
624
* @param successHandler optional callback for a successful service invocation. If not supplied, a Map will be returned.
625
+ * @param useToken whether to use the JWT token for the request.
626
+ * @param userToken whether to use the user token or system token for the request.
535
627
* @return [status:<request status>, content:<The response content from the server, assumed to be JSON>
536
628
*/
537
- def postMultipart (url , Map params , InputStream contentIn , contentType , originalFilename , fileParamName = ' files' , Closure successHandler = null , boolean useToken = false ) {
629
+ def postMultipart (url , Map params , InputStream contentIn , contentType , originalFilename , fileParamName = ' files' , Closure successHandler = null , boolean useToken = false , boolean userToken = false ) {
538
630
useToken = useToken || useJWT()
539
631
def result = [:]
540
632
def user = userService. getUser()
@@ -552,7 +644,7 @@ class EcpWebService {
552
644
if (canAddSecret(url)) {
553
645
if (useToken) {
554
646
if (useJWT()) {
555
- headers. ' Authorization' = getToken()
647
+ headers. ' Authorization' = getToken(userToken )
556
648
}
557
649
else {
558
650
headers. ' apiKey' = grailsApplication. config. getProperty(' api_key' )
0 commit comments