feat(dart): add algolia_chopper_requester package
🧠What and Why
In this PR I have created a custom Requester based on Chopper.
Why? Dio is great, but if my Flutter app uses http I now have to have 2 different clients. 🙈 On top of that Chopper gives the user the option to provide their own Client.
final Requester chopperRequester = ChopperRequester({
/// Your Algolia Application ID
required String appId,
/// Your Algolia Search-Only API Key
required String apiKey,
/// Additional headers to send with the request
Map<String, dynamic>? headers,
/// The segments to include in the `User-Agent` header
Iterable<AgentSegment>? clientSegments,
/// The logger to use for debugging
Logger? logger,
/// The Chopper Interceptors to use for modifying the request
Iterable<Interceptor>? interceptors,
/// The HTTP client to use for sending requests
/// Will use https://pub.dev/packages/http by default
/// Accepts any [Client], for example https://pub.dev/packages/cupertino_http
/// or https://pub.dev/packages/cronet_http
Client? client,
/// A custom JSON converter to use for serializing and deserializing JSON
JsonConverter? converter,
});
Basic Usage
final String appId = 'latency';
final String apiKey = '6be0576ff61c053d5f9a3225e2a90f76';
final SearchClient _client = SearchClient(
appId: appId,
apiKey: apiKey,
options: ClientOptions(
requester: ChopperRequester(
appId: appId,
apiKey: apiKey,
)
),
);
Future<SearchResponse> search(String query) => _client.searchIndex(
request: SearchForHits(
indexName: 'flutter',
query: query,
hitsPerPage: 5,
),
);
Advanced Usage
To set the connect timeout one has to do that directly on the Client, i.e.
final requester = ChopperRequester(
appId: appId,
apiKey: apiKey,
client: http.IOClient(
HttpClient()..connectionTimeout = const Duration(seconds: 60),
),
);
Custom Interceptors
For interceptors please see the Chopper documentation.
Custom Clients
Via the client option users can use platform specific HTTP clients such:
-
cronet_http on Android
final requester = ChopperRequester( appId: appId, apiKey: apiKey, client: CronetClient.fromCronetEngine( CronetEngine.build( cacheMode: CacheMode.memory, cacheMaxSize: 50 * 1024 * 1024, ), closeEngine: true, ), ); -
cupertino_http on iOS/macOS
final requester = ChopperRequester( appId: appId, apiKey: apiKey, client: CupertinoClient.fromSessionConfiguration( (URLSessionConfiguration.defaultSessionConfiguration() ..timeoutIntervalForRequest = const Duration(seconds: 30)), ), );
NOTE: Custom HTTP clients must be manully disposed of, i.e.
final http.Client client = IOClient(
HttpClient()..connectionTimeout = const Duration(seconds: 30),
);
/// ... use client ...
client.close();
Parsing JSON in the background using Isolates
Parsing JSON in the background is a good idea if you don't want to block the main thread. Please see the Chopper documentation on Decoding JSON using Isolate worker pools.
Changes included:
- export
AlgoliaAgentfromalgolia_client_corepackage - add new package
algolia_chopper_requester
Full disclosure: I'm one of the maintainers of Chopper. Supersedes https://github.com/algolia/algoliasearch-client-dart/pull/14
Since I've added a completely new package here there were a few defaults I chose, which should probably be modified in this PR:
- the package's version is
1.0.0and not1.15.1like the other of the Dart packages - the LICENSE of the package is MIT
Both of these should be reviewed and potentially changed.
Since I've added a completely new package here there were a few defaults I chose, which should probably be modified in this PR:
- the package's version is
1.0.0and not1.15.1like the other of the Dart packages- the LICENSE of the package is MIT
Both of these should be reviewed and potentially changed.
Ah nice I was actually commenting on that, do you want to do it with the pointers I've gave or should I take over this part?
@shortcuts I think I've done all the tasks. I'm just not sure about https://github.com/algolia/api-clients-automation/pull/3291/commits/d812167a58d39acfc587d088f84710062038d5bf 😅
I synced version.dart with the Dart package version manually. Not sure if I should delete it since it's a dependency of chopper_requester.dart.
The only annoying bit left to fix is the fact that the user has to provide their appId and apiKey twice now, i.e.
final SearchClient _client = SearchClient(
appId: appId, // <-- here
apiKey: apiKey, // <-- here
options: ClientOptions(
requester: ChopperRequester(
appId: appId, // <-- and here
apiKey: apiKey, // <-- and here
)
),
);
If only there were a way for these credentials to be passed down from the SearchClient to the ClientOptions.
CC/ @shortcuts @aallam
@shortcuts Looks like the query parameters in HttpRequest.queryParameters are already encoded.
https://github.com/algolia/api-clients-automation/blob/d4ffebaf4ed8722eddd7f4127bdd0813df0a08d8/clients/algoliasearch-client-dart/packages/client_core/lib/src/transport/retry_strategy.dart#L117
Chopper expects them to be unencoded, so I copied over the DioRequester.requestUri method to consturct the full Uri before feeding it to Chopper which should take care of this issue.
The only annoying bit left to fix is the fact that the user has to provide their
appIdandapiKeytwice now, i.e.final SearchClient _client = SearchClient( appId: appId, // <-- here apiKey: apiKey, // <-- here options: ClientOptions( requester: ChopperRequester( appId: appId, // <-- and here apiKey: apiKey, // <-- and here ) ), );If only there were a way for these credentials to be passed down from the
SearchClientto theClientOptions.
Indeed, this could be improved; however, I think this should be done in a separate PR since it's not specific to this package
Indeed, this could be improved; however, I think this should be done in a separate PR since it's not specific to this package
Agreed.
Almost there to make CI work on forks! We will try to get that sorted out soon, thanks a lot for your contribution :D
Almost there to make CI work on forks! We will try to get that sorted out soon, thanks a lot for your contribution :D
@shortcuts any update on the CI thingy?
Regarding testing, I've been successfully using this "package" in my app for months now in order to get around some of the Dio limitations when it comes to invalid / self-signed SSL certificates etc and it works like a charm.