From f1409e806718189d8740c8ad41f24ce20099636d Mon Sep 17 00:00:00 2001 From: Nwachi ifeanyichukwu Victor <56759256+farmery@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:16:27 +0100 Subject: [PATCH] Feat: Enable passing in Extras using Custom Annotations (#706) * Implemented TypedExtras * changed TypedExtra to TypedExtras * remove publish_to: none * update TypedExtra to TypedExtras * reaname _getExtrasFromClass to _getMapFromTypedExtras * allow nullable properties in TypedExtras * update docs * Update docs * remove pubspec changes * update dependencies * update pubspec * fix retrofit deps * Update changelogs and version --------- Co-authored-by: Trevor Wang --- generator/CHANGELOG.md | 16 +++++ generator/lib/src/generator.dart | 44 +++++++++++++- generator/pubspec.yaml | 11 +++- generator/test/src/generator_test_src.dart | 70 +++++++++++++++++++++- retrofit/CHANGELOG.md | 16 +++++ retrofit/lib/dio.dart | 20 +++++++ retrofit/pubspec.yaml | 2 +- 7 files changed, 171 insertions(+), 8 deletions(-) diff --git a/generator/CHANGELOG.md b/generator/CHANGELOG.md index fbf32741..e150e78b 100644 --- a/generator/CHANGELOG.md +++ b/generator/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 9.1.0 + +- Added `@TypedExtras` to pass extra options to dio requests using custom annotations. + + Example : + + ```dart + @TypedExtrasSubClass( + id: 'abcd', + count: 5, + shouldProceed: true, + ) + @http.POST('/path/') + Future myMethod(@Extras() Map extras); + ``` + ## 9.0.0 - Require Dart 3.3 diff --git a/generator/lib/src/generator.dart b/generator/lib/src/generator.dart index 2c8a5a4b..f05e593d 100644 --- a/generator/lib/src/generator.dart +++ b/generator/lib/src/generator.dart @@ -2271,6 +2271,46 @@ ${bodyName.displayName} == null return result; } + dynamic _getFieldValue(ConstantReader? value) { + if (value?.isBool ?? false) return value?.boolValue; + if (value?.isDouble ?? false) return value?.doubleValue; + if (value?.isInt ?? false) return value?.intValue; + if (value?.isString ?? false) return value?.stringValue; + if (value?.isList ?? false) { + return value?.listValue.map((item) => _getFieldValue(ConstantReader(item))).toList(); + } + if (value?.isMap ?? false) { + final mapValue = value?.mapValue.map((key, val) { + return MapEntry( + _getFieldValue(ConstantReader(key)), + _getFieldValue(ConstantReader(val)), + ); + }); + return mapValue; + } + if (value?.isSet ?? false) { + return value?.setValue.map((item) { + return _getFieldValue(ConstantReader(item)); + }).toSet(); + } + return null; + } + + Map _getMapFromTypedExtras(MethodElement m) { + final annotation = _getMethodAnnotations(m, retrofit.TypedExtras).firstOrNull; + final fields = annotation?.objectValue.type?.element?.children + .whereType(); + Map mapFromTypedExtras = {}; + for (var field in fields ?? []) { + final value = annotation?.peek(field.name); + final fieldValue = _getFieldValue(value); + if (fieldValue != null) { + mapFromTypedExtras[field.name] = fieldValue; + } + } + return mapFromTypedExtras; + } + void _generateExtra( MethodElement m, List blocks, @@ -2306,7 +2346,9 @@ ${bodyName.displayName} == null ), ), ) - .fold>({}, (p, e) => p..addAll(e ?? {})), + .fold>({}, (p, e) { + return p..addAll(e ?? {}); + })..addAll(_getMapFromTypedExtras(m)), refer('String'), refer('dynamic'), ), diff --git a/generator/pubspec.yaml b/generator/pubspec.yaml index 34347819..e437bcda 100644 --- a/generator/pubspec.yaml +++ b/generator/pubspec.yaml @@ -8,7 +8,7 @@ topics: - rest - retrofit - codegen -version: 9.0.0 +version: 9.1.0 environment: sdk: '>=3.3.0 <4.0.0' @@ -19,11 +19,16 @@ dependencies: code_builder: ^4.4.0 dart_style: ^2.3.0 dio: ^5.0.0 - protobuf: ^3.1.0 retrofit: ^4.3.0 - source_gen: ^1.5.0 + source_gen: ^1.3.0 + tuple: ^2.0.1 + protobuf: ^3.1.0 dev_dependencies: lints: ^4.0.0 source_gen_test: ^1.0.6 test: ^1.25.0 + +dependency_overrides: + retrofit: + path: ../retrofit diff --git a/generator/test/src/generator_test_src.dart b/generator/test/src/generator_test_src.dart index d6c6c016..e99765f9 100644 --- a/generator/test/src/generator_test_src.dart +++ b/generator/test/src/generator_test_src.dart @@ -1,11 +1,75 @@ import 'dart:io'; - import 'package:dio/dio.dart' hide Headers; -import 'package:retrofit/retrofit.dart'; import 'package:source_gen_test/annotations.dart'; - +import 'package:retrofit/retrofit.dart'; import 'query.pb.dart'; +class DummyTypedExtras extends TypedExtras { + final String id; + final Map config; + final List fileTypes; + final Set sources; + final bool shouldProceed; + final bool? canFly; + const DummyTypedExtras({ + required this.id, + required this.config, + required this.fileTypes, + required this.sources, + required this.shouldProceed, + this.canFly, + }); +} + +@ShouldGenerate( + ''' + final _extra = { + 'id': '1234', + 'config': { + 'date': '24-10-2024', + 'type': 'analytics', + 'shouldReplace': true, + 'subConfig': {'date': '24-11-2025'}, + }, + 'fileTypes': [ + 'mp4', + 'mp3', + 'mkv', + ], + 'sources': { + 'internet', + 'local', + }, + 'shouldProceed': true, + }; + ''', + contains: true, +) +@RestApi() +abstract class TypedExtrasTest { + @DummyTypedExtras( + id: '1234', + config: { + 'date': '24-10-2024', + 'type': 'analytics', + 'shouldReplace': true, + 'subConfig': {'date': '24-11-2025'}, + }, + fileTypes: [ + 'mp4', + 'mp3', + 'mkv', + ], + sources: { + 'internet', + 'local', + }, + shouldProceed: true, + ) + @GET('path') + Future list(); +} + @ShouldGenerate( ''' // ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element diff --git a/retrofit/CHANGELOG.md b/retrofit/CHANGELOG.md index a79f345f..5c70f2ef 100644 --- a/retrofit/CHANGELOG.md +++ b/retrofit/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 4.4.0 + +- Added `@TypedExtras` to pass extra options to dio requests using custom annotations. + + Example : + + ```dart + @TypedExtrasSubClass( + id: 'abcd', + count: 5, + shouldProceed: true, + ) + @http.POST('/path/') + Future myMethod(@Extras() Map extras); + ``` + ## 4.3.0 - Required Dart 2.19 diff --git a/retrofit/lib/dio.dart b/retrofit/lib/dio.dart index 35c1e2a4..f51cf88d 100644 --- a/retrofit/lib/dio.dart +++ b/retrofit/lib/dio.dart @@ -1,6 +1,26 @@ import 'package:dio/dio.dart'; import 'package:meta/meta.dart'; +/// Extra data that will be passed to Dio's request, response, transformer, and interceptors. +/// Extend [TypedExtras] and define fields that correspond to the keys passed into `extras`. +/// The values of these fields will be derived from the data passed into your subclass. +/// +/// Example: +/// +/// ```dart +/// @TypedExtrasSubClass( +/// id: '1234', +/// fileType: '.json', +/// ) +/// @GET("/get") +/// Future foo(); +/// ``` +/// +@immutable +class TypedExtras { + const TypedExtras(); +} + /// Extra data that will be passed to dio's request, response, transformer and interceptors. @immutable class Extra { diff --git a/retrofit/pubspec.yaml b/retrofit/pubspec.yaml index 9ea202d2..1b7103a1 100644 --- a/retrofit/pubspec.yaml +++ b/retrofit/pubspec.yaml @@ -8,7 +8,7 @@ topics: - rest - dio - retrofit -version: 4.3.0 +version: 4.4.0 environment: sdk: '>=2.19.0 <4.0.0'