diff --git a/example/example.dart b/example/example.dart index 634300d..b9ff1d4 100644 --- a/example/example.dart +++ b/example/example.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:algolia/algolia.dart'; void main() async { @@ -160,3 +161,44 @@ class Application { apiKey: 'YOUR_API_KEY', ); } + +class IsolateDecodingAlgolia extends Algolia { + IsolateDecodingAlgolia.init({ + required String applicationId, + required String apiKey, + Map extraHeaders = const {}, + List extraUserAgents = const [], + }) : super.init( + applicationId: applicationId, + apiKey: apiKey, + extraHeaders: extraHeaders, + extraUserAgents: extraUserAgents, + ); + + /// Override the default JSON decoder with an Isolated one + @override + Future decodeJson( + String source, { + Object? Function(Object? key, Object? value)? reviver, + }) { + /// In Flutter you can simply return + /// + /// ```dart + /// return compute(json.decode, source); + /// ``` + /// + /// In pure Dart <2.19 you have to import the compute package + /// https://pub.dev/packages/compute + /// + /// Dart 2.19 will come with Isolate.run + /// + /// ```dart + /// return Isolate.run(json.decode, source); + /// ``` + /// + /// Consider also using a worker pool using the excellent Squadron package + /// https://pub.dev/packages/squadron + + return json.decode(source, reviver: reviver); + } +} diff --git a/lib/algolia.dart b/lib/algolia.dart index 40a8ee3..a50b626 100644 --- a/lib/algolia.dart +++ b/lib/algolia.dart @@ -4,8 +4,8 @@ import 'dart:async'; import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:uuid/uuid.dart'; import 'package:universal_io/io.dart' show Platform; +import 'package:uuid/uuid.dart'; part 'src/algolia.dart'; part 'src/batch.dart'; @@ -17,9 +17,9 @@ part 'src/index_settings.dart'; part 'src/index_snapshot.dart'; part 'src/object_reference.dart'; part 'src/object_snapshot.dart'; -part 'src/synonyms_reference.dart'; part 'src/query.dart'; part 'src/query_snapshot.dart'; +part 'src/synonyms_reference.dart'; part 'src/task.dart'; -part 'src/util/json_encode.dart'; part 'src/util/enum_util.dart'; +part 'src/util/json_encode.dart'; diff --git a/lib/src/algolia.dart b/lib/src/algolia.dart index 0eb9127..8a7d470 100644 --- a/lib/src/algolia.dart +++ b/lib/src/algolia.dart @@ -210,7 +210,7 @@ class Algolia { ApiRequestType.get, 'indexes', ); - Map body = json.decode(response.body); + Map body = await decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -247,10 +247,29 @@ class Algolia { body: utf8.encode(json.encode({'events': eventList})), encoding: Encoding.getByName('utf-8'), ); - Map body = json.decode(response.body); + Map body = await decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); } } + + /// This method can be overridden to allow decoding JSON in an isolate, + /// for example using Flutter compute: + /// + /// ```dart + /// @override + /// Future decodeJson( + /// String source, { + /// Object? Function(Object? key, Object? value)? reviver, + /// }) => + /// compute(json.decode); + /// ``` + /// + /// Read more about background JSON parsing here https://docs.flutter.dev/cookbook/networking/background-parsing + FutureOr decodeJson( + String source, { + Object? Function(Object? key, Object? value)? reviver, + }) => + json.decode(source, reviver: reviver); } diff --git a/lib/src/batch.dart b/lib/src/batch.dart index 4d10249..c105d29 100644 --- a/lib/src/batch.dart +++ b/lib/src/batch.dart @@ -69,7 +69,7 @@ class AlgoliaBatch { data: {'requests': actions}, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); diff --git a/lib/src/index_reference.dart b/lib/src/index_reference.dart index 29323b3..ac0f49c 100644 --- a/lib/src/index_reference.dart +++ b/lib/src/index_reference.dart @@ -94,7 +94,7 @@ class AlgoliaIndexReference extends AlgoliaQuery { 'maxFacetHits': maxFacetHits, }, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 500)) { throw AlgoliaError._(body, response.statusCode); } @@ -154,7 +154,7 @@ class AlgoliaIndexReference extends AlgoliaQuery { 'indexes/$encodedIndex', data: data, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -194,7 +194,7 @@ class AlgoliaIndexReference extends AlgoliaQuery { 'indexes/*/objects', data: {'requests': objects}, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -215,7 +215,7 @@ class AlgoliaIndexReference extends AlgoliaQuery { ApiRequestType.post, 'indexes/$encodedIndex/clear', ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -264,7 +264,7 @@ class AlgoliaIndexReference extends AlgoliaQuery { 'indexes/$encodedIndex/operation', data: data, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -312,7 +312,7 @@ class AlgoliaIndexReference extends AlgoliaQuery { ApiRequestType.delete, 'indexes/$encodedIndex', ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); } @@ -399,7 +399,7 @@ class AlgoliaMultiIndexesReference { 'strategy': 'none', }, ); - Map body = json.decode(response.body); + Map body = await _algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); diff --git a/lib/src/index_settings.dart b/lib/src/index_settings.dart index c5fbc09..886a555 100644 --- a/lib/src/index_settings.dart +++ b/lib/src/index_settings.dart @@ -22,7 +22,7 @@ class AlgoliaIndexSettings extends AlgoliaSettings { ApiRequestType.get, 'indexes/$encodedIndex/settings', ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -44,6 +44,7 @@ class AlgoliaSettings { final Algolia algolia; final String _index; final Map _parameters; + String get encodedIndex => Uri.encodeFull(_index); AlgoliaSettings _copyWithParameters(Map parameters) { @@ -74,7 +75,7 @@ class AlgoliaSettings { 'indexes/$encodedIndex/settings', data: _parameters, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); } diff --git a/lib/src/object_reference.dart b/lib/src/object_reference.dart index 5474f02..1087daf 100644 --- a/lib/src/object_reference.dart +++ b/lib/src/object_reference.dart @@ -27,7 +27,7 @@ class AlgoliaObjectReference { ApiRequestType.get, 'indexes/$encodedIndex/$encodedObjectID', ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); } @@ -56,7 +56,7 @@ class AlgoliaObjectReference { url, data: data, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -87,7 +87,7 @@ class AlgoliaObjectReference { url, data: data, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -130,7 +130,7 @@ class AlgoliaObjectReference { url, data: data, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); } @@ -153,7 +153,7 @@ class AlgoliaObjectReference { ApiRequestType.delete, url, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); } diff --git a/lib/src/query.dart b/lib/src/query.dart index b6c6114..5a711a6 100644 --- a/lib/src/query.dart +++ b/lib/src/query.dart @@ -79,7 +79,7 @@ class AlgoliaQuery { 'indexes/$encodedIndex/query', data: _parameters, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); } @@ -110,7 +110,7 @@ class AlgoliaQuery { 'indexes/$encodedIndex/deleteByQuery', data: _parameters, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); } diff --git a/lib/src/synonyms_reference.dart b/lib/src/synonyms_reference.dart index d1b7aba..97b9183 100644 --- a/lib/src/synonyms_reference.dart +++ b/lib/src/synonyms_reference.dart @@ -26,6 +26,7 @@ extension ExtensionDietaryType on SynonymsType { /// class AlgoliaSynonymsReference { const AlgoliaSynonymsReference._(this.algolia, this.index); + final String index; final Algolia algolia; @@ -68,7 +69,7 @@ class AlgoliaSynonymsReference { 'indexes/$encodedIndex/synonyms/search', data: data, ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 500)) { throw AlgoliaError._(body, response.statusCode); } @@ -124,7 +125,7 @@ class AlgoliaSynonymsReference { 'indexes/$encodedIndex/synonyms/${synonyms.objectID}?forwardToReplicas=${synonyms.forwardToReplicas}', data: synonyms.toMap(), ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -144,7 +145,7 @@ class AlgoliaSynonymsReference { 'indexes/$encodedIndex/synonyms/batch', data: synonyms.map((e) => e.toMap()).toList(), ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); } @@ -161,7 +162,7 @@ class AlgoliaSynonymsReference { ApiRequestType.get, 'indexes/$encodedIndex/synonyms/$objectID', ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -179,7 +180,7 @@ class AlgoliaSynonymsReference { ApiRequestType.post, 'indexes/$encodedIndex/synonyms/clear', ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -197,7 +198,7 @@ class AlgoliaSynonymsReference { ApiRequestType.post, 'indexes/$encodedIndex/synonyms/$objectID', ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); @@ -255,6 +256,7 @@ class AlgoliaSynonyms { final String? word; final String? placeholder; final bool forwardToReplicas; + const AlgoliaSynonyms({ required this.objectID, required this.type, diff --git a/lib/src/task.dart b/lib/src/task.dart index d704306..3824749 100644 --- a/lib/src/task.dart +++ b/lib/src/task.dart @@ -32,7 +32,7 @@ class AlgoliaTask { ApiRequestType.get, 'indexes/$_index/task/$taskID', ); - Map body = json.decode(response.body); + Map body = await algolia.decodeJson(response.body); if (!(response.statusCode >= 200 && response.statusCode < 300)) { throw AlgoliaError._(body, response.statusCode); }