@@ -78,9 +78,10 @@ public LibraryVersion(@NonNull String name, @NonNull String version) {
78
78
79
79
@ Override
80
80
public boolean equals (Object object ) {
81
- if (!(object instanceof LibraryVersion ))
81
+ if (!(object instanceof LibraryVersion )) {
82
82
return false ;
83
- LibraryVersion other = (LibraryVersion )object ;
83
+ }
84
+ LibraryVersion other = (LibraryVersion ) object ;
84
85
return this .name .equals (other .name ) && this .version .equals (other .version );
85
86
}
86
87
@@ -150,7 +151,8 @@ private static class HostStatus {
150
151
protected ExecutorService searchExecutorService = Executors .newFixedThreadPool (4 );
151
152
152
153
/** Executor used to run completion handlers. By default, runs on the main thread. */
153
- protected @ NonNull Executor completionExecutor = new HandlerExecutor (new Handler (Looper .getMainLooper ()));
154
+ protected @ NonNull
155
+ Executor completionExecutor = new HandlerExecutor (new Handler (Looper .getMainLooper ()));
154
156
155
157
protected Map <String , WeakReference <Object >> indices = new HashMap <>();
156
158
@@ -162,19 +164,21 @@ private static class HostStatus {
162
164
* Create a new client.
163
165
*
164
166
* @param applicationID [optional] The application ID.
165
- * @param apiKey [optional] A valid API key for the service.
166
- * @param readHosts List of hosts for read operations.
167
- * @param writeHosts List of hosts for write operations.
167
+ * @param apiKey [optional] A valid API key for the service.
168
+ * @param readHosts List of hosts for read operations.
169
+ * @param writeHosts List of hosts for write operations.
168
170
*/
169
171
protected AbstractClient (@ Nullable String applicationID , @ Nullable String apiKey , @ Nullable String [] readHosts , @ Nullable String [] writeHosts ) {
170
172
this .applicationID = applicationID ;
171
173
this .apiKey = apiKey ;
172
174
this .addUserAgent (new LibraryVersion ("Algolia for Android" , version ));
173
175
this .addUserAgent (new LibraryVersion ("Android" , Build .VERSION .RELEASE ));
174
- if (readHosts != null )
176
+ if (readHosts != null ) {
175
177
setReadHosts (readHosts );
176
- if (writeHosts != null )
178
+ }
179
+ if (writeHosts != null ) {
177
180
setWriteHosts (writeHosts );
181
+ }
178
182
}
179
183
180
184
// ----------------------------------------------------------------------
@@ -322,7 +326,9 @@ public void setHostDownDelay(int hostDownDelay) {
322
326
* @param userAgent The library to add.
323
327
*/
324
328
public void addUserAgent (@ NonNull LibraryVersion userAgent ) {
325
- userAgents .add (userAgent );
329
+ if (!userAgents .contains (userAgent )) {
330
+ userAgents .add (userAgent );
331
+ }
326
332
updateUserAgents ();
327
333
}
328
334
@@ -398,28 +404,28 @@ private enum Method {
398
404
GET , POST , PUT , DELETE
399
405
}
400
406
401
- protected byte [] getRequestRaw (String url , boolean search ) throws AlgoliaException {
402
- return _requestRaw (Method .GET , url , null , getReadHostsThatAreUp (), connectTimeout , search ? searchTimeout : readTimeout );
407
+ protected byte [] getRequestRaw (@ NonNull String url , @ Nullable Map < String , String > urlParameters , boolean search , @ Nullable RequestOptions requestOptions ) throws AlgoliaException {
408
+ return _requestRaw (Method .GET , url , urlParameters , /* json: */ null , getReadHostsThatAreUp (), connectTimeout , search ? searchTimeout : readTimeout , requestOptions );
403
409
}
404
410
405
- protected JSONObject getRequest (String url , boolean search ) throws AlgoliaException {
406
- return _request (Method .GET , url , null , getReadHostsThatAreUp (), connectTimeout , search ? searchTimeout : readTimeout );
411
+ protected JSONObject getRequest (@ NonNull String url , @ Nullable Map < String , String > urlParameters , boolean search , @ Nullable RequestOptions requestOptions ) throws AlgoliaException {
412
+ return _request (Method .GET , url , urlParameters , /* json: */ null , getReadHostsThatAreUp (), connectTimeout , search ? searchTimeout : readTimeout , requestOptions );
407
413
}
408
414
409
- protected JSONObject deleteRequest (String url ) throws AlgoliaException {
410
- return _request (Method .DELETE , url , null , getWriteHostsThatAreUp (), connectTimeout , readTimeout );
415
+ protected JSONObject deleteRequest (@ NonNull String url , @ Nullable Map < String , String > urlParameters , @ Nullable RequestOptions requestOptions ) throws AlgoliaException {
416
+ return _request (Method .DELETE , url , urlParameters , /* json: */ null , getWriteHostsThatAreUp (), connectTimeout , readTimeout , requestOptions );
411
417
}
412
418
413
- protected JSONObject postRequest (String url , String obj , boolean readOperation ) throws AlgoliaException {
414
- return _request (Method .POST , url , obj , (readOperation ? getReadHostsThatAreUp () : getWriteHostsThatAreUp ()), connectTimeout , (readOperation ? searchTimeout : readTimeout ));
419
+ protected JSONObject postRequest (@ NonNull String url , @ Nullable Map < String , String > urlParameters , @ Nullable String obj , boolean readOperation , @ Nullable RequestOptions requestOptions ) throws AlgoliaException {
420
+ return _request (Method .POST , url , urlParameters , obj , (readOperation ? getReadHostsThatAreUp () : getWriteHostsThatAreUp ()), connectTimeout , (readOperation ? searchTimeout : readTimeout ), requestOptions );
415
421
}
416
422
417
- protected byte [] postRequestRaw (String url , String obj , boolean readOperation ) throws AlgoliaException {
418
- return _requestRaw (Method .POST , url , obj , (readOperation ? getReadHostsThatAreUp () : getWriteHostsThatAreUp ()), connectTimeout , (readOperation ? searchTimeout : readTimeout ));
423
+ protected byte [] postRequestRaw (@ NonNull String url , @ Nullable Map < String , String > urlParameters , @ Nullable String obj , boolean readOperation , @ Nullable RequestOptions requestOptions ) throws AlgoliaException {
424
+ return _requestRaw (Method .POST , url , urlParameters , obj , (readOperation ? getReadHostsThatAreUp () : getWriteHostsThatAreUp ()), connectTimeout , (readOperation ? searchTimeout : readTimeout ), requestOptions );
419
425
}
420
426
421
- protected JSONObject putRequest (String url , String obj ) throws AlgoliaException {
422
- return _request (Method .PUT , url , obj , getWriteHostsThatAreUp (), connectTimeout , readTimeout );
427
+ protected JSONObject putRequest (@ NonNull String url , @ Nullable Map < String , String > urlParameters , @ NonNull String obj , @ Nullable RequestOptions requestOptions ) throws AlgoliaException {
428
+ return _request (Method .PUT , url , urlParameters , obj , getWriteHostsThatAreUp (), connectTimeout , readTimeout , requestOptions );
423
429
}
424
430
425
431
/**
@@ -444,6 +450,7 @@ private static String _toCharArray(InputStream stream) throws IOException {
444
450
445
451
/**
446
452
* Reads the InputStream into a byte array
453
+ *
447
454
* @param stream the InputStream to read
448
455
* @return the stream's content as a byte[]
449
456
* @throws AlgoliaException if the stream can't be read or flushed
@@ -482,17 +489,18 @@ private static JSONObject _getAnswerJSONObject(InputStream istream) throws IOExc
482
489
* Send the query according to parameters and returns its result as a JSONObject
483
490
*
484
491
* @param m HTTP Method to use
485
- * @param url endpoint URL
492
+ * @param url Endpoint URL, *without query string*. The query string is handled by `urlParameters`.
493
+ * @param urlParameters URL parameters
486
494
* @param json optional JSON Object to send
487
495
* @param hostsArray array of hosts to try successively
488
496
* @param connectTimeout maximum wait time to open connection
489
497
* @param readTimeout maximum time to read data on socket
490
498
* @return a JSONObject containing the resulting data or error
491
499
* @throws AlgoliaException if the request data is not valid json
492
500
*/
493
- private JSONObject _request (Method m , String url , String json , List <String > hostsArray , int connectTimeout , int readTimeout ) throws AlgoliaException {
501
+ private JSONObject _request (@ NonNull Method m , @ NonNull String url , @ Nullable Map < String , String > urlParameters , @ Nullable String json , @ NonNull List <String > hostsArray , int connectTimeout , int readTimeout , @ Nullable RequestOptions requestOptions ) throws AlgoliaException {
494
502
try {
495
- return _getJSONObject (_requestRaw (m , url , json , hostsArray , connectTimeout , readTimeout ));
503
+ return _getJSONObject (_requestRaw (m , url , urlParameters , json , hostsArray , connectTimeout , readTimeout , requestOptions ));
496
504
} catch (JSONException e ) {
497
505
throw new AlgoliaException ("JSON decode error:" + e .getMessage ());
498
506
} catch (UnsupportedEncodingException e ) {
@@ -504,15 +512,16 @@ private JSONObject _request(Method m, String url, String json, List<String> host
504
512
* Send the query according to parameters and returns its result as a JSONObject
505
513
*
506
514
* @param m HTTP Method to use
507
- * @param url endpoint URL
508
- * @param json optional JSON Object to send
515
+ * @param url Endpoint URL, *without query string*. The query string is handled by `urlParameters`.
516
+ * @param urlParameters URL parameters
517
+ * @param json (optional) JSON body
509
518
* @param hostsArray array of hosts to try successively
510
519
* @param connectTimeout maximum wait time to open connection
511
520
* @param readTimeout maximum time to read data on socket
512
521
* @return a JSONObject containing the resulting data or error
513
522
* @throws AlgoliaException in case of connection or data handling error
514
523
*/
515
- private byte [] _requestRaw (Method m , String url , String json , List <String > hostsArray , int connectTimeout , int readTimeout ) throws AlgoliaException {
524
+ private byte [] _requestRaw (@ NonNull Method m , @ NonNull String url , @ Nullable Map < String , String > urlParameters , @ Nullable String json , @ NonNull List <String > hostsArray , int connectTimeout , int readTimeout , @ Nullable RequestOptions requestOptions ) throws AlgoliaException {
516
525
String requestMethod ;
517
526
List <Exception > errors = new ArrayList <>(hostsArray .size ());
518
527
// for each host
@@ -536,17 +545,32 @@ private byte[] _requestRaw(Method m, String url, String json, List<String> hosts
536
545
537
546
InputStream stream = null ;
538
547
HttpURLConnection hostConnection = null ;
539
- // set URL
540
548
try {
541
- URL hostURL = new URL ("https://" + host + url );
549
+ // Compute final URL parameters.
550
+ final Map <String , String > parameters = new HashMap <>();
551
+ if (urlParameters != null ) {
552
+ parameters .putAll (urlParameters );
553
+ }
554
+ if (requestOptions != null ) {
555
+ parameters .putAll (requestOptions .urlParameters );
556
+ }
557
+
558
+ // Build URL.
559
+ String urlString = "https://" + host + url ;
560
+ if (!parameters .isEmpty ()) {
561
+ urlString += "?" + AbstractQuery .build (parameters );
562
+ }
563
+ URL hostURL = new URL (urlString );
564
+
565
+ // Open connection.
542
566
hostConnection = (HttpURLConnection ) hostURL .openConnection ();
543
567
544
568
//set timeouts
545
569
hostConnection .setRequestMethod (requestMethod );
546
570
hostConnection .setConnectTimeout (connectTimeout );
547
571
hostConnection .setReadTimeout (readTimeout );
548
572
549
- // set auth headers
573
+ // Headers
550
574
hostConnection .setRequestProperty ("X-Algolia-Application-Id" , this .applicationID );
551
575
// If API key is too big, send it in the request's body (if applicable).
552
576
if (this .apiKey != null && this .apiKey .length () > MAX_API_KEY_LENGTH && json != null ) {
@@ -560,9 +584,16 @@ private byte[] _requestRaw(Method m, String url, String json, List<String> hosts
560
584
} else {
561
585
hostConnection .setRequestProperty ("X-Algolia-API-Key" , this .apiKey );
562
586
}
587
+ // Client-level headers
563
588
for (Map .Entry <String , String > entry : this .headers .entrySet ()) {
564
589
hostConnection .setRequestProperty (entry .getKey (), entry .getValue ());
565
590
}
591
+ // Request-level headers
592
+ if (requestOptions != null ) {
593
+ for (Map .Entry <String , String > entry : requestOptions .headers .entrySet ()) {
594
+ hostConnection .setRequestProperty (entry .getKey (), entry .getValue ());
595
+ }
596
+ }
566
597
567
598
// set user agent
568
599
hostConnection .setRequestProperty ("User-Agent" , userAgentRaw );
@@ -614,12 +645,10 @@ private byte[] _requestRaw(Method m, String url, String json, List<String> hosts
614
645
}
615
646
return rawResponse ;
616
647
617
- }
618
- catch (JSONException e ) { // fatal
648
+ } catch (JSONException e ) { // fatal
619
649
consumeQuietly (hostConnection );
620
650
throw new AlgoliaException ("Invalid JSON returned by server" , e );
621
- }
622
- catch (UnsupportedEncodingException e ) { // fatal
651
+ } catch (UnsupportedEncodingException e ) { // fatal
623
652
consumeQuietly (hostConnection );
624
653
throw new AlgoliaException ("Invalid encoding returned by server" , e );
625
654
} catch (IOException e ) { // host error, continue on the next host
@@ -673,6 +702,7 @@ private void checkTimeout(int connectTimeout) {
673
702
674
703
/**
675
704
* Get the hosts that are not considered down in a given list.
705
+ *
676
706
* @param hosts a list of hosts whose {@link HostStatus} will be checked.
677
707
* @return the hosts considered up, or all hosts if none is known to be reachable.
678
708
*/
@@ -703,7 +733,7 @@ abstract protected class AsyncTaskRequest extends FutureRequest {
703
733
* Construct a new request with the specified completion handler, executing on the client's search executor,
704
734
* and calling the completion handler on the client's completion executor.
705
735
*
706
- * @param completionHandler The completion handler to be notified of results. May be null if the caller omitted it.
736
+ * @param completionHandler The completion handler to be notified of results. May be null if the caller omitted it.
707
737
*/
708
738
protected AsyncTaskRequest (@ Nullable CompletionHandler completionHandler ) {
709
739
this (completionHandler , searchExecutorService );
@@ -713,8 +743,8 @@ protected AsyncTaskRequest(@Nullable CompletionHandler completionHandler) {
713
743
* Construct a new request with the specified completion handler, executing on the specified executor, and
714
744
* calling the completion handler on the client's completion executor.
715
745
*
716
- * @param completionHandler The completion handler to be notified of results. May be null if the caller omitted it.
717
- * @param requestExecutor Executor on which to execute the request.
746
+ * @param completionHandler The completion handler to be notified of results. May be null if the caller omitted it.
747
+ * @param requestExecutor Executor on which to execute the request.
718
748
*/
719
749
protected AsyncTaskRequest (@ Nullable CompletionHandler completionHandler , @ NonNull Executor requestExecutor ) {
720
750
super (completionHandler , requestExecutor , completionExecutor );
0 commit comments