Skip to content

Commit

Permalink
Provide an example using an Isolate Worker Pool with Squadron (#361)
Browse files Browse the repository at this point in the history
  • Loading branch information
techouse authored and JEuler committed Oct 8, 2022
1 parent 0c258ef commit 83c8191
Show file tree
Hide file tree
Showing 7 changed files with 462 additions and 0 deletions.
164 changes: 164 additions & 0 deletions example/bin/main_json_serializable_squadron_worker_pool.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/// This example uses
/// - https://github.com/google/json_serializable.dart
/// - https://github.com/d-markey/squadron
/// - https://github.com/d-markey/squadron_builder
import 'dart:async' show FutureOr;
import 'dart:convert' show jsonDecode;

import 'package:chopper/chopper.dart';
import 'package:chopper_example/json_decode_service.dart';
import 'package:chopper_example/json_serializable.dart';
import 'package:http/testing.dart';
import 'package:squadron/squadron.dart';
import 'package:http/http.dart' as http;

import 'main_json_serializable.dart' show authHeader;

typedef JsonFactory<T> = T Function(Map<String, dynamic> json);

/// This JsonConverter works with or without a WorkerPool
class JsonSerializableWorkerPoolConverter extends JsonConverter {
const JsonSerializableWorkerPoolConverter(this.factories, [this.workerPool]);

final Map<Type, JsonFactory> factories;
final JsonDecodeServiceWorkerPool? workerPool;

T? _decodeMap<T>(Map<String, dynamic> values) {
/// Get jsonFactory using Type parameters
/// if not found or invalid, throw error or return null
final jsonFactory = factories[T];
if (jsonFactory == null || jsonFactory is! JsonFactory<T>) {
/// throw serializer not found error;
return null;
}

return jsonFactory(values);
}

List<T> _decodeList<T>(Iterable values) =>
values.where((v) => v != null).map<T>((v) => _decode<T>(v)).toList();

dynamic _decode<T>(entity) {
if (entity is Iterable) return _decodeList<T>(entity as List);

if (entity is Map) return _decodeMap<T>(entity as Map<String, dynamic>);

return entity;
}

@override
FutureOr<Response<ResultType>> convertResponse<ResultType, Item>(
Response response,
) async {
// use [JsonConverter] to decode json
final jsonRes = await super.convertResponse(response);

return jsonRes.copyWith<ResultType>(body: _decode<Item>(jsonRes.body));
}

@override
FutureOr<Response> convertError<ResultType, Item>(Response response) async {
// use [JsonConverter] to decode json
final jsonRes = await super.convertError(response);

return jsonRes.copyWith<ResourceError>(
body: ResourceError.fromJsonFactory(jsonRes.body),
);
}

@override
FutureOr<dynamic> tryDecodeJson(String data) async {
try {
// if there is a worker pool use it, otherwise run in the main thread
return workerPool != null
? await workerPool!.jsonDecode(data)
: jsonDecode(data);
} catch (error) {
print(error);

chopperLogger.warning(error);

return data;
}
}
}

/// Simple client to have working example without remote server
final client = MockClient((http.Request req) async {
if (req.method == 'POST') {
return http.Response('{"type":"Fatal","message":"fatal error"}', 500);
}
if (req.method == 'GET' && req.headers['test'] == 'list') {
return http.Response('[{"id":"1","name":"Foo"}]', 200);
}

return http.Response('{"id":"1","name":"Foo"}', 200);
});

/// inspired by https://github.com/d-markey/squadron_sample/blob/main/lib/main.dart
void initSquadron(String id) {
Squadron.setId(id);
Squadron.setLogger(ConsoleSquadronLogger());
Squadron.logLevel = SquadronLogLevel.all;
Squadron.debugMode = true;
}

Future<void> main() async {
/// initialize Squadron before using it
initSquadron('worker_pool_example');

final jsonDecodeServiceWorkerPool = JsonDecodeServiceWorkerPool(
// Set whatever you want here
concurrencySettings: ConcurrencySettings.oneCpuThread,
);

/// start the Worker Pool
await jsonDecodeServiceWorkerPool.start();

final converter = JsonSerializableWorkerPoolConverter(
{
Resource: Resource.fromJsonFactory,
},
// make sure to provide the WorkerPool to the JsonConverter
jsonDecodeServiceWorkerPool,
);

final chopper = ChopperClient(
client: client,
baseUrl: 'http://localhost:8000',
// bind your object factories here
converter: converter,
errorConverter: converter,
services: [
// the generated service
MyService.create(),
],
/* ResponseInterceptorFunc | RequestInterceptorFunc | ResponseInterceptor | RequestInterceptor */
interceptors: [authHeader],
);

final myService = chopper.getService<MyService>();

/// All of the calls below will use jsonDecode in an Isolate worker
final response1 = await myService.getResource('1');
print('response 1: ${response1.body}'); // undecoded String

final response2 = await myService.getResources();
print('response 2: ${response2.body}'); // decoded list of Resources

final response3 = await myService.getTypedResource();
print('response 3: ${response3.body}'); // decoded Resource

final response4 = await myService.getMapResource('1');
print('response 4: ${response4.body}'); // undecoded Resource

try {
await myService.newResource(Resource('3', 'Super Name'));
} on Response catch (error) {
print(error.body);
}

/// stop the Worker Pool
jsonDecodeServiceWorkerPool.stop();
}
9 changes: 9 additions & 0 deletions example/lib/json_decode_service.activator.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions example/lib/json_decode_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// This example uses https://github.com/d-markey/squadron_builder
import 'dart:async';
import 'dart:convert' show json;

import 'package:squadron/squadron.dart';
import 'package:squadron/squadron_annotations.dart';

import 'json_decode_service.activator.g.dart';

part 'json_decode_service.worker.g.dart';

@SquadronService(
// disable web to keep the number of generated files low for this example
web: false,
)
class JsonDecodeService extends WorkerService
with $JsonDecodeServiceOperations {
@SquadronMethod()
Future<dynamic> jsonDecode(String source) async => json.decode(source);
}
13 changes: 13 additions & 0 deletions example/lib/json_decode_service.vm.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 59 additions & 0 deletions example/lib/json_decode_service.worker.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies:
analyzer:
http:
built_collection:
squadron: ^4.3.0

dev_dependencies:
build_runner:
Expand All @@ -22,6 +23,7 @@ dev_dependencies:
built_value_generator:
dart_code_metrics: ^4.8.1
lints: ^2.0.0
squadron_builder: ^0.9.0

dependency_overrides:
chopper:
Expand Down
Loading

0 comments on commit 83c8191

Please sign in to comment.