diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8591699a..664fde42 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -29,7 +29,8 @@ jobs: - id: checkout uses: actions/checkout@v3 with: - ref: ${{ github.event.pull_request.base.ref }} + fetch-depth: 2 + - run: git checkout HEAD^ - name: Load base version id: load_base_version run: | @@ -79,4 +80,9 @@ jobs: - name: Skip publish id: skip_publish if: ${{ env.IS_VERSION_GREATER == 0 }} - run: echo "Skipping publish for ${{ matrix.package }} because the version is not greater than the one on pub.dev" \ No newline at end of file + run: echo "Skipping publish for ${{ matrix.package }} because the version is not greater than the one on pub.dev" + - name: Cleanup + id: cleanup + if: ${{ always() }} + run: | + rm -rf "$XDG_CONFIG_HOME/dart/pub-credentials.json" \ No newline at end of file diff --git a/chopper/CHANGELOG.md b/chopper/CHANGELOG.md index 8efd695f..e14d3af5 100644 --- a/chopper/CHANGELOG.md +++ b/chopper/CHANGELOG.md @@ -1,9 +1,17 @@ # Changelog +## 6.1.3 + +- Add follow redirects to toHttpRequest ([#430](https://github.com/lejard-h/chopper/pull/430)) +- Update http constraint to ">=0.13.0 <2.0.0" ([#431](https://github.com/lejard-h/chopper/pull/431)) +- Add MultipartRequest log to CurlInterceptor ([#435](https://github.com/lejard-h/chopper/pull/435)) + ## 6.1.2 + - Packages upgrade, constraints upgrade ## 6.1.1 + - EquatableMixin for Request, Response and PartValue ## 6.1.0 @@ -37,6 +45,7 @@ ## 4.0.1 - Fix for the null safety support + ## 4.0.0 - **Null safety support** @@ -73,15 +82,15 @@ **Breaking change** New way to handle errors - if (response.isSuccessful) { - final body = response.body; - } else { - final error = response.error; - } +if (response.isSuccessful) { +final body = response.body; +} else { +final error = response.error; +} + - Fix error handling by introducing `Response.error` getter - Remove `onError` since every response are available via `onResponse` stream - ## 2.5.0 - Unsuccessful response are not throw anymore, use `Response.isSuccessful` getter or `statusCode` instead @@ -90,8 +99,8 @@ New way to handle errors ## 2.4.2 - Fix on JsonConverter - If content type header overrided using @Post(headers: {'content-type': '...'}) - The converter won't add json header and won't apply json.encode if content type is not JSON + If content type header overrided using @Post(headers: {'content-type': '...'}) + The converter won't add json header and won't apply json.encode if content type is not JSON - add `bool override` on `applyHeader(s)` functions, true by default @@ -107,8 +116,9 @@ New way to handle errors `Response.base` is now a `BaseRequest` instead of a `Request`, which means that you can't do base.body now. Please use Response.bodyBytes or Response.bodyString instead for non streaming case. - Now supports streams ! - - You can pass `Stream>` as a body to a request - - You can also use `Stream>` as the BodyType for the response, in this case the returned response will contain a stream in `body`. + - You can pass `Stream>` as a body to a request + - You can also use `Stream>` as the BodyType for the response, in this case the returned response will + contain a stream in `body`. - Support passing `MutlipartFile` (from packages:http) directly to `@FileField` annotation ## 2.3.2 @@ -138,12 +148,12 @@ New way to handle errors ## 2.2.0 - Fix converter issue on List - - ***Breaking Change*** - on `Converter.convertResponse(response)`, - it take a new generic type => `Converter.convertResponse(response)` + - ***Breaking Change*** + on `Converter.convertResponse(response)`, + it take a new generic type => `Converter.convertResponse(response)` - deprecated `Chopper.service(Type)`, use `Chopper.getservice()` instead -thanks to @MichaelDark + thanks to @MichaelDark ## 2.1.0 @@ -159,30 +169,31 @@ thanks to @MichaelDark - Request is now containing baseUrl - Can call `Request.toHttpRequest()` direclty to get the `http.BaseRequest` will receive -- If a full url is specified in the `path` (ex: @Get(path: 'https://...')), it won't be concaten with the baseUrl of the ChopperClient and the ChopperAPI +- If a full url is specified in the `path` (ex: @Get(path: 'https://...')), it won't be concaten with the baseUrl of the + ChopperClient and the ChopperAPI - Add `CurlInterceptor` thanks @edwardaux - Add `HttpLoggingInterceptor` - Add `FactoryConverter` annotation `@FactoryConverter(request: convertRequest, response: convertResponse)` - ***BreakingChange*** - - Method.url renamed to path - - `Converter.encode` and `Converter.decode` removed, implement `Converter.convertResponse` and Converter.convertRequest` instead - - `ChopperClient.jsonApi` deprecated, use a `JsonConverter` instead - - `ChopperClient.formUrlEncodedApi`, use `FormUrlEncodedConverter` instead - - remove `JsonEncoded` annotation, use `FactoryConverter` instead + - Method.url renamed to path + - `Converter.encode` and `Converter.decode` removed, implement `Converter.convertResponse` and + Converter.convertRequest` instead + - `ChopperClient.jsonApi` deprecated, use a `JsonConverter` instead + - `ChopperClient.formUrlEncodedApi`, use `FormUrlEncodedConverter` instead + - remove `JsonEncoded` annotation, use `FactoryConverter` instead ## 1.1.0 - ***BreakingChange*** - Removed `name` parameter on `ChopperApi` - New way to instanciate a service + Removed `name` parameter on `ChopperApi` + New way to instanciate a service @ChopperApi() abstract class MyService extends ChopperService { static MyService create([ChopperClient client]) => _$MyService(client); } - ## 1.0.0 - Multipart request diff --git a/chopper/lib/src/interceptor.dart b/chopper/lib/src/interceptor.dart index 0e123237..b8595cea 100644 --- a/chopper/lib/src/interceptor.dart +++ b/chopper/lib/src/interceptor.dart @@ -151,6 +151,16 @@ class CurlInterceptor implements RequestInterceptor { curl += ' -d \'$body\''; } } + if (baseRequest is http.MultipartRequest) { + final fields = baseRequest.fields; + final files = baseRequest.files; + fields.forEach((k, v) { + curl += ' -f \'$k: $v\''; + }); + for (var file in files) { + curl += ' -f \'${file.field}: ${file.filename ?? ''}\''; + } + } curl += ' "$url"'; chopperLogger.info(curl); diff --git a/chopper/lib/src/request.dart b/chopper/lib/src/request.dart index e4ecfdb6..7e0b21f2 100644 --- a/chopper/lib/src/request.dart +++ b/chopper/lib/src/request.dart @@ -138,6 +138,7 @@ class Request extends http.BaseRequest with EquatableMixin { @visibleForTesting http.Request toHttpRequest() { final http.Request request = http.Request(method, url) + ..followRedirects = followRedirects ..headers.addAll(headers); if (body != null) { diff --git a/chopper/pubspec.yaml b/chopper/pubspec.yaml index f6cfea6a..322108e1 100644 --- a/chopper/pubspec.yaml +++ b/chopper/pubspec.yaml @@ -1,6 +1,6 @@ name: chopper description: Chopper is an http client generator using source_gen, inspired by Retrofit -version: 6.1.2 +version: 6.1.3 documentation: https://hadrien-lejard.gitbook.io/chopper repository: https://github.com/lejard-h/chopper @@ -9,7 +9,7 @@ environment: dependencies: equatable: ^2.0.5 - http: ">=0.13.0 <1.0.0" + http: ">=0.13.0 <2.0.0" logging: ^1.0.0 meta: ^1.3.0 diff --git a/chopper/test/interceptors_test.dart b/chopper/test/interceptors_test.dart index 0584dcc5..3ee651d0 100644 --- a/chopper/test/interceptors_test.dart +++ b/chopper/test/interceptors_test.dart @@ -204,6 +204,35 @@ void main() { ), ); }); + + final fakeRequestMultipart = Request( + 'POST', + Uri.parse('/'), + Uri.parse('base'), + headers: {'foo': 'bar'}, + parts: [ + PartValue('p1', 123), + PartValueFile( + 'p2', + http.MultipartFile.fromBytes('file', [0], filename: 'filename'), + ), + ], + multipart: true, + ); + + test('Curl interceptors Multipart', () async { + final curl = CurlInterceptor(); + var log = ''; + chopperLogger.onRecord.listen((r) => log = r.message); + await curl.onRequest(fakeRequestMultipart); + + expect( + log, + equals( + "curl -v -X POST -H 'foo: bar' -f 'p1: 123' -f 'file: filename' \"base/\"", + ), + ); + }); }); } diff --git a/chopper_built_value/CHANGELOG.md b/chopper_built_value/CHANGELOG.md index c78c9e07..5fc1f1f7 100644 --- a/chopper_built_value/CHANGELOG.md +++ b/chopper_built_value/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.2.2 + +- Update http constraint to ">=0.13.0 <2.0.0" ([#431](https://github.com/lejard-h/chopper/pull/431)) + ## 1.2.1 - Packages upgrade, constraints upgrade diff --git a/chopper_built_value/pubspec.yaml b/chopper_built_value/pubspec.yaml index 1f536048..1eed9725 100644 --- a/chopper_built_value/pubspec.yaml +++ b/chopper_built_value/pubspec.yaml @@ -1,6 +1,6 @@ name: chopper_built_value description: A built_value based Converter for Chopper. -version: 1.2.1 +version: 1.2.2 documentation: https://hadrien-lejard.gitbook.io/chopper/converters/built-value-converter repository: https://github.com/lejard-h/chopper @@ -11,7 +11,7 @@ dependencies: built_value: ^8.0.0 built_collection: ^5.0.0 chopper: ^6.0.0 - http: ^0.13.0 + http: ">=0.13.0 <2.0.0" dev_dependencies: test: ^1.16.4 diff --git a/chopper_generator/CHANGELOG.md b/chopper_generator/CHANGELOG.md index 4fa0cf97..2c58007d 100644 --- a/chopper_generator/CHANGELOG.md +++ b/chopper_generator/CHANGELOG.md @@ -36,13 +36,13 @@ ## 4.0.1 - Fix for the null safety support + ## 4.0.0 - **Null safety support** - Fix `@Header` annotation not generating null safe code - Respect `required` keyword in functions - ## 3.0.5 - Packages upgrade @@ -66,7 +66,8 @@ ## 3.0.0 -- Maintenance release to support last version of `chopper` package (3.0.0) that introduced a breaking change on error handling +- Maintenance release to support last version of `chopper` package (3.0.0) that introduced a breaking change on error + handling ## 2.5.0 @@ -76,8 +77,8 @@ ## 2.4.2 - Fix on JsonConverter - If content type header overrided using @Post(headers: {'content-type': '...'}) - The converter won't add json header and won't apply json.encode if content type is not JSON + If content type header overrided using @Post(headers: {'content-type': '...'}) + The converter won't add json header and won't apply json.encode if content type is not JSON - add `bool override` on `applyHeader(s)` functions, true by default @@ -94,7 +95,7 @@ ## 2.3.4 - fix trailing slash when empty path +fix trailing slash when empty path ## 2.3.3 @@ -127,12 +128,12 @@ ## 2.2.0 - Fix converter issue on List - - ***Breaking Change*** - on `Converter.convertResponse(response)`, - it take a new generic type => `Converter.convertResponse(response)` + - ***Breaking Change*** + on `Converter.convertResponse(response)`, + it take a new generic type => `Converter.convertResponse(response)` - deprecated `Chopper.service(Type)`, use `Chopper.getservice()` instead -thanks to @MichaelDark + thanks to @MichaelDark ## 2.1.0 @@ -142,29 +143,31 @@ thanks to @MichaelDark - Request is now containing baseUrl - Can call `Request.toHttpRequest()` direclty to get the `http.BaseRequest` will receive -- If a full url is specified in the `path` (ex: @Get(path: 'https://...')), it won't be concaten with the baseUrl of the ChopperClient and the ChopperAPI +- If a full url is specified in the `path` (ex: @Get(path: 'https://...')), it won't be concaten with the baseUrl of the + ChopperClient and the ChopperAPI - Add `CurlInterceptor` thanks @edwardaux - Add `HttpLoggingInterceptor` - Add `FactoryConverter` annotation `@FactoryConverter(request: convertRequest, response: convertResponse)` - ***BreakingChange*** - - Method.url renamed to path - - `Converter.encode` and `Converter.decode` removed, implement `Converter.convertResponse` and Converter.convertRequest` instead - - `ChopperClient.jsonApi` deprecated, use a `JsonConverter` instead - - `ChopperClient.formUrlEncodedApi`, use `FormUrlEncodedConverter` instead - - remove `JsonEncoded` annotation, use `FactoryConverter` instead + - Method.url renamed to path + - `Converter.encode` and `Converter.decode` removed, implement `Converter.convertResponse` and + Converter.convertRequest` instead + - `ChopperClient.jsonApi` deprecated, use a `JsonConverter` instead + - `ChopperClient.formUrlEncodedApi`, use `FormUrlEncodedConverter` instead + - remove `JsonEncoded` annotation, use `FactoryConverter` instead ## 1.1.0 - ***BreakingChange*** - Removed `name` parameter on `ChopperApi` - New way to instanciate a service - ```dart - @ChopperApi() - abstract class MyService extends ChopperService { - static MyService create([ChopperClient client]) => _$MyService(client); - } - ``` + Removed `name` parameter on `ChopperApi` + New way to instanciate a service + ```dart + @ChopperApi() + abstract class MyService extends ChopperService { + static MyService create([ChopperClient client]) => _$MyService(client); + } + ``` ## 1.0.1 diff --git a/chopper_generator/lib/src/generator.dart b/chopper_generator/lib/src/generator.dart index cce54040..72635a75 100644 --- a/chopper_generator/lib/src/generator.dart +++ b/chopper_generator/lib/src/generator.dart @@ -457,6 +457,7 @@ class ChopperGenerator extends GeneratorForAnnotation { _typeChecker(Map).isExactlyType(type) || _typeChecker(BuiltMap).isExactlyType(type)) return type; + // ignore: deprecated_member_use if (generic.isDynamic) return null; if (_typeChecker(List).isExactlyType(type) || diff --git a/example/build.yaml b/example/build.yaml index d0fb37db..b8099626 100644 --- a/example/build.yaml +++ b/example/build.yaml @@ -13,4 +13,8 @@ targets: any_map: false checked: false explicit_to_json: true - create_to_json: true \ No newline at end of file + create_to_json: true + squadron_builder:worker_builder: + options: + with_finalizers: true + serialization_type: List \ No newline at end of file diff --git a/example/lib/json_decode_service.vm.g.dart b/example/lib/json_decode_service.vm.g.dart index e798c22e..656bcef1 100644 --- a/example/lib/json_decode_service.vm.g.dart +++ b/example/lib/json_decode_service.vm.g.dart @@ -8,6 +8,6 @@ import 'package:squadron/squadron_service.dart'; import 'json_decode_service.dart'; // VM entry point -void _start(Map command) => run($JsonDecodeServiceInitializer, command, null); +void _start(List command) => run($JsonDecodeServiceInitializer, command, null); dynamic $getJsonDecodeServiceActivator() => _start; diff --git a/example/lib/json_decode_service.worker.g.dart b/example/lib/json_decode_service.worker.g.dart index 0119d302..0cd306ce 100644 --- a/example/lib/json_decode_service.worker.g.dart +++ b/example/lib/json_decode_service.worker.g.dart @@ -23,10 +23,10 @@ JsonDecodeService $JsonDecodeServiceInitializer(WorkerRequest startRequest) => JsonDecodeService(); // Worker for JsonDecodeService -class JsonDecodeServiceWorker extends Worker +class _JsonDecodeServiceWorker extends Worker with $JsonDecodeServiceOperations implements JsonDecodeService { - JsonDecodeServiceWorker() : super($JsonDecodeServiceActivator); + _JsonDecodeServiceWorker() : super($JsonDecodeServiceActivator); @override Future jsonDecode(String source) => send( @@ -36,13 +36,109 @@ class JsonDecodeServiceWorker extends Worker @override Map get operations => WorkerService.noOperations; + + final Object _detachToken = Object(); +} + +// Finalizable worker wrapper for JsonDecodeService +class JsonDecodeServiceWorker implements _JsonDecodeServiceWorker { + JsonDecodeServiceWorker() : _worker = _JsonDecodeServiceWorker() { + _finalizer.attach(this, _worker, detach: _worker._detachToken); + } + + final _JsonDecodeServiceWorker _worker; + + static final Finalizer<_JsonDecodeServiceWorker> _finalizer = + Finalizer<_JsonDecodeServiceWorker>((w) { + try { + _finalizer.detach(w._detachToken); + w.stop(); + } catch (ex) { + // finalizers must not throw + } + }); + + @override + Future jsonDecode(String source) => _worker.jsonDecode(source); + + @override + Map get operations => _worker.operations; + + @override + List get args => _worker.args; + + @override + Channel? get channel => _worker.channel; + + @override + Duration get idleTime => _worker.idleTime; + + @override + bool get isStopped => _worker.isStopped; + + @override + int get maxWorkload => _worker.maxWorkload; + + @override + WorkerStat get stats => _worker.stats; + + @override + String get status => _worker.status; + + @override + int get totalErrors => _worker.totalErrors; + + @override + int get totalWorkload => _worker.totalWorkload; + + @override + Duration get upTime => _worker.upTime; + + @override + String get workerId => _worker.workerId; + + @override + int get workload => _worker.workload; + + @override + Future start() => _worker.start(); + + @override + void stop() => _worker.stop(); + + @override + Future send(int command, + {List args = const [], + CancellationToken? token, + bool inspectRequest = false, + bool inspectResponse = false}) => + _worker.send(command, + args: args, + token: token, + inspectRequest: inspectRequest, + inspectResponse: inspectResponse); + + @override + Stream stream(int command, + {List args = const [], + CancellationToken? token, + bool inspectRequest = false, + bool inspectResponse = false}) => + _worker.stream(command, + args: args, + token: token, + inspectRequest: inspectRequest, + inspectResponse: inspectResponse); + + @override + Object get _detachToken => _worker._detachToken; } // Worker pool for JsonDecodeService -class JsonDecodeServiceWorkerPool extends WorkerPool +class _JsonDecodeServiceWorkerPool extends WorkerPool with $JsonDecodeServiceOperations implements JsonDecodeService { - JsonDecodeServiceWorkerPool({ConcurrencySettings? concurrencySettings}) + _JsonDecodeServiceWorkerPool({ConcurrencySettings? concurrencySettings}) : super(() => JsonDecodeServiceWorker(), concurrencySettings: concurrencySettings); @@ -52,4 +148,126 @@ class JsonDecodeServiceWorkerPool extends WorkerPool @override Map get operations => WorkerService.noOperations; + + final Object _detachToken = Object(); +} + +// Finalizable worker pool wrapper for JsonDecodeService +class JsonDecodeServiceWorkerPool implements _JsonDecodeServiceWorkerPool { + JsonDecodeServiceWorkerPool({ConcurrencySettings? concurrencySettings}) + : _pool = _JsonDecodeServiceWorkerPool( + concurrencySettings: concurrencySettings) { + _finalizer.attach(this, _pool, detach: _pool._detachToken); + } + + final _JsonDecodeServiceWorkerPool _pool; + + static final Finalizer<_JsonDecodeServiceWorkerPool> _finalizer = + Finalizer<_JsonDecodeServiceWorkerPool>((p) { + try { + _finalizer.detach(p._detachToken); + p.stop(); + } catch (ex) { + // finalizers must not throw + } + }); + + @override + Future jsonDecode(String source) => _pool.jsonDecode(source); + + @override + Map get operations => _pool.operations; + + @override + ConcurrencySettings get concurrencySettings => _pool.concurrencySettings; + + @override + Iterable get fullStats => _pool.fullStats; + + @override + int get maxConcurrency => _pool.maxConcurrency; + + @override + int get maxParallel => _pool.maxParallel; + + @override + int get maxSize => _pool.maxSize; + + @override + int get maxWorkers => _pool.maxWorkers; + + @override + int get maxWorkload => _pool.maxWorkload; + + @override + int get minWorkers => _pool.minWorkers; + + @override + int get pendingWorkload => _pool.pendingWorkload; + + @override + int get size => _pool.size; + + @override + Iterable get stats => _pool.stats; + + @override + bool get stopped => _pool.stopped; + + @override + int get totalErrors => _pool.totalErrors; + + @override + int get totalWorkload => _pool.totalWorkload; + + @override + int get workload => _pool.workload; + + @override + void cancel([Task? task, String? message]) => _pool.cancel(task, message); + + @override + FutureOr start() => _pool.start(); + + @override + int stop([bool Function(JsonDecodeServiceWorker worker)? predicate]) => + _pool.stop(predicate); + + @override + Object registerWorkerPoolListener( + void Function(JsonDecodeServiceWorker worker, bool removed) + listener) => + _pool.registerWorkerPoolListener(listener); + + @override + void unregisterWorkerPoolListener( + {void Function(JsonDecodeServiceWorker worker, bool removed)? + listener, + Object? token}) => + _pool.unregisterWorkerPoolListener(listener: listener, token: token); + + @override + Future execute(Future Function(JsonDecodeServiceWorker worker) task, + {PerfCounter? counter}) => + _pool.execute(task, counter: counter); + + @override + StreamTask scheduleStream( + Stream Function(JsonDecodeServiceWorker worker) task, + {PerfCounter? counter}) => + _pool.scheduleStream(task, counter: counter); + + @override + ValueTask scheduleTask( + Future Function(JsonDecodeServiceWorker worker) task, + {PerfCounter? counter}) => + _pool.scheduleTask(task, counter: counter); + + @override + Stream stream(Stream Function(JsonDecodeServiceWorker worker) task, + {PerfCounter? counter}) => + _pool.stream(task, counter: counter); + + @override + Object get _detachToken => _pool._detachToken; } diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 963f23c9..d88b0adf 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,6 @@ name: chopper_example description: Example usage of the Chopper package -version: 0.0.4 +version: 0.0.5 documentation: https://hadrien-lejard.gitbook.io/chopper/ #author: Hadrien Lejard @@ -14,7 +14,7 @@ dependencies: analyzer: http: built_collection: - squadron: ^4.3.8 + squadron: ^5.0.0 dev_dependencies: build_runner: @@ -23,7 +23,7 @@ dev_dependencies: built_value_generator: dart_code_metrics: '>=4.8.1 <6.0.0' lints: ^2.0.0 - squadron_builder: ^2.0.0 + squadron_builder: ^2.1.2 dependency_overrides: chopper: