diff --git a/chopper/analysis_options.yaml b/chopper/analysis_options.yaml index 4fd79467..6f56a451 100644 --- a/chopper/analysis_options.yaml +++ b/chopper/analysis_options.yaml @@ -3,6 +3,7 @@ include: package:lints/recommended.yaml analyzer: exclude: - "**.g.dart" + - "**.chopper.dart" - "**.mocks.dart" - "example/**" diff --git a/chopper/lib/chopper.dart b/chopper/lib/chopper.dart index bcd68145..38fd56aa 100644 --- a/chopper/lib/chopper.dart +++ b/chopper/lib/chopper.dart @@ -12,6 +12,7 @@ export 'src/constants.dart'; export 'src/extensions.dart'; export 'src/http_logging_interceptor.dart'; export 'src/interceptor.dart'; +export 'src/list_format.dart'; export 'src/request.dart'; export 'src/response.dart'; export 'src/utils.dart' hide mapToQuery; diff --git a/chopper/lib/src/annotations.dart b/chopper/lib/src/annotations.dart index 0a0b8058..899b5ecb 100644 --- a/chopper/lib/src/annotations.dart +++ b/chopper/lib/src/annotations.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:chopper/src/constants.dart'; +import 'package:chopper/src/list_format.dart'; import 'package:chopper/src/request.dart'; import 'package:chopper/src/response.dart'; import 'package:meta/meta.dart'; @@ -190,14 +191,23 @@ sealed class Method { /// Mark the body as optional to suppress warnings during code generation final bool optionalBody; - /// Use brackets [ ] to when encoding + /// List format to use when encoding lists + /// + /// - [ListFormat.repeat] `hxxp://path/to/script?foo=123&foo=456&foo=789` (default) + /// - [ListFormat.brackets] `hxxp://path/to/script?foo[]=123&foo[]=456&foo[]=789` + /// - [ListFormat.indices] `hxxp://path/to/script?foo[0]=123&foo[1]=456&foo[2]=789` + /// - [ListFormat.comma] `hxxp://path/to/script?foo=123,456,789` + final ListFormat? listFormat; + + /// Use brackets `[ ]` to when encoding /// /// - lists - /// hxxp://path/to/script?foo[]=123&foo[]=456&foo[]=789 + /// `hxxp://path/to/script?foo[]=123&foo[]=456&foo[]=789` /// /// - maps - /// hxxp://path/to/script?user[name]=john&user[surname]=doe&user[age]=21 - final bool useBrackets; + /// `hxxp://path/to/script?user[name]=john&user[surname]=doe&user[age]=21` + @Deprecated('Use listFormat instead') + final bool? useBrackets; /// Set to [true] to include query variables with null values. This includes nested maps. /// The default is to exclude them. @@ -223,7 +233,7 @@ sealed class Method { /// ``` /// /// The above code produces hxxp://path/to/script&foo=foo_var&bar=&baz=baz_var - final bool includeNullQueryVars; + final bool? includeNullQueryVars; /// {@macro Method} const Method( @@ -231,8 +241,9 @@ sealed class Method { this.optionalBody = false, this.path = '', this.headers = const {}, - this.useBrackets = false, - this.includeNullQueryVars = false, + this.listFormat, + @Deprecated('Use listFormat instead') this.useBrackets, + this.includeNullQueryVars, }); } @@ -247,6 +258,7 @@ final class Get extends Method { super.optionalBody = true, super.path, super.headers, + super.listFormat, super.useBrackets, super.includeNullQueryVars, }) : super(HttpMethod.Get); @@ -265,6 +277,7 @@ final class Post extends Method { super.optionalBody, super.path, super.headers, + super.listFormat, super.useBrackets, super.includeNullQueryVars, }) : super(HttpMethod.Post); @@ -281,6 +294,7 @@ final class Delete extends Method { super.optionalBody = true, super.path, super.headers, + super.listFormat, super.useBrackets, super.includeNullQueryVars, }) : super(HttpMethod.Delete); @@ -299,6 +313,7 @@ final class Put extends Method { super.optionalBody, super.path, super.headers, + super.listFormat, super.useBrackets, super.includeNullQueryVars, }) : super(HttpMethod.Put); @@ -316,6 +331,7 @@ final class Patch extends Method { super.optionalBody, super.path, super.headers, + super.listFormat, super.useBrackets, super.includeNullQueryVars, }) : super(HttpMethod.Patch); @@ -332,6 +348,7 @@ final class Head extends Method { super.optionalBody = true, super.path, super.headers, + super.listFormat, super.useBrackets, super.includeNullQueryVars, }) : super(HttpMethod.Head); @@ -348,6 +365,7 @@ final class Options extends Method { super.optionalBody = true, super.path, super.headers, + super.listFormat, super.useBrackets, super.includeNullQueryVars, }) : super(HttpMethod.Options); diff --git a/chopper/lib/src/list_format.dart b/chopper/lib/src/list_format.dart new file mode 100644 index 00000000..dedb3d72 --- /dev/null +++ b/chopper/lib/src/list_format.dart @@ -0,0 +1,29 @@ +import 'package:qs_dart/qs_dart.dart' as qs show ListFormat; + +/// An enum of all available list format options. +/// +/// This is a wrapper around the [qs.ListFormat] enum. +enum ListFormat { + /// Use brackets to represent list items, for example + /// `foo[]=123&foo[]=456&foo[]=789` + brackets(qs.ListFormat.brackets), + + /// Use commas to represent list items, for example + /// `foo=123,456,789` + comma(qs.ListFormat.comma), + + /// Repeat the same key to represent list items, for example + /// `foo=123&foo=456&foo=789` + repeat(qs.ListFormat.repeat), + + /// Use indices in brackets to represent list items, for example + /// `foo[0]=123&foo[1]=456&foo[2]=789` + indices(qs.ListFormat.indices); + + const ListFormat(this.qsListFormat); + + final qs.ListFormat qsListFormat; + + @override + String toString() => name; +} diff --git a/chopper/lib/src/request.dart b/chopper/lib/src/request.dart index a9e7e9b7..fbf1891b 100644 --- a/chopper/lib/src/request.dart +++ b/chopper/lib/src/request.dart @@ -1,6 +1,7 @@ -import 'dart:async'; +import 'dart:async' show Stream; import 'package:chopper/src/extensions.dart'; +import 'package:chopper/src/list_format.dart'; import 'package:chopper/src/utils.dart'; import 'package:equatable/equatable.dart' show EquatableMixin; import 'package:http/http.dart' as http; @@ -17,8 +18,10 @@ base class Request extends http.BaseRequest with EquatableMixin { final Object? tag; final bool multipart; final List parts; - final bool useBrackets; - final bool includeNullQueryVars; + final ListFormat? listFormat; + @Deprecated('Use listFormat instead') + final bool? useBrackets; + final bool? includeNullQueryVars; /// {@macro request} Request( @@ -31,8 +34,9 @@ base class Request extends http.BaseRequest with EquatableMixin { this.multipart = false, this.parts = const [], this.tag, - this.useBrackets = false, - this.includeNullQueryVars = false, + this.listFormat, + @Deprecated('Use listFormat instead') this.useBrackets, + this.includeNullQueryVars, }) : assert( !baseUri.hasQuery, 'baseUri should not contain query parameters.' @@ -45,6 +49,8 @@ base class Request extends http.BaseRequest with EquatableMixin { baseUri, uri, {...uri.queryParametersAll, ...?parameters}, + listFormat: listFormat, + // ignore: deprecated_member_use_from_same_package useBrackets: useBrackets, includeNullQueryVars: includeNullQueryVars, ), @@ -62,7 +68,8 @@ base class Request extends http.BaseRequest with EquatableMixin { Map? headers, bool? multipart, List? parts, - bool? useBrackets, + ListFormat? listFormat, + @Deprecated('Use listFormat instead') bool? useBrackets, bool? includeNullQueryVars, Object? tag, }) => @@ -75,6 +82,8 @@ base class Request extends http.BaseRequest with EquatableMixin { headers: headers ?? this.headers, multipart: multipart ?? this.multipart, parts: parts ?? this.parts, + listFormat: listFormat ?? this.listFormat, + // ignore: deprecated_member_use_from_same_package useBrackets: useBrackets ?? this.useBrackets, includeNullQueryVars: includeNullQueryVars ?? this.includeNullQueryVars, tag: tag ?? this.tag, @@ -88,8 +97,9 @@ base class Request extends http.BaseRequest with EquatableMixin { Uri baseUrl, Uri url, Map parameters, { - bool useBrackets = false, - bool includeNullQueryVars = false, + ListFormat? listFormat, + @Deprecated('Use listFormat instead') bool? useBrackets, + bool? includeNullQueryVars, }) { // If the request's url is already a fully qualified URL, we can use it // as-is and ignore the baseUrl. @@ -106,6 +116,8 @@ base class Request extends http.BaseRequest with EquatableMixin { final String query = mapToQuery( allParameters, + listFormat: listFormat, + // ignore: deprecated_member_use_from_same_package useBrackets: useBrackets, includeNullQueryVars: includeNullQueryVars, ); @@ -239,6 +251,8 @@ base class Request extends http.BaseRequest with EquatableMixin { headers, multipart, parts, + listFormat, + // ignore: deprecated_member_use_from_same_package useBrackets, includeNullQueryVars, ]; diff --git a/chopper/lib/src/utils.dart b/chopper/lib/src/utils.dart index 63eeba6e..4906df9b 100644 --- a/chopper/lib/src/utils.dart +++ b/chopper/lib/src/utils.dart @@ -1,8 +1,8 @@ import 'dart:collection'; import 'package:chopper/chopper.dart'; -import 'package:equatable/equatable.dart' show EquatableMixin; import 'package:logging/logging.dart'; +import 'package:qs_dart/qs_dart.dart' as qs; /// Creates a new [Request] by copying [request] and adding a header with the /// provided key [name] and value [value] to the result. @@ -63,99 +63,24 @@ final chopperLogger = Logger('Chopper'); /// E.g., `{'foo': 'bar', 'ints': [ 1337, 42 ] }` will become 'foo=bar&ints=1337&ints=42'. String mapToQuery( Map map, { - bool useBrackets = false, - bool includeNullQueryVars = false, -}) => - _mapToQuery( - map, - useBrackets: useBrackets, - includeNullQueryVars: includeNullQueryVars, - ).join('&'); - -Iterable<_Pair> _mapToQuery( - Map map, { - String? prefix, - bool useBrackets = false, - bool includeNullQueryVars = false, + ListFormat? listFormat, + @Deprecated('Use listFormat instead') bool? useBrackets, + bool? includeNullQueryVars, }) { - final Set<_Pair> pairs = {}; - - map.forEach((key, value) { - String name = Uri.encodeQueryComponent(key); - - if (prefix != null) { - name = useBrackets - ? '$prefix${Uri.encodeQueryComponent('[')}$name${Uri.encodeQueryComponent(']')}' - : '$prefix.$name'; - } - - if (value != null) { - if (value is Iterable) { - pairs.addAll(_iterableToQuery(name, value, useBrackets: useBrackets)); - } else if (value is Map) { - pairs.addAll( - _mapToQuery( - value, - prefix: name, - useBrackets: useBrackets, - includeNullQueryVars: includeNullQueryVars, - ), - ); - } else { - pairs.add( - _Pair(name, _normalizeValue(value)), - ); - } - } else { - if (includeNullQueryVars) { - pairs.add(_Pair(name, '')); - } - } - }); - - return pairs; -} - -Iterable<_Pair> _iterableToQuery( - String name, - Iterable values, { - bool useBrackets = false, -}) => - values.where((value) => value?.toString().isNotEmpty ?? false).map( - (value) => _Pair( - name, - _normalizeValue(value), - useBrackets: useBrackets, - ), - ); - -String _normalizeValue(value) => Uri.encodeComponent( - value is DateTime - ? value.toUtc().toIso8601String() - : value?.toString() ?? '', - ); - -final class _Pair with EquatableMixin { - final A first; - final B second; - final bool useBrackets; - - const _Pair( - this.first, - this.second, { - this.useBrackets = false, - }); - - @override - String toString() => useBrackets - ? '$first${Uri.encodeQueryComponent('[]')}=$second' - : '$first=$second'; - - @override - List get props => [ - first, - second, - ]; + listFormat ??= useBrackets == true ? ListFormat.brackets : ListFormat.repeat; + + return qs.encode( + map, + qs.EncodeOptions( + listFormat: listFormat.qsListFormat, + allowDots: listFormat == ListFormat.repeat, + encodeDotInKeys: listFormat == ListFormat.repeat, + encodeValuesOnly: listFormat == ListFormat.repeat, + skipNulls: includeNullQueryVars != true, + strictNullHandling: false, + serializeDate: (DateTime date) => date.toUtc().toIso8601String(), + ), + ); } bool isTypeOf() => _Instance() is _Instance; diff --git a/chopper/pubspec.yaml b/chopper/pubspec.yaml index dc5a2d0b..dd8f83d2 100644 --- a/chopper/pubspec.yaml +++ b/chopper/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: http: ^1.1.0 logging: ^1.2.0 meta: ^1.9.1 + qs_dart: ^1.0.3 dev_dependencies: build_runner: ^2.4.6 diff --git a/chopper/test/base_test.dart b/chopper/test/base_test.dart index 8de582a8..d3e0fee2 100644 --- a/chopper/test/base_test.dart +++ b/chopper/test/base_test.dart @@ -154,7 +154,7 @@ void main() { final httpClient = MockClient((request) async { expect( request.url.toString(), - equals('$baseUrl/test/query?name='), + equals('$baseUrl/test/query'), ); expect(request.method, equals('GET')); @@ -176,7 +176,7 @@ void main() { final httpClient = MockClient((request) async { expect( request.url.toString(), - equals('$baseUrl/test/query?name=&default_value=42'), + equals('$baseUrl/test/query?default_value=42'), ); expect(request.method, equals('GET')); @@ -1328,6 +1328,122 @@ void main() { httpClient.close(); }); + test('List query param with brackets (legacy)', () async { + final httpClient = MockClient((request) async { + expect( + request.url.toString(), + equals('$baseUrl/test/list_query_param_with_brackets_legacy' + '?value%5B%5D=foo' + '&value%5B%5D=bar' + '&value%5B%5D=baz'), + ); + expect(request.method, equals('GET')); + + return http.Response('get response', 200); + }); + + final chopper = buildClient(httpClient); + final service = chopper.getService(); + + final response = await service.getUsingListQueryParamWithBracketsLegacy([ + 'foo', + 'bar', + 'baz', + ]); + + expect(response.body, equals('get response')); + expect(response.statusCode, equals(200)); + + httpClient.close(); + }); + + test('List query param with indices', () async { + final httpClient = MockClient((request) async { + expect( + request.url.toString(), + equals('$baseUrl/test/list_query_param_with_indices' + '?value%5B0%5D=foo' + '&value%5B1%5D=bar' + '&value%5B2%5D=baz'), + ); + expect(request.method, equals('GET')); + + return http.Response('get response', 200); + }); + + final chopper = buildClient(httpClient); + final service = chopper.getService(); + + final response = await service.getUsingListQueryParamWithIndices([ + 'foo', + 'bar', + 'baz', + ]); + + expect(response.body, equals('get response')); + expect(response.statusCode, equals(200)); + + httpClient.close(); + }); + + test('List query param with repeat', () async { + final httpClient = MockClient((request) async { + expect( + request.url.toString(), + equals('$baseUrl/test/list_query_param_with_repeat' + '?value=foo' + '&value=bar' + '&value=baz'), + ); + expect(request.method, equals('GET')); + + return http.Response('get response', 200); + }); + + final chopper = buildClient(httpClient); + final service = chopper.getService(); + + final response = await service.getUsingListQueryParamWithRepeat([ + 'foo', + 'bar', + 'baz', + ]); + + expect(response.body, equals('get response')); + expect(response.statusCode, equals(200)); + + httpClient.close(); + }); + + test('List query param with comma', () async { + final httpClient = MockClient((request) async { + expect( + request.url.toString(), + equals('$baseUrl/test/list_query_param_with_comma' + '?value=foo' + '%2Cbar' + '%2Cbaz'), + ); + expect(request.method, equals('GET')); + + return http.Response('get response', 200); + }); + + final chopper = buildClient(httpClient); + final service = chopper.getService(); + + final response = await service.getUsingListQueryParamWithComma([ + 'foo', + 'bar', + 'baz', + ]); + + expect(response.body, equals('get response')); + expect(response.statusCode, equals(200)); + + httpClient.close(); + }); + test('Map query param using default dot QueryMapSeparator', () async { final DateTime now = DateTime.now(); @@ -1423,6 +1539,196 @@ void main() { httpClient.close(); }); + test('Map query param with brackets (legacy) QueryMapSeparator', () async { + final DateTime now = DateTime.now(); + + final httpClient = MockClient((request) async { + expect( + request.url.toString(), + equals('$baseUrl/test/map_query_param_with_brackets_legacy' + '?value%5Bbar%5D=baz' + '&value%5Bzap%5D=abc' + '&value%5Betc%5D%5Babc%5D=def' + '&value%5Betc%5D%5Bghi%5D=jkl' + '&value%5Betc%5D%5Bmno%5D%5Bopq%5D=rst' + '&value%5Betc%5D%5Bmno%5D%5Buvw%5D=xyz' + '&value%5Betc%5D%5Bmno%5D%5Blist%5D%5B%5D=a' + '&value%5Betc%5D%5Bmno%5D%5Blist%5D%5B%5D=123' + '&value%5Betc%5D%5Bmno%5D%5Blist%5D%5B%5D=false' + '&value%5Betc%5D%5Bdt%5D=${Uri.encodeComponent(now.toUtc().toIso8601String())}'), + ); + expect(request.method, equals('GET')); + + return http.Response('get response', 200); + }); + + final chopper = buildClient(httpClient); + final service = chopper.getService(); + + final response = + await service.getUsingMapQueryParamWithBracketsLegacy({ + 'bar': 'baz', + 'zap': 'abc', + 'etc': { + 'abc': 'def', + 'ghi': 'jkl', + 'mno': { + 'opq': 'rst', + 'uvw': 'xyz', + 'list': ['a', 123, false], + }, + 'dt': now, + }, + }); + + expect(response.body, equals('get response')); + expect(response.statusCode, equals(200)); + + httpClient.close(); + }); + + test('Map query param with indices QueryMapSeparator', () async { + final DateTime now = DateTime.now(); + + final httpClient = MockClient((request) async { + expect( + request.url.toString(), + equals('$baseUrl/test/map_query_param_with_indices' + '?value%5Bbar%5D=baz' + '&value%5Bzap%5D=abc' + '&value%5Betc%5D%5Babc%5D=def' + '&value%5Betc%5D%5Bghi%5D=jkl' + '&value%5Betc%5D%5Bmno%5D%5Bopq%5D=rst' + '&value%5Betc%5D%5Bmno%5D%5Buvw%5D=xyz' + '&value%5Betc%5D%5Bmno%5D%5Blist%5D%5B0%5D=a' + '&value%5Betc%5D%5Bmno%5D%5Blist%5D%5B1%5D=123' + '&value%5Betc%5D%5Bmno%5D%5Blist%5D%5B2%5D=false' + '&value%5Betc%5D%5Bdt%5D=${Uri.encodeComponent(now.toUtc().toIso8601String())}'), + ); + expect(request.method, equals('GET')); + + return http.Response('get response', 200); + }); + + final chopper = buildClient(httpClient); + final service = chopper.getService(); + + final response = + await service.getUsingMapQueryParamWithIndices({ + 'bar': 'baz', + 'zap': 'abc', + 'etc': { + 'abc': 'def', + 'ghi': 'jkl', + 'mno': { + 'opq': 'rst', + 'uvw': 'xyz', + 'list': ['a', 123, false], + }, + 'dt': now, + }, + }); + + expect(response.body, equals('get response')); + expect(response.statusCode, equals(200)); + + httpClient.close(); + }); + + test('Map query param with repeat QueryMapSeparator', () async { + final DateTime now = DateTime.now(); + + final httpClient = MockClient((request) async { + expect( + request.url.toString(), + equals('$baseUrl/test/map_query_param_with_repeat' + '?value.bar=baz' + '&value.zap=abc' + '&value.etc.abc=def' + '&value.etc.ghi=jkl' + '&value.etc.mno.opq=rst' + '&value.etc.mno.uvw=xyz' + '&value.etc.mno.list=a' + '&value.etc.mno.list=123' + '&value.etc.mno.list=false' + '&value.etc.dt=${Uri.encodeComponent(now.toUtc().toIso8601String())}'), + ); + expect(request.method, equals('GET')); + + return http.Response('get response', 200); + }); + + final chopper = buildClient(httpClient); + final service = chopper.getService(); + + final response = + await service.getUsingMapQueryParamWithRepeat({ + 'bar': 'baz', + 'zap': 'abc', + 'etc': { + 'abc': 'def', + 'ghi': 'jkl', + 'mno': { + 'opq': 'rst', + 'uvw': 'xyz', + 'list': ['a', 123, false], + }, + 'dt': now, + }, + }); + + expect(response.body, equals('get response')); + expect(response.statusCode, equals(200)); + + httpClient.close(); + }); + + test('Map query param with comma QueryMapSeparator', () async { + final DateTime now = DateTime.now(); + + final httpClient = MockClient((request) async { + expect( + request.url.toString(), + equals('$baseUrl/test/map_query_param_with_comma' + '?value%5Bbar%5D=baz' + '&value%5Bzap%5D=abc' + '&value%5Betc%5D%5Babc%5D=def' + '&value%5Betc%5D%5Bghi%5D=jkl' + '&value%5Betc%5D%5Bmno%5D%5Bopq%5D=rst' + '&value%5Betc%5D%5Bmno%5D%5Buvw%5D=xyz' + '&value%5Betc%5D%5Bmno%5D%5Blist%5D=a%2C123%2Cfalse' + '&value%5Betc%5D%5Bdt%5D=${Uri.encodeComponent(now.toUtc().toIso8601String())}'), + ); + expect(request.method, equals('GET')); + + return http.Response('get response', 200); + }); + + final chopper = buildClient(httpClient); + final service = chopper.getService(); + + final response = + await service.getUsingMapQueryParamWithComma({ + 'bar': 'baz', + 'zap': 'abc', + 'etc': { + 'abc': 'def', + 'ghi': 'jkl', + 'mno': { + 'opq': 'rst', + 'uvw': 'xyz', + 'list': ['a', 123, false], + }, + 'dt': now, + }, + }); + + expect(response.body, equals('get response')); + expect(response.statusCode, equals(200)); + + httpClient.close(); + }); + test('Map query param without including null query vars', () async { final DateTime now = DateTime.now(); diff --git a/chopper/test/test_service.chopper.dart b/chopper/test/test_service.chopper.dart index c6335821..28dbbb81 100644 --- a/chopper/test/test_service.chopper.dart +++ b/chopper/test/test_service.chopper.dart @@ -580,6 +580,21 @@ final class _$HttpTestService extends HttpTestService { return client.send($request); } + @override + Future> getUsingListQueryParamWithBracketsLegacy( + List value) { + final Uri $url = Uri.parse('/test/list_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + return client.send($request); + } + @override Future> getUsingListQueryParamWithBrackets( List value) { @@ -590,7 +605,51 @@ final class _$HttpTestService extends HttpTestService { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithIndices( + List value) { + final Uri $url = Uri.parse('/test/list_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithRepeat( + List value) { + final Uri $url = Uri.parse('/test/list_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithComma(List value) { + final Uri $url = Uri.parse('/test/list_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); return client.send($request); } @@ -623,6 +682,21 @@ final class _$HttpTestService extends HttpTestService { return client.send($request); } + @override + Future> getUsingMapQueryParamWithBracketsLegacy( + Map value) { + final Uri $url = Uri.parse('/test/map_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + return client.send($request); + } + @override Future> getUsingMapQueryParamWithBrackets( Map value) { @@ -633,7 +707,52 @@ final class _$HttpTestService extends HttpTestService { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithIndices( + Map value) { + final Uri $url = Uri.parse('/test/map_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithRepeat( + Map value) { + final Uri $url = Uri.parse('/test/map_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithComma( + Map value) { + final Uri $url = Uri.parse('/test/map_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); return client.send($request); } diff --git a/chopper/test/test_service.dart b/chopper/test/test_service.dart index 6ae6cb4e..cca87ca6 100644 --- a/chopper/test/test_service.dart +++ b/chopper/test/test_service.dart @@ -168,11 +168,31 @@ abstract class HttpTestService extends ChopperService { @Query('value') List value, ); - @Get(path: '/list_query_param_with_brackets', useBrackets: true) + @Get(path: '/list_query_param_with_brackets_legacy', useBrackets: true) + Future> getUsingListQueryParamWithBracketsLegacy( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_brackets', listFormat: ListFormat.brackets) Future> getUsingListQueryParamWithBrackets( @Query('value') List value, ); + @Get(path: '/list_query_param_with_indices', listFormat: ListFormat.indices) + Future> getUsingListQueryParamWithIndices( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_repeat', listFormat: ListFormat.repeat) + Future> getUsingListQueryParamWithRepeat( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_comma', listFormat: ListFormat.comma) + Future> getUsingListQueryParamWithComma( + @Query('value') List value, + ); + @Get(path: '/map_query_param') Future> getUsingMapQueryParam( @Query('value') Map value, @@ -186,11 +206,31 @@ abstract class HttpTestService extends ChopperService { @Query('value') Map value, ); - @Get(path: '/map_query_param_with_brackets', useBrackets: true) + @Get(path: '/map_query_param_with_brackets_legacy', useBrackets: true) + Future> getUsingMapQueryParamWithBracketsLegacy( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_brackets', listFormat: ListFormat.brackets) Future> getUsingMapQueryParamWithBrackets( @Query('value') Map value, ); + @Get(path: '/map_query_param_with_indices', listFormat: ListFormat.indices) + Future> getUsingMapQueryParamWithIndices( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_repeat', listFormat: ListFormat.repeat) + Future> getUsingMapQueryParamWithRepeat( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_comma', listFormat: ListFormat.comma) + Future> getUsingMapQueryParamWithComma( + @Query('value') Map value, + ); + @Get(path: '/date_time') Future> getDateTime( @Query('value') DateTime value, diff --git a/chopper/test/test_service_base_url.chopper.dart b/chopper/test/test_service_base_url.chopper.dart index 6b859b03..262d72c7 100644 --- a/chopper/test/test_service_base_url.chopper.dart +++ b/chopper/test/test_service_base_url.chopper.dart @@ -86,6 +86,22 @@ final class _$HttpTestServiceBaseUrl extends HttpTestServiceBaseUrl { return client.send($request); } + @override + Future> getUsingListQueryParamWithBracketsLegacy( + List value) { + final Uri $url = Uri.parse( + 'https://localhost:4000/test/list_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + return client.send($request); + } + @override Future> getUsingListQueryParamWithBrackets( List value) { @@ -97,7 +113,54 @@ final class _$HttpTestServiceBaseUrl extends HttpTestServiceBaseUrl { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithIndices( + List value) { + final Uri $url = + Uri.parse('https://localhost:4000/test/list_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithRepeat( + List value) { + final Uri $url = + Uri.parse('https://localhost:4000/test/list_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithComma(List value) { + final Uri $url = + Uri.parse('https://localhost:4000/test/list_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); return client.send($request); } @@ -131,6 +194,22 @@ final class _$HttpTestServiceBaseUrl extends HttpTestServiceBaseUrl { return client.send($request); } + @override + Future> getUsingMapQueryParamWithBracketsLegacy( + Map value) { + final Uri $url = Uri.parse( + 'https://localhost:4000/test/map_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + return client.send($request); + } + @override Future> getUsingMapQueryParamWithBrackets( Map value) { @@ -142,7 +221,55 @@ final class _$HttpTestServiceBaseUrl extends HttpTestServiceBaseUrl { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithIndices( + Map value) { + final Uri $url = + Uri.parse('https://localhost:4000/test/map_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithRepeat( + Map value) { + final Uri $url = + Uri.parse('https://localhost:4000/test/map_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithComa( + Map value) { + final Uri $url = + Uri.parse('https://localhost:4000/test/map_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); return client.send($request); } diff --git a/chopper/test/test_service_base_url.dart b/chopper/test/test_service_base_url.dart index 02de8bcf..905707f1 100644 --- a/chopper/test/test_service_base_url.dart +++ b/chopper/test/test_service_base_url.dart @@ -31,11 +31,31 @@ abstract class HttpTestServiceBaseUrl extends ChopperService { @Query('value') List value, ); - @Get(path: '/list_query_param_with_brackets', useBrackets: true) + @Get(path: '/list_query_param_with_brackets_legacy', useBrackets: true) + Future> getUsingListQueryParamWithBracketsLegacy( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_brackets', listFormat: ListFormat.brackets) Future> getUsingListQueryParamWithBrackets( @Query('value') List value, ); + @Get(path: '/list_query_param_with_indices', listFormat: ListFormat.indices) + Future> getUsingListQueryParamWithIndices( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_repeat', listFormat: ListFormat.repeat) + Future> getUsingListQueryParamWithRepeat( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_comma', listFormat: ListFormat.comma) + Future> getUsingListQueryParamWithComma( + @Query('value') List value, + ); + @Get(path: '/map_query_param') Future> getUsingMapQueryParam( @Query('value') Map value, @@ -49,10 +69,30 @@ abstract class HttpTestServiceBaseUrl extends ChopperService { @Query('value') Map value, ); - @Get(path: '/map_query_param_with_brackets', useBrackets: true) + @Get(path: '/map_query_param_with_brackets_legacy', useBrackets: true) + Future> getUsingMapQueryParamWithBracketsLegacy( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_brackets', listFormat: ListFormat.brackets) Future> getUsingMapQueryParamWithBrackets( @Query('value') Map value, ); + + @Get(path: '/map_query_param_with_indices', listFormat: ListFormat.indices) + Future> getUsingMapQueryParamWithIndices( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_repeat', listFormat: ListFormat.repeat) + Future> getUsingMapQueryParamWithRepeat( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_comma', listFormat: ListFormat.comma) + Future> getUsingMapQueryParamWithComa( + @Query('value') Map value, + ); } Request customConvertRequest(Request req) { diff --git a/chopper/test/test_service_variable.chopper.dart b/chopper/test/test_service_variable.chopper.dart index 9c69ffa0..c60e766e 100644 --- a/chopper/test/test_service_variable.chopper.dart +++ b/chopper/test/test_service_variable.chopper.dart @@ -581,6 +581,22 @@ final class _$HttpTestServiceVariable extends HttpTestServiceVariable { return client.send($request); } + @override + Future> getUsingListQueryParamWithBracketsLegacy( + List value) { + final Uri $url = + Uri.parse('${service}/list_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + return client.send($request); + } + @override Future> getUsingListQueryParamWithBrackets( List value) { @@ -591,7 +607,51 @@ final class _$HttpTestServiceVariable extends HttpTestServiceVariable { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithIndices( + List value) { + final Uri $url = Uri.parse('${service}/list_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithRepeat( + List value) { + final Uri $url = Uri.parse('${service}/list_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithComma(List value) { + final Uri $url = Uri.parse('${service}/list_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); return client.send($request); } @@ -625,6 +685,22 @@ final class _$HttpTestServiceVariable extends HttpTestServiceVariable { return client.send($request); } + @override + Future> getUsingMapQueryParamWithBracketsLegacy( + Map value) { + final Uri $url = + Uri.parse('${service}/map_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + return client.send($request); + } + @override Future> getUsingMapQueryParamWithBrackets( Map value) { @@ -635,7 +711,52 @@ final class _$HttpTestServiceVariable extends HttpTestServiceVariable { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithIndices( + Map value) { + final Uri $url = Uri.parse('${service}/map_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithRepeat( + Map value) { + final Uri $url = Uri.parse('${service}/map_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithComma( + Map value) { + final Uri $url = Uri.parse('${service}/map_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); return client.send($request); } diff --git a/chopper/test/test_service_variable.dart b/chopper/test/test_service_variable.dart index 251b48cb..c878ada8 100644 --- a/chopper/test/test_service_variable.dart +++ b/chopper/test/test_service_variable.dart @@ -168,11 +168,31 @@ abstract class HttpTestServiceVariable extends ChopperService { @Query('value') List value, ); - @Get(path: '/list_query_param_with_brackets', useBrackets: true) + @Get(path: '/list_query_param_with_brackets_legacy', useBrackets: true) + Future> getUsingListQueryParamWithBracketsLegacy( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_brackets', listFormat: ListFormat.brackets) Future> getUsingListQueryParamWithBrackets( @Query('value') List value, ); + @Get(path: '/list_query_param_with_indices', listFormat: ListFormat.indices) + Future> getUsingListQueryParamWithIndices( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_repeat', listFormat: ListFormat.repeat) + Future> getUsingListQueryParamWithRepeat( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_comma', listFormat: ListFormat.comma) + Future> getUsingListQueryParamWithComma( + @Query('value') List value, + ); + @Get(path: '/map_query_param') Future> getUsingMapQueryParam( @Query('value') Map value, @@ -186,10 +206,30 @@ abstract class HttpTestServiceVariable extends ChopperService { @Query('value') Map value, ); - @Get(path: '/map_query_param_with_brackets', useBrackets: true) + @Get(path: '/map_query_param_with_brackets_legacy', useBrackets: true) + Future> getUsingMapQueryParamWithBracketsLegacy( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_brackets', listFormat: ListFormat.brackets) Future> getUsingMapQueryParamWithBrackets( @Query('value') Map value, ); + + @Get(path: '/map_query_param_with_indices', listFormat: ListFormat.indices) + Future> getUsingMapQueryParamWithIndices( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_repeat', listFormat: ListFormat.repeat) + Future> getUsingMapQueryParamWithRepeat( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_comma', listFormat: ListFormat.comma) + Future> getUsingMapQueryParamWithComma( + @Query('value') Map value, + ); } Request customConvertRequest(Request req) { diff --git a/chopper/test/test_without_response_service.chopper.dart b/chopper/test/test_without_response_service.chopper.dart index c7b8462a..2bc8fcbd 100644 --- a/chopper/test/test_without_response_service.chopper.dart +++ b/chopper/test/test_without_response_service.chopper.dart @@ -616,6 +616,22 @@ final class _$HttpTestService extends HttpTestService { return $response.bodyOrThrow; } + @override + Future getUsingListQueryParamWithBracketsLegacy( + List value) async { + final Uri $url = Uri.parse('/test/list_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + @override Future getUsingListQueryParamWithBrackets(List value) async { final Uri $url = Uri.parse('/test/list_query_param_with_brackets'); @@ -625,7 +641,52 @@ final class _$HttpTestService extends HttpTestService { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingListQueryParamWithIndices(List value) async { + final Uri $url = Uri.parse('/test/list_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingListQueryParamWithRepeat(List value) async { + final Uri $url = Uri.parse('/test/list_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingListQueryParamWithComma(List value) async { + final Uri $url = Uri.parse('/test/list_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); final Response $response = await client.send($request); return $response.bodyOrThrow; @@ -661,6 +722,22 @@ final class _$HttpTestService extends HttpTestService { return $response.bodyOrThrow; } + @override + Future getUsingMapQueryParamWithBracketsLegacy( + Map value) async { + final Uri $url = Uri.parse('/test/map_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + @override Future getUsingMapQueryParamWithBrackets( Map value) async { @@ -671,7 +748,55 @@ final class _$HttpTestService extends HttpTestService { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingMapQueryParamWithIndices( + Map value) async { + final Uri $url = Uri.parse('/test/map_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingMapQueryParamWithRepeat( + Map value) async { + final Uri $url = Uri.parse('/test/map_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingMapQueryParamWithComma( + Map value) async { + final Uri $url = Uri.parse('/test/map_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); final Response $response = await client.send($request); return $response.bodyOrThrow; diff --git a/chopper/test/test_without_response_service.dart b/chopper/test/test_without_response_service.dart index d8a95417..d29c0eb9 100644 --- a/chopper/test/test_without_response_service.dart +++ b/chopper/test/test_without_response_service.dart @@ -166,11 +166,31 @@ abstract class HttpTestService extends ChopperService { @Query('value') List value, ); - @Get(path: '/list_query_param_with_brackets', useBrackets: true) + @Get(path: '/list_query_param_with_brackets_legacy', useBrackets: true) + Future getUsingListQueryParamWithBracketsLegacy( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_brackets', listFormat: ListFormat.brackets) Future getUsingListQueryParamWithBrackets( @Query('value') List value, ); + @Get(path: '/list_query_param_with_indices', listFormat: ListFormat.indices) + Future getUsingListQueryParamWithIndices( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_repeat', listFormat: ListFormat.repeat) + Future getUsingListQueryParamWithRepeat( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_comma', listFormat: ListFormat.comma) + Future getUsingListQueryParamWithComma( + @Query('value') List value, + ); + @Get(path: '/map_query_param') Future getUsingMapQueryParam( @Query('value') Map value, @@ -184,11 +204,31 @@ abstract class HttpTestService extends ChopperService { @Query('value') Map value, ); - @Get(path: '/map_query_param_with_brackets', useBrackets: true) + @Get(path: '/map_query_param_with_brackets_legacy', useBrackets: true) + Future getUsingMapQueryParamWithBracketsLegacy( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_brackets', listFormat: ListFormat.brackets) Future getUsingMapQueryParamWithBrackets( @Query('value') Map value, ); + @Get(path: '/map_query_param_with_indices', listFormat: ListFormat.indices) + Future getUsingMapQueryParamWithIndices( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_repeat', listFormat: ListFormat.repeat) + Future getUsingMapQueryParamWithRepeat( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_comma', listFormat: ListFormat.comma) + Future getUsingMapQueryParamWithComma( + @Query('value') Map value, + ); + @Get(path: 'headers') Future getHeaders({ @Header('x-string') required String stringHeader, diff --git a/chopper/test/utils_test.dart b/chopper/test/utils_test.dart index 63a35084..fc4e6810 100644 --- a/chopper/test/utils_test.dart +++ b/chopper/test/utils_test.dart @@ -1,12 +1,17 @@ +// ignore_for_file: deprecated_member_use_from_same_package + +import 'package:chopper/src/list_format.dart'; import 'package:chopper/src/request.dart'; import 'package:chopper/src/utils.dart'; import 'package:test/test.dart'; +import 'fixtures/example_enum.dart'; + void main() { group('mapToQuery single', () { , String>{ {'foo': null}: '', - {'foo': ''}: 'foo=', + {'foo': ''}: '', {'foo': ' '}: 'foo=%20', {'foo': ' '}: 'foo=%20%20', {'foo': '\t'}: 'foo=%09', @@ -62,12 +67,12 @@ void main() { group('mapToQuery multiple', () { , String>{ {'foo': null, 'baz': null}: '', - {'foo': '', 'baz': ''}: 'foo=&baz=', - {'foo': null, 'baz': ''}: 'baz=', - {'foo': '', 'baz': null}: 'foo=', - {'foo': 'bar', 'baz': ''}: 'foo=bar&baz=', + {'foo': '', 'baz': ''}: '', + {'foo': null, 'baz': ''}: '', + {'foo': '', 'baz': null}: '', + {'foo': 'bar', 'baz': ''}: 'foo=bar', {'foo': null, 'baz': 'etc'}: 'baz=etc', - {'foo': '', 'baz': 'etc'}: 'foo=&baz=etc', + {'foo': '', 'baz': 'etc'}: 'baz=etc', {'foo': 'bar', 'baz': 'etc'}: 'foo=bar&baz=etc', {'foo': 'null', 'baz': 'null'}: 'foo=null&baz=null', {'foo': ' ', 'baz': ' '}: 'foo=%20&baz=%20', @@ -113,7 +118,7 @@ void main() { ); }); - group('mapToQuery lists', () { + group('mapToQuery lists with repeat (default)', () { , String>{ { 'foo': ['bar', 'baz', 'etc'], @@ -150,12 +155,28 @@ void main() { 'bar': 'baz', 'etc': '', 'xyz': null, - }: 'foo=bar&foo=baz&foo=etc&bar=baz&etc=', - }.forEach((map, query) => - test('$map -> $query', () => expect(mapToQuery(map), query))); + }: 'foo=bar&foo=baz&foo=etc&bar=baz', + }.forEach((map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery(map), + query, + reason: 'legacy default', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery(map, listFormat: ListFormat.repeat), + query, + ), + ); + }); }); - group('mapToQuery lists with includeNullQueryVars', () { + group('mapToQuery lists with repeat (default) with includeNullQueryVars', () { , String>{ { 'foo': ['bar', 'baz', 'etc'], @@ -165,22 +186,22 @@ void main() { }: 'foo=bar&foo=123&foo=456.789&foo=0&foo=-123&foo=-456.789', { 'foo': ['', 'baz', 'etc'], - }: 'foo=baz&foo=etc', + }: 'foo=&foo=baz&foo=etc', { 'foo': ['bar', '', 'etc'], - }: 'foo=bar&foo=etc', + }: 'foo=bar&foo=&foo=etc', { 'foo': ['bar', 'baz', ''], - }: 'foo=bar&foo=baz', + }: 'foo=bar&foo=baz&foo=', { 'foo': [null, 'baz', 'etc'], - }: 'foo=baz&foo=etc', + }: 'foo=&foo=baz&foo=etc', { 'foo': ['bar', null, 'etc'], - }: 'foo=bar&foo=etc', + }: 'foo=bar&foo=&foo=etc', { 'foo': ['bar', 'baz', null], - }: 'foo=bar&foo=baz', + }: 'foo=bar&foo=baz&foo=', { 'foo': ['bar', 'baz', ' '], }: 'foo=bar&foo=baz&foo=%20', @@ -194,10 +215,28 @@ void main() { 'xyz': null, }: 'foo=bar&foo=baz&foo=etc&bar=baz&etc=&xyz=', }.forEach( - (map, query) => test( - '$map -> $query', - () => expect(mapToQuery(map, includeNullQueryVars: true), query), - ), + (map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery(map, includeNullQueryVars: true), + query, + reason: 'legacy default', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.repeat, + includeNullQueryVars: true, + ), + query, + ), + ); + }, ); }); @@ -238,15 +277,26 @@ void main() { 'bar': 'baz', 'etc': '', 'xyz': null, - }: 'foo%5B%5D=bar&foo%5B%5D=baz&foo%5B%5D=etc&bar=baz&etc=', + }: 'foo%5B%5D=bar&foo%5B%5D=baz&foo%5B%5D=etc&bar=baz', }.forEach( - (map, query) => test( - '$map -> $query', - () => expect( - mapToQuery(map, useBrackets: true), - query, - ), - ), + (map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery(map, useBrackets: true), + query, + reason: 'legacy brackets', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery(map, listFormat: ListFormat.brackets), + query, + ), + ); + }, ); }); @@ -260,22 +310,22 @@ void main() { }: 'foo%5B%5D=bar&foo%5B%5D=123&foo%5B%5D=456.789&foo%5B%5D=0&foo%5B%5D=-123&foo%5B%5D=-456.789', { 'foo': ['', 'baz', 'etc'], - }: 'foo%5B%5D=baz&foo%5B%5D=etc', + }: 'foo%5B%5D=&foo%5B%5D=baz&foo%5B%5D=etc', { 'foo': ['bar', '', 'etc'], - }: 'foo%5B%5D=bar&foo%5B%5D=etc', + }: 'foo%5B%5D=bar&foo%5B%5D=&foo%5B%5D=etc', { 'foo': ['bar', 'baz', ''], - }: 'foo%5B%5D=bar&foo%5B%5D=baz', + }: 'foo%5B%5D=bar&foo%5B%5D=baz&foo%5B%5D=', { 'foo': [null, 'baz', 'etc'], - }: 'foo%5B%5D=baz&foo%5B%5D=etc', + }: 'foo%5B%5D=&foo%5B%5D=baz&foo%5B%5D=etc', { 'foo': ['bar', null, 'etc'], - }: 'foo%5B%5D=bar&foo%5B%5D=etc', + }: 'foo%5B%5D=bar&foo%5B%5D=&foo%5B%5D=etc', { 'foo': ['bar', 'baz', null], - }: 'foo%5B%5D=bar&foo%5B%5D=baz', + }: 'foo%5B%5D=bar&foo%5B%5D=baz&foo%5B%5D=', { 'foo': ['bar', 'baz', ' '], }: 'foo%5B%5D=bar&foo%5B%5D=baz&foo%5B%5D=%20', @@ -288,25 +338,248 @@ void main() { 'etc': '', 'xyz': null, }: 'foo%5B%5D=bar&foo%5B%5D=baz&foo%5B%5D=etc&bar=baz&etc=&xyz=', + }.forEach( + (map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + useBrackets: true, + includeNullQueryVars: true, + ), + query, + reason: 'legacy brackets', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.brackets, + includeNullQueryVars: true, + ), + query, + ), + ); + }, + ); + }); + + group('mapToQuery lists with indices', () { + , String>{ + { + 'foo': ['bar', 'baz', 'etc'], + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz&foo%5B2%5D=etc', + { + 'foo': ['bar', 123, 456.789, 0, -123, -456.789], + }: 'foo%5B0%5D=bar&foo%5B1%5D=123&foo%5B2%5D=456.789&foo%5B3%5D=0&foo%5B4%5D=-123&foo%5B5%5D=-456.789', + { + 'foo': ['', 'baz', 'etc'], + }: 'foo%5B1%5D=baz&foo%5B2%5D=etc', + { + 'foo': ['bar', '', 'etc'], + }: 'foo%5B0%5D=bar&foo%5B2%5D=etc', + { + 'foo': ['bar', 'baz', ''], + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz', + { + 'foo': [null, 'baz', 'etc'], + }: 'foo%5B1%5D=baz&foo%5B2%5D=etc', + { + 'foo': ['bar', null, 'etc'], + }: 'foo%5B0%5D=bar&foo%5B2%5D=etc', + { + 'foo': ['bar', 'baz', null], + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz', + { + 'foo': ['bar', 'baz', ' '], + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz&foo%5B2%5D=%20', + { + 'foo': ['bar', 'baz', '\t'], + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz&foo%5B2%5D=%09', + { + 'foo': ['bar', 'baz', 'etc'], + 'bar': 'baz', + 'etc': '', + 'xyz': null, + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz&foo%5B2%5D=etc&bar=baz', + }.forEach( + (map, query) => test( + '$map -> $query', + () => expect( + mapToQuery(map, listFormat: ListFormat.indices), + query, + ), + ), + ); + }); + + group('mapToQuery lists with indices with includeNullQueryVars', () { + , String>{ + { + 'foo': ['bar', 'baz', 'etc'], + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz&foo%5B2%5D=etc', + { + 'foo': ['bar', 123, 456.789, 0, -123, -456.789], + }: 'foo%5B0%5D=bar&foo%5B1%5D=123&foo%5B2%5D=456.789&foo%5B3%5D=0&foo%5B4%5D=-123&foo%5B5%5D=-456.789', + { + 'foo': ['', 'baz', 'etc'], + }: 'foo%5B0%5D=&foo%5B1%5D=baz&foo%5B2%5D=etc', + { + 'foo': ['bar', '', 'etc'], + }: 'foo%5B0%5D=bar&foo%5B1%5D=&foo%5B2%5D=etc', + { + 'foo': ['bar', 'baz', ''], + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz&foo%5B2%5D=', + { + 'foo': [null, 'baz', 'etc'], + }: 'foo%5B0%5D=&foo%5B1%5D=baz&foo%5B2%5D=etc', + { + 'foo': ['bar', null, 'etc'], + }: 'foo%5B0%5D=bar&foo%5B1%5D=&foo%5B2%5D=etc', + { + 'foo': ['bar', 'baz', null], + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz&foo%5B2%5D=', + { + 'foo': ['bar', 'baz', ' '], + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz&foo%5B2%5D=%20', + { + 'foo': ['bar', 'baz', '\t'], + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz&foo%5B2%5D=%09', + { + 'foo': ['bar', 'baz', 'etc'], + 'bar': 'baz', + 'etc': '', + 'xyz': null, + }: 'foo%5B0%5D=bar&foo%5B1%5D=baz&foo%5B2%5D=etc&bar=baz&etc=&xyz=', + }.forEach( + (map, query) => test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.indices, + includeNullQueryVars: true, + ), + query, + ), + ), + ); + }); + + group('mapToQuery lists with comma', () { + , String>{ + { + 'foo': ['bar', 'baz', 'etc'], + }: 'foo=bar%2Cbaz%2Cetc', + { + 'foo': ['bar', 123, 456.789, 0, -123, -456.789], + }: 'foo=bar%2C123%2C456.789%2C0%2C-123%2C-456.789', + { + 'foo': ['', 'baz', 'etc'], + }: 'foo=%2Cbaz%2Cetc', + { + 'foo': ['bar', '', 'etc'], + }: 'foo=bar%2C%2Cetc', + { + 'foo': ['bar', 'baz', ''], + }: 'foo=bar%2Cbaz%2C', + { + 'foo': [null, 'baz', 'etc'], + }: 'foo=%2Cbaz%2Cetc', + { + 'foo': ['bar', null, 'etc'], + }: 'foo=bar%2C%2Cetc', + { + 'foo': ['bar', 'baz', null], + }: 'foo=bar%2Cbaz%2C', + { + 'foo': ['bar', 'baz', ' '], + }: 'foo=bar%2Cbaz%2C%20', + { + 'foo': ['bar', 'baz', '\t'], + }: 'foo=bar%2Cbaz%2C%09', + { + 'foo': ['bar', 'baz', 'etc'], + 'bar': 'baz', + 'etc': '', + 'xyz': null, + }: 'foo=bar%2Cbaz%2Cetc&bar=baz', + }.forEach( + (map, query) => test( + '$map -> $query', + () => expect( + mapToQuery(map, listFormat: ListFormat.comma), + query, + ), + ), + ); + }); + + group('mapToQuery lists with comma with includeNullQueryVars', () { + , String>{ + { + 'foo': ['bar', 'baz', 'etc'], + }: 'foo=bar%2Cbaz%2Cetc', + { + 'foo': ['bar', 123, 456.789, 0, -123, -456.789], + }: 'foo=bar%2C123%2C456.789%2C0%2C-123%2C-456.789', + { + 'foo': ['', 'baz', 'etc'], + }: 'foo=%2Cbaz%2Cetc', + { + 'foo': ['bar', '', 'etc'], + }: 'foo=bar%2C%2Cetc', + { + 'foo': ['bar', 'baz', ''], + }: 'foo=bar%2Cbaz%2C', + { + 'foo': [null, 'baz', 'etc'], + }: 'foo=%2Cbaz%2Cetc', + { + 'foo': ['bar', null, 'etc'], + }: 'foo=bar%2C%2Cetc', + { + 'foo': ['bar', 'baz', null], + }: 'foo=bar%2Cbaz%2C', + { + 'foo': ['bar', 'baz', ' '], + }: 'foo=bar%2Cbaz%2C%20', + { + 'foo': ['bar', 'baz', '\t'], + }: 'foo=bar%2Cbaz%2C%09', + { + 'foo': ['bar', 'baz', 'etc'], + 'bar': 'baz', + 'etc': '', + 'xyz': null, + }: 'foo=bar%2Cbaz%2Cetc&bar=baz&etc=&xyz=', }.forEach( (map, query) => test( '$map -> $query', () => expect( - mapToQuery(map, useBrackets: true, includeNullQueryVars: true), + mapToQuery( + map, + listFormat: ListFormat.comma, + includeNullQueryVars: true, + ), query, ), ), ); }); - group('mapToQuery maps', () { + group('mapToQuery maps with repeat (default)', () { , String>{ { 'foo': {'bar': 'baz'}, }: 'foo.bar=baz', { 'foo': {'bar': ''}, - }: 'foo.bar=', + }: '', { 'foo': {'bar': null}, }: '', @@ -333,7 +606,7 @@ void main() { 'tab': '\t', 'list': ['a', 123, false], }, - }: 'foo.bar=baz&foo.int=123&foo.double=456.789&foo.zero=0&foo.negInt=-123&foo.negDouble=-456.789&foo.emptyString=&foo.space=%20&foo.tab=%09&foo.list=a&foo.list=123&foo.list=false', + }: 'foo.bar=baz&foo.int=123&foo.double=456.789&foo.zero=0&foo.negInt=-123&foo.negDouble=-456.789&foo.space=%20&foo.tab=%09&foo%2Elist=a&foo%2Elist=123&foo%2Elist=false', { 'foo': {'bar': 'baz'}, 'etc': 'xyz', @@ -356,12 +629,49 @@ void main() { }, }, }, - }: 'foo.bar=baz&foo.zap=abc&foo.etc.abc=def&foo.etc.ghi=jkl&foo.etc.mno.opq=rst&foo.etc.mno.uvw=xyz&foo.etc.mno.aab=bbc&foo.etc.mno.aab=ccd&foo.etc.mno.aab=eef', - }.forEach((map, query) => - test('$map -> $query', () => expect(mapToQuery(map), query))); + }: 'foo.bar=baz&foo.zap=abc&foo%2Eetc.abc=def&foo%2Eetc.ghi=jkl&foo%2Eetc%2Emno.opq=rst&foo%2Eetc%2Emno.uvw=xyz&foo%2Eetc%2Emno%2Eaab=bbc&foo%2Eetc%2Emno%2Eaab=ccd&foo%2Eetc%2Emno%2Eaab=eef', + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'John doe', + }, + } + } + }: r'filters%2E$or%2Edate.$eq=2020-01-01&filters%2E$or%2Edate.$eq=2020-01-02&filters%2Eauthor%2Ename.$eq=John%20doe', + }.forEach((map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery(map), + query, + reason: 'legacy default', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery(map, listFormat: ListFormat.repeat), + query, + ), + ); + }); }); - group('mapToQuery maps with includeNullQueryVars', () { + group('mapToQuery maps with repeat (default) with includeNullQueryVars', () { , String>{ { 'foo': {'bar': 'baz'}, @@ -395,7 +705,7 @@ void main() { 'tab': '\t', 'list': ['a', 123, false], }, - }: 'foo.bar=baz&foo.int=123&foo.double=456.789&foo.zero=0&foo.negInt=-123&foo.negDouble=-456.789&foo.emptyString=&foo.nullValue=&foo.space=%20&foo.tab=%09&foo.list=a&foo.list=123&foo.list=false', + }: 'foo.bar=baz&foo.int=123&foo.double=456.789&foo.zero=0&foo.negInt=-123&foo.negDouble=-456.789&foo.emptyString=&foo.nullValue=&foo.space=%20&foo.tab=%09&foo%2Elist=a&foo%2Elist=123&foo%2Elist=false', { 'foo': {'bar': 'baz'}, 'etc': 'xyz', @@ -418,12 +728,51 @@ void main() { }, }, }, - }: 'foo.bar=baz&foo.zap=abc&foo.etc.abc=def&foo.etc.ghi=jkl&foo.etc.mno.opq=rst&foo.etc.mno.uvw=xyz&foo.etc.mno.aab=bbc&foo.etc.mno.aab=ccd&foo.etc.mno.aab=eef', + }: 'foo.bar=baz&foo.zap=abc&foo%2Eetc.abc=def&foo%2Eetc.ghi=jkl&foo%2Eetc%2Emno.opq=rst&foo%2Eetc%2Emno.uvw=xyz&foo%2Eetc%2Emno%2Eaab=bbc&foo%2Eetc%2Emno%2Eaab=ccd&foo%2Eetc%2Emno%2Eaab=eef', + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'John doe', + }, + } + } + }: r'filters%2E$or%2Edate.$eq=2020-01-01&filters%2E$or%2Edate.$eq=2020-01-02&filters%2Eauthor%2Ename.$eq=John%20doe', }.forEach( - (map, query) => test( - '$map -> $query', - () => expect(mapToQuery(map, includeNullQueryVars: true), query), - ), + (map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery(map, includeNullQueryVars: true), + query, + reason: 'legacy default', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.repeat, + includeNullQueryVars: true, + ), + query, + ), + ); + }, ); }); @@ -434,7 +783,7 @@ void main() { }: 'foo%5Bbar%5D=baz', { 'foo': {'bar': ''}, - }: 'foo%5Bbar%5D=', + }: '', { 'foo': {'bar': null}, }: '', @@ -461,7 +810,7 @@ void main() { 'tab': '\t', 'list': ['a', 123, false], }, - }: 'foo%5Bbar%5D=baz&foo%5Bint%5D=123&foo%5Bdouble%5D=456.789&foo%5Bzero%5D=0&foo%5BnegInt%5D=-123&foo%5BnegDouble%5D=-456.789&foo%5BemptyString%5D=&foo%5Bspace%5D=%20&foo%5Btab%5D=%09&foo%5Blist%5D%5B%5D=a&foo%5Blist%5D%5B%5D=123&foo%5Blist%5D%5B%5D=false', + }: 'foo%5Bbar%5D=baz&foo%5Bint%5D=123&foo%5Bdouble%5D=456.789&foo%5Bzero%5D=0&foo%5BnegInt%5D=-123&foo%5BnegDouble%5D=-456.789&foo%5Bspace%5D=%20&foo%5Btab%5D=%09&foo%5Blist%5D%5B%5D=a&foo%5Blist%5D%5B%5D=123&foo%5Blist%5D%5B%5D=false', { 'foo': {'bar': 'baz'}, 'etc': 'xyz', @@ -485,14 +834,46 @@ void main() { }, }, }: 'foo%5Bbar%5D=baz&foo%5Bzap%5D=abc&foo%5Betc%5D%5Babc%5D=def&foo%5Betc%5D%5Bghi%5D=jkl&foo%5Betc%5D%5Bmno%5D%5Bopq%5D=rst&foo%5Betc%5D%5Bmno%5D%5Buvw%5D=xyz&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B%5D=bbc&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B%5D=ccd&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B%5D=eef', + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'John doe', + }, + } + } + }: 'filters%5B%24or%5D%5B%5D%5Bdate%5D%5B%24eq%5D=2020-01-01&filters%5B%24or%5D%5B%5D%5Bdate%5D%5B%24eq%5D=2020-01-02&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=John%20doe', }.forEach( - (map, query) => test( - '$map -> $query', - () => expect( - mapToQuery(map, useBrackets: true), - query, - ), - ), + (map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery(map, useBrackets: true), + query, + reason: 'legacy brackets', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery(map, listFormat: ListFormat.brackets), + query, + ), + ); + }, ); }); @@ -554,17 +935,885 @@ void main() { }, }, }: 'foo%5Bbar%5D=baz&foo%5Bzap%5D=abc&foo%5Betc%5D%5Babc%5D=def&foo%5Betc%5D%5Bghi%5D=jkl&foo%5Betc%5D%5Bmno%5D%5Bopq%5D=rst&foo%5Betc%5D%5Bmno%5D%5Buvw%5D=xyz&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B%5D=bbc&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B%5D=ccd&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B%5D=eef', + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'John doe', + }, + } + } + }: 'filters%5B%24or%5D%5B%5D%5Bdate%5D%5B%24eq%5D=2020-01-01&filters%5B%24or%5D%5B%5D%5Bdate%5D%5B%24eq%5D=2020-01-02&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=John%20doe', }.forEach( - (map, query) => test( - '$map -> $query', - () => expect( - mapToQuery(map, useBrackets: true, includeNullQueryVars: true), - query, - ), - ), + (map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + useBrackets: true, + includeNullQueryVars: true, + ), + query, + reason: 'legacy brackets', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.brackets, + includeNullQueryVars: true, + ), + query, + ), + ); + }, ); }); + group('mapToQuery maps with indices', () { + , String>{ + { + 'foo': {'bar': 'baz'}, + }: 'foo%5Bbar%5D=baz', + { + 'foo': {'bar': ''}, + }: '', + { + 'foo': {'bar': null}, + }: '', + { + 'foo': {'bar': ' '}, + }: 'foo%5Bbar%5D=%20', + { + 'foo': {'bar': '\t'}, + }: 'foo%5Bbar%5D=%09', + { + 'foo': {'bar': 'baz', 'etc': 'xyz', 'space': ' ', 'tab': '\t'}, + }: 'foo%5Bbar%5D=baz&foo%5Betc%5D=xyz&foo%5Bspace%5D=%20&foo%5Btab%5D=%09', + { + 'foo': { + 'bar': 'baz', + 'int': 123, + 'double': 456.789, + 'zero': 0, + 'negInt': -123, + 'negDouble': -456.789, + 'emptyString': '', + 'nullValue': null, + 'space': ' ', + 'tab': '\t', + 'list': ['a', 123, false], + }, + }: 'foo%5Bbar%5D=baz&foo%5Bint%5D=123&foo%5Bdouble%5D=456.789&foo%5Bzero%5D=0&foo%5BnegInt%5D=-123&foo%5BnegDouble%5D=-456.789&foo%5Bspace%5D=%20&foo%5Btab%5D=%09&foo%5Blist%5D%5B0%5D=a&foo%5Blist%5D%5B1%5D=123&foo%5Blist%5D%5B2%5D=false', + { + 'foo': {'bar': 'baz'}, + 'etc': 'xyz', + }: 'foo%5Bbar%5D=baz&etc=xyz', + { + 'foo': { + 'bar': 'baz', + 'zap': 'abc', + 'etc': { + 'abc': 'def', + 'ghi': 'jkl', + 'mno': { + 'opq': 'rst', + 'uvw': 'xyz', + 'aab': [ + 'bbc', + 'ccd', + 'eef', + ], + }, + }, + }, + }: 'foo%5Bbar%5D=baz&foo%5Bzap%5D=abc&foo%5Betc%5D%5Babc%5D=def&foo%5Betc%5D%5Bghi%5D=jkl&foo%5Betc%5D%5Bmno%5D%5Bopq%5D=rst&foo%5Betc%5D%5Bmno%5D%5Buvw%5D=xyz&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B0%5D=bbc&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B1%5D=ccd&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B2%5D=eef', + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'John doe', + }, + } + } + }: 'filters%5B%24or%5D%5B0%5D%5Bdate%5D%5B%24eq%5D=2020-01-01&filters%5B%24or%5D%5B1%5D%5Bdate%5D%5B%24eq%5D=2020-01-02&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=John%20doe', + }.forEach( + (map, query) => test( + '$map -> $query', + () => expect( + mapToQuery(map, listFormat: ListFormat.indices), + query, + ), + ), + ); + }); + + group('mapToQuery maps with indices with includeNullQueryVars', () { + , String>{ + { + 'foo': {'bar': 'baz'}, + }: 'foo%5Bbar%5D=baz', + { + 'foo': {'bar': ''}, + }: 'foo%5Bbar%5D=', + { + 'foo': {'bar': null}, + }: 'foo%5Bbar%5D=', + { + 'foo': {'bar': ' '}, + }: 'foo%5Bbar%5D=%20', + { + 'foo': {'bar': '\t'}, + }: 'foo%5Bbar%5D=%09', + { + 'foo': {'bar': 'baz', 'etc': 'xyz', 'space': ' ', 'tab': '\t'}, + }: 'foo%5Bbar%5D=baz&foo%5Betc%5D=xyz&foo%5Bspace%5D=%20&foo%5Btab%5D=%09', + { + 'foo': { + 'bar': 'baz', + 'int': 123, + 'double': 456.789, + 'zero': 0, + 'negInt': -123, + 'negDouble': -456.789, + 'emptyString': '', + 'nullValue': null, + 'space': ' ', + 'tab': '\t', + 'list': ['a', 123, false], + }, + }: 'foo%5Bbar%5D=baz&foo%5Bint%5D=123&foo%5Bdouble%5D=456.789&foo%5Bzero%5D=0&foo%5BnegInt%5D=-123&foo%5BnegDouble%5D=-456.789&foo%5BemptyString%5D=&foo%5BnullValue%5D=&foo%5Bspace%5D=%20&foo%5Btab%5D=%09&foo%5Blist%5D%5B0%5D=a&foo%5Blist%5D%5B1%5D=123&foo%5Blist%5D%5B2%5D=false', + { + 'foo': {'bar': 'baz'}, + 'etc': 'xyz', + }: 'foo%5Bbar%5D=baz&etc=xyz', + { + 'foo': { + 'bar': 'baz', + 'zap': 'abc', + 'etc': { + 'abc': 'def', + 'ghi': 'jkl', + 'mno': { + 'opq': 'rst', + 'uvw': 'xyz', + 'aab': [ + 'bbc', + 'ccd', + 'eef', + ], + }, + }, + }, + }: 'foo%5Bbar%5D=baz&foo%5Bzap%5D=abc&foo%5Betc%5D%5Babc%5D=def&foo%5Betc%5D%5Bghi%5D=jkl&foo%5Betc%5D%5Bmno%5D%5Bopq%5D=rst&foo%5Betc%5D%5Bmno%5D%5Buvw%5D=xyz&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B0%5D=bbc&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B1%5D=ccd&foo%5Betc%5D%5Bmno%5D%5Baab%5D%5B2%5D=eef', + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'John doe', + }, + } + } + }: 'filters%5B%24or%5D%5B0%5D%5Bdate%5D%5B%24eq%5D=2020-01-01&filters%5B%24or%5D%5B1%5D%5Bdate%5D%5B%24eq%5D=2020-01-02&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=John%20doe', + }.forEach( + (map, query) => test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.indices, + includeNullQueryVars: true, + ), + query, + ), + ), + ); + }); + + group('mapToQuery maps with comma', () { + , String>{ + { + 'foo': {'bar': 'baz'}, + }: 'foo%5Bbar%5D=baz', + { + 'foo': {'bar': ''}, + }: '', + { + 'foo': {'bar': null}, + }: '', + { + 'foo': {'bar': ' '}, + }: 'foo%5Bbar%5D=%20', + { + 'foo': {'bar': '\t'}, + }: 'foo%5Bbar%5D=%09', + { + 'foo': {'bar': 'baz', 'etc': 'xyz', 'space': ' ', 'tab': '\t'}, + }: 'foo%5Bbar%5D=baz&foo%5Betc%5D=xyz&foo%5Bspace%5D=%20&foo%5Btab%5D=%09', + { + 'foo': { + 'bar': 'baz', + 'int': 123, + 'double': 456.789, + 'zero': 0, + 'negInt': -123, + 'negDouble': -456.789, + 'emptyString': '', + 'nullValue': null, + 'space': ' ', + 'tab': '\t', + 'list': ['a', 123, false], + }, + }: 'foo%5Bbar%5D=baz&foo%5Bint%5D=123&foo%5Bdouble%5D=456.789&foo%5Bzero%5D=0&foo%5BnegInt%5D=-123&foo%5BnegDouble%5D=-456.789&foo%5Bspace%5D=%20&foo%5Btab%5D=%09&foo%5Blist%5D=a%2C123%2Cfalse', + { + 'foo': {'bar': 'baz'}, + 'etc': 'xyz', + }: 'foo%5Bbar%5D=baz&etc=xyz', + { + 'foo': { + 'bar': 'baz', + 'zap': 'abc', + 'etc': { + 'abc': 'def', + 'ghi': 'jkl', + 'mno': { + 'opq': 'rst', + 'uvw': 'xyz', + 'aab': [ + 'bbc', + 'ccd', + 'eef', + ], + }, + }, + }, + }: 'foo%5Bbar%5D=baz&foo%5Bzap%5D=abc&foo%5Betc%5D%5Babc%5D=def&foo%5Betc%5D%5Bghi%5D=jkl&foo%5Betc%5D%5Bmno%5D%5Bopq%5D=rst&foo%5Betc%5D%5Bmno%5D%5Buvw%5D=xyz&foo%5Betc%5D%5Bmno%5D%5Baab%5D=bbc%2Cccd%2Ceef', + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'John doe', + }, + } + } + }: 'filters%5B%24or%5D=%7Bdate%3A%20%7B%24eq%3A%202020-01-01%7D%7D%2C%7Bdate%3A%20%7B%24eq%3A%202020-01-02%7D%7D&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=John%20doe', + }.forEach( + (map, query) => test( + '$map -> $query', + () => expect( + mapToQuery(map, listFormat: ListFormat.comma), + query, + ), + ), + ); + }); + + group('mapToQuery maps with comma with includeNullQueryVars', () { + , String>{ + { + 'foo': {'bar': 'baz'}, + }: 'foo%5Bbar%5D=baz', + { + 'foo': {'bar': ''}, + }: 'foo%5Bbar%5D=', + { + 'foo': {'bar': null}, + }: 'foo%5Bbar%5D=', + { + 'foo': {'bar': ' '}, + }: 'foo%5Bbar%5D=%20', + { + 'foo': {'bar': '\t'}, + }: 'foo%5Bbar%5D=%09', + { + 'foo': {'bar': 'baz', 'etc': 'xyz', 'space': ' ', 'tab': '\t'}, + }: 'foo%5Bbar%5D=baz&foo%5Betc%5D=xyz&foo%5Bspace%5D=%20&foo%5Btab%5D=%09', + { + 'foo': { + 'bar': 'baz', + 'int': 123, + 'double': 456.789, + 'zero': 0, + 'negInt': -123, + 'negDouble': -456.789, + 'emptyString': '', + 'nullValue': null, + 'space': ' ', + 'tab': '\t', + 'list': ['a', 123, false], + }, + }: 'foo%5Bbar%5D=baz&foo%5Bint%5D=123&foo%5Bdouble%5D=456.789&foo%5Bzero%5D=0&foo%5BnegInt%5D=-123&foo%5BnegDouble%5D=-456.789&foo%5BemptyString%5D=&foo%5BnullValue%5D=&foo%5Bspace%5D=%20&foo%5Btab%5D=%09&foo%5Blist%5D=a%2C123%2Cfalse', + { + 'foo': {'bar': 'baz'}, + 'etc': 'xyz', + }: 'foo%5Bbar%5D=baz&etc=xyz', + { + 'foo': { + 'bar': 'baz', + 'zap': 'abc', + 'etc': { + 'abc': 'def', + 'ghi': 'jkl', + 'mno': { + 'opq': 'rst', + 'uvw': 'xyz', + 'aab': [ + 'bbc', + 'ccd', + 'eef', + ], + }, + }, + }, + }: 'foo%5Bbar%5D=baz&foo%5Bzap%5D=abc&foo%5Betc%5D%5Babc%5D=def&foo%5Betc%5D%5Bghi%5D=jkl&foo%5Betc%5D%5Bmno%5D%5Bopq%5D=rst&foo%5Betc%5D%5Bmno%5D%5Buvw%5D=xyz&foo%5Betc%5D%5Bmno%5D%5Baab%5D=bbc%2Cccd%2Ceef', + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'John doe', + }, + } + } + }: 'filters%5B%24or%5D=%7Bdate%3A%20%7B%24eq%3A%202020-01-01%7D%7D%2C%7Bdate%3A%20%7B%24eq%3A%202020-01-02%7D%7D&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=John%20doe', + }.forEach( + (map, query) => test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.comma, + includeNullQueryVars: true, + ), + query, + ), + ), + ); + }); + + group('mapToQuery maps with indices and nested lists', () { + group( + 'mapToQuery maps with repeat (default) and nested lists', + () { + , String>{ + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + null, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'Kai doe', + }, + } + } + }: r'filters%2E$or%2Edate.$eq=2020-01-01&filters%2E$or%2Edate.$eq=2020-01-02&filters%2Eauthor%2Ename.$eq=Kai%20doe', + { + 'filters': { + 'id': { + r'$in': [3, 6, 8], + }, + } + }: r'filters%2Eid%2E$in=3&filters%2Eid%2E$in=6&filters%2Eid%2E$in=8' + }.forEach( + (map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + ), + query, + reason: 'legacy default', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.repeat, + ), + query, + ), + ); + }, + ); + }, + ); + + group( + 'mapToQuery maps with brackets and nested lists', + () { + , String>{ + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'Kai doe', + }, + } + } + }: 'filters%5B%24or%5D%5B%5D%5Bdate%5D%5B%24eq%5D=2020-01-01&filters%5B%24or%5D%5B%5D%5Bdate%5D%5B%24eq%5D=2020-01-02&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=Kai%20doe', + { + 'filters': { + 'id': { + r'$in': [3, 6, 8], + }, + } + }: 'filters%5Bid%5D%5B%24in%5D%5B%5D=3&filters%5Bid%5D%5B%24in%5D%5B%5D=6&filters%5Bid%5D%5B%24in%5D%5B%5D=8' + }.forEach( + (map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + useBrackets: true, + ), + query, + reason: 'legacy brackets', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.brackets, + ), + query, + ), + ); + }, + ); + }, + ); + + group( + 'mapToQuery maps with comma and nested lists', + () { + , String>{ + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'Kai doe', + }, + } + } + }: 'filters%5B%24or%5D=%7Bdate%3A%20%7B%24eq%3A%202020-01-01%7D%7D%2C%7Bdate%3A%20%7B%24eq%3A%202020-01-02%7D%7D&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=Kai%20doe', + { + 'filters': { + 'id': { + r'$in': [3, 6, 8], + }, + } + }: 'filters%5Bid%5D%5B%24in%5D=3%2C6%2C8' + }.forEach( + (map, query) => test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.comma, + includeNullQueryVars: true, + ), + query, + ), + ), + ); + }, + ); + , String>{ + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'Kai doe', + }, + } + } + }: 'filters%5B%24or%5D%5B%5D%5Bdate%5D%5B%24eq%5D=2020-01-01&filters%5B%24or%5D%5B%5D%5Bdate%5D%5B%24eq%5D=2020-01-02&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=Kai%20doe', + { + 'filters': { + 'id': { + r'$in': [3, 6, 8], + }, + } + }: 'filters%5Bid%5D%5B%24in%5D%5B%5D=3&filters%5Bid%5D%5B%24in%5D%5B%5D=6&filters%5Bid%5D%5B%24in%5D%5B%5D=8' + }.forEach( + (map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery(map, useBrackets: true), + query, + reason: 'legacy brackets', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery(map, listFormat: ListFormat.brackets), + query, + ), + ); + }, + ); + }); + + group( + 'mapToQuery maps with repeat (default) with includeNullQueryVars and nested lists', + () { + , String>{ + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + null, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'Kai doe', + }, + } + } + }: r'filters%2E$or%2Edate.$eq=2020-01-01&filters%2E$or=&filters%2E$or%2Edate.$eq=2020-01-02&filters%2Eauthor%2Ename.$eq=Kai%20doe', + { + 'filters': { + 'id': { + r'$in': [3, null, 8], + }, + } + }: r'filters%2Eid%2E$in=3&filters%2Eid%2E$in=&filters%2Eid%2E$in=8' + }.forEach( + (map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + includeNullQueryVars: true, + ), + query, + reason: 'legacy default', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.repeat, + includeNullQueryVars: true, + ), + query, + ), + ); + }, + ); + }, + ); + + group( + 'mapToQuery maps with brackets with includeNullQueryVars and nested lists', + () { + , String>{ + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + null, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'Kai doe', + }, + } + } + }: 'filters%5B%24or%5D%5B%5D%5Bdate%5D%5B%24eq%5D=2020-01-01&filters%5B%24or%5D%5B%5D=&filters%5B%24or%5D%5B%5D%5Bdate%5D%5B%24eq%5D=2020-01-02&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=Kai%20doe', + { + 'filters': { + 'id': { + r'$in': [3, null, 8], + }, + } + }: 'filters%5Bid%5D%5B%24in%5D%5B%5D=3&filters%5Bid%5D%5B%24in%5D%5B%5D=&filters%5Bid%5D%5B%24in%5D%5B%5D=8' + }.forEach( + (map, query) { + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + useBrackets: true, + includeNullQueryVars: true, + ), + query, + reason: 'legacy brackets', + ), + ); + + test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.brackets, + includeNullQueryVars: true, + ), + query, + ), + ); + }, + ); + }, + ); + + group( + 'mapToQuery maps with indices with includeNullQueryVars and nested lists', + () { + , String>{ + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + null, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'Kai doe', + }, + } + } + }: 'filters%5B%24or%5D%5B0%5D%5Bdate%5D%5B%24eq%5D=2020-01-01&filters%5B%24or%5D%5B1%5D=&filters%5B%24or%5D%5B2%5D%5Bdate%5D%5B%24eq%5D=2020-01-02&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=Kai%20doe', + { + 'filters': { + 'id': { + r'$in': [3, null, 8], + }, + } + }: 'filters%5Bid%5D%5B%24in%5D%5B0%5D=3&filters%5Bid%5D%5B%24in%5D%5B1%5D=&filters%5Bid%5D%5B%24in%5D%5B2%5D=8' + }.forEach( + (map, query) => test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.indices, + includeNullQueryVars: true, + ), + query, + ), + ), + ); + }, + ); + + group( + 'mapToQuery maps with comma with includeNullQueryVars and nested lists', + () { + , String>{ + { + 'filters': { + r'$or': [ + { + 'date': { + r'$eq': '2020-01-01', + } + }, + null, + { + 'date': { + r'$eq': '2020-01-02', + } + } + ], + 'author': { + 'name': { + r'$eq': 'Kai doe', + }, + } + } + }: 'filters%5B%24or%5D=%7Bdate%3A%20%7B%24eq%3A%202020-01-01%7D%7D%2C%2C%7Bdate%3A%20%7B%24eq%3A%202020-01-02%7D%7D&filters%5Bauthor%5D%5Bname%5D%5B%24eq%5D=Kai%20doe', + { + 'filters': { + 'id': { + r'$in': [3, null, 8], + }, + } + }: 'filters%5Bid%5D%5B%24in%5D=3%2C%2C8' + }.forEach( + (map, query) => test( + '$map -> $query', + () => expect( + mapToQuery( + map, + listFormat: ListFormat.comma, + includeNullQueryVars: true, + ), + query, + ), + ), + ); + + test('mapToQuery maps with enums', () { + final map = { + 'filters': { + 'name': 'foo', + 'example': ExampleEnum.bar, + } + }; + + expect( + mapToQuery(map), + equals('filters.name=foo&filters.example=bar'), + ); + }); + }, + ); + Request createRequest(Map headers) => Request( 'POST', Uri.parse('foo'), diff --git a/chopper_generator/analysis_options.yaml b/chopper_generator/analysis_options.yaml index 4fd79467..6f56a451 100644 --- a/chopper_generator/analysis_options.yaml +++ b/chopper_generator/analysis_options.yaml @@ -3,6 +3,7 @@ include: package:lints/recommended.yaml analyzer: exclude: - "**.g.dart" + - "**.chopper.dart" - "**.mocks.dart" - "example/**" diff --git a/chopper_generator/lib/src/generator.dart b/chopper_generator/lib/src/generator.dart index c3a99766..7c1b4d80 100644 --- a/chopper_generator/lib/src/generator.dart +++ b/chopper_generator/lib/src/generator.dart @@ -12,6 +12,7 @@ import 'package:chopper_generator/src/vars.dart'; import 'package:code_builder/code_builder.dart'; import 'package:dart_style/dart_style.dart'; import 'package:logging/logging.dart'; +import 'package:qs_dart/qs_dart.dart' show ListFormat; import 'package:source_gen/source_gen.dart'; /// Code generator for [chopper.ChopperApi] annotated classes. @@ -403,9 +404,11 @@ final class ChopperGenerator final bool hasTag = tag.isNotEmpty; - final bool useBrackets = Utils.getUseBrackets(method); + final ListFormat? listFormat = Utils.getListFormat(method); - final bool includeNullQueryVars = Utils.getIncludeNullQueryVars(method); + final bool? useBrackets = Utils.getUseBrackets(method); + + final bool? includeNullQueryVars = Utils.getIncludeNullQueryVars(method); blocks.add( declareFinal(Vars.request.toString(), type: refer('Request')) @@ -417,6 +420,8 @@ final class ChopperGenerator useHeaders: headers != null, hasParts: hasParts, tagRefer: hasTag ? refer(tag.keys.first) : null, + listFormat: listFormat, + // ignore: deprecated_member_use_from_same_package useBrackets: useBrackets, includeNullQueryVars: includeNullQueryVars, ), @@ -703,8 +708,9 @@ final class ChopperGenerator bool hasParts = false, bool useQueries = false, bool useHeaders = false, - bool useBrackets = false, - bool includeNullQueryVars = false, + ListFormat? listFormat, + @Deprecated('Use listFormat instead') bool? useBrackets, + bool? includeNullQueryVars, Reference? tagRefer, }) => refer('Request').newInstance( @@ -722,8 +728,10 @@ final class ChopperGenerator if (useQueries) 'parameters': refer(Vars.parameters.toString()), if (useHeaders) 'headers': refer(Vars.headers.toString()), if (tagRefer != null) 'tag': tagRefer, - if (useBrackets) 'useBrackets': literalBool(useBrackets), - if (includeNullQueryVars) + if (listFormat != null) + 'listFormat': refer('ListFormat').type.property(listFormat.name), + if (useBrackets != null) 'useBrackets': literalBool(useBrackets), + if (includeNullQueryVars != null) 'includeNullQueryVars': literalBool(includeNullQueryVars), }, ); diff --git a/chopper_generator/lib/src/utils.dart b/chopper_generator/lib/src/utils.dart index 315238ed..1e5f9915 100644 --- a/chopper_generator/lib/src/utils.dart +++ b/chopper_generator/lib/src/utils.dart @@ -1,6 +1,8 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:chopper_generator/src/extensions.dart'; import 'package:code_builder/code_builder.dart'; +import 'package:collection/collection.dart'; +import 'package:qs_dart/qs_dart.dart' show ListFormat; import 'package:source_gen/source_gen.dart'; final class Utils { @@ -13,11 +15,23 @@ final class Utils { static String getMethodName(ConstantReader method) => method.read('method').stringValue; - static bool getUseBrackets(ConstantReader method) => - method.peek('useBrackets')?.boolValue ?? false; + static ListFormat? getListFormat(ConstantReader method) { + return ListFormat.values.firstWhereOrNull( + (listFormat) => + listFormat.name == + method + .peek('listFormat') + ?.objectValue + .getField('_name') + ?.toStringValue(), + ); + } - static bool getIncludeNullQueryVars(ConstantReader method) => - method.peek('includeNullQueryVars')?.boolValue ?? false; + static bool? getUseBrackets(ConstantReader method) => + method.peek('useBrackets')?.boolValue; + + static bool? getIncludeNullQueryVars(ConstantReader method) => + method.peek('includeNullQueryVars')?.boolValue; /// All positional required params must support nullability static Parameter buildRequiredPositionalParam(ParameterElement p) => diff --git a/chopper_generator/pubspec.yaml b/chopper_generator/pubspec.yaml index 7eb33501..0df81516 100644 --- a/chopper_generator/pubspec.yaml +++ b/chopper_generator/pubspec.yaml @@ -18,6 +18,8 @@ dependencies: meta: ^1.9.1 source_gen: ^1.4.0 yaml: ^3.1.2 + qs_dart: ^1.0.3 + collection: ^1.18.0 dev_dependencies: build_runner: ^2.4.6 diff --git a/chopper_generator/test/test_service.chopper.dart b/chopper_generator/test/test_service.chopper.dart index 773bddf3..ae0052ea 100644 --- a/chopper_generator/test/test_service.chopper.dart +++ b/chopper_generator/test/test_service.chopper.dart @@ -675,6 +675,21 @@ final class _$HttpTestService extends HttpTestService { return client.send($request); } + @override + Future> getUsingListQueryParamWithBracketsLegacy( + List value) { + final Uri $url = Uri.parse('/test/list_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + return client.send($request); + } + @override Future> getUsingListQueryParamWithBrackets( List value) { @@ -685,7 +700,51 @@ final class _$HttpTestService extends HttpTestService { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithIndices( + List value) { + final Uri $url = Uri.parse('/test/list_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithRepeat( + List value) { + final Uri $url = Uri.parse('/test/list_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithComma(List value) { + final Uri $url = Uri.parse('/test/list_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); return client.send($request); } @@ -718,6 +777,21 @@ final class _$HttpTestService extends HttpTestService { return client.send($request); } + @override + Future> getUsingMapQueryParamWithBracketsLegacy( + Map value) { + final Uri $url = Uri.parse('/test/map_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + return client.send($request); + } + @override Future> getUsingMapQueryParamWithBrackets( Map value) { @@ -728,7 +802,52 @@ final class _$HttpTestService extends HttpTestService { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithIndices( + Map value) { + final Uri $url = Uri.parse('/test/map_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithRepeat( + Map value) { + final Uri $url = Uri.parse('/test/map_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithComma( + Map value) { + final Uri $url = Uri.parse('/test/map_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); return client.send($request); } diff --git a/chopper_generator/test/test_service.dart b/chopper_generator/test/test_service.dart index 7f818dae..9ee8b877 100644 --- a/chopper_generator/test/test_service.dart +++ b/chopper_generator/test/test_service.dart @@ -193,11 +193,31 @@ abstract class HttpTestService extends ChopperService { @Query('value') List value, ); - @Get(path: '/list_query_param_with_brackets', useBrackets: true) + @Get(path: '/list_query_param_with_brackets_legacy', useBrackets: true) + Future> getUsingListQueryParamWithBracketsLegacy( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_brackets', listFormat: ListFormat.brackets) Future> getUsingListQueryParamWithBrackets( @Query('value') List value, ); + @Get(path: '/list_query_param_with_indices', listFormat: ListFormat.indices) + Future> getUsingListQueryParamWithIndices( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_repeat', listFormat: ListFormat.repeat) + Future> getUsingListQueryParamWithRepeat( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_comma', listFormat: ListFormat.comma) + Future> getUsingListQueryParamWithComma( + @Query('value') List value, + ); + @Get(path: '/map_query_param') Future> getUsingMapQueryParam( @Query('value') Map value, @@ -211,11 +231,31 @@ abstract class HttpTestService extends ChopperService { @Query('value') Map value, ); - @Get(path: '/map_query_param_with_brackets', useBrackets: true) + @Get(path: '/map_query_param_with_brackets_legacy', useBrackets: true) + Future> getUsingMapQueryParamWithBracketsLegacy( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_brackets', listFormat: ListFormat.brackets) Future> getUsingMapQueryParamWithBrackets( @Query('value') Map value, ); + @Get(path: '/map_query_param_with_indices', listFormat: ListFormat.indices) + Future> getUsingMapQueryParamWithIndices( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_repeat', listFormat: ListFormat.repeat) + Future> getUsingMapQueryParamWithRepeat( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_comma', listFormat: ListFormat.comma) + Future> getUsingMapQueryParamWithComma( + @Query('value') Map value, + ); + @Get(path: 'headers') Future> getHeaders({ @Header('x-string') required String stringHeader, diff --git a/chopper_generator/test/test_service_variable.chopper.dart b/chopper_generator/test/test_service_variable.chopper.dart index 9c69ffa0..c60e766e 100644 --- a/chopper_generator/test/test_service_variable.chopper.dart +++ b/chopper_generator/test/test_service_variable.chopper.dart @@ -581,6 +581,22 @@ final class _$HttpTestServiceVariable extends HttpTestServiceVariable { return client.send($request); } + @override + Future> getUsingListQueryParamWithBracketsLegacy( + List value) { + final Uri $url = + Uri.parse('${service}/list_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + return client.send($request); + } + @override Future> getUsingListQueryParamWithBrackets( List value) { @@ -591,7 +607,51 @@ final class _$HttpTestServiceVariable extends HttpTestServiceVariable { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithIndices( + List value) { + final Uri $url = Uri.parse('${service}/list_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithRepeat( + List value) { + final Uri $url = Uri.parse('${service}/list_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + return client.send($request); + } + + @override + Future> getUsingListQueryParamWithComma(List value) { + final Uri $url = Uri.parse('${service}/list_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); return client.send($request); } @@ -625,6 +685,22 @@ final class _$HttpTestServiceVariable extends HttpTestServiceVariable { return client.send($request); } + @override + Future> getUsingMapQueryParamWithBracketsLegacy( + Map value) { + final Uri $url = + Uri.parse('${service}/map_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + return client.send($request); + } + @override Future> getUsingMapQueryParamWithBrackets( Map value) { @@ -635,7 +711,52 @@ final class _$HttpTestServiceVariable extends HttpTestServiceVariable { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithIndices( + Map value) { + final Uri $url = Uri.parse('${service}/map_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithRepeat( + Map value) { + final Uri $url = Uri.parse('${service}/map_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + return client.send($request); + } + + @override + Future> getUsingMapQueryParamWithComma( + Map value) { + final Uri $url = Uri.parse('${service}/map_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); return client.send($request); } diff --git a/chopper_generator/test/test_service_variable.dart b/chopper_generator/test/test_service_variable.dart index 251b48cb..c878ada8 100644 --- a/chopper_generator/test/test_service_variable.dart +++ b/chopper_generator/test/test_service_variable.dart @@ -168,11 +168,31 @@ abstract class HttpTestServiceVariable extends ChopperService { @Query('value') List value, ); - @Get(path: '/list_query_param_with_brackets', useBrackets: true) + @Get(path: '/list_query_param_with_brackets_legacy', useBrackets: true) + Future> getUsingListQueryParamWithBracketsLegacy( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_brackets', listFormat: ListFormat.brackets) Future> getUsingListQueryParamWithBrackets( @Query('value') List value, ); + @Get(path: '/list_query_param_with_indices', listFormat: ListFormat.indices) + Future> getUsingListQueryParamWithIndices( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_repeat', listFormat: ListFormat.repeat) + Future> getUsingListQueryParamWithRepeat( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_comma', listFormat: ListFormat.comma) + Future> getUsingListQueryParamWithComma( + @Query('value') List value, + ); + @Get(path: '/map_query_param') Future> getUsingMapQueryParam( @Query('value') Map value, @@ -186,10 +206,30 @@ abstract class HttpTestServiceVariable extends ChopperService { @Query('value') Map value, ); - @Get(path: '/map_query_param_with_brackets', useBrackets: true) + @Get(path: '/map_query_param_with_brackets_legacy', useBrackets: true) + Future> getUsingMapQueryParamWithBracketsLegacy( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_brackets', listFormat: ListFormat.brackets) Future> getUsingMapQueryParamWithBrackets( @Query('value') Map value, ); + + @Get(path: '/map_query_param_with_indices', listFormat: ListFormat.indices) + Future> getUsingMapQueryParamWithIndices( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_repeat', listFormat: ListFormat.repeat) + Future> getUsingMapQueryParamWithRepeat( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_comma', listFormat: ListFormat.comma) + Future> getUsingMapQueryParamWithComma( + @Query('value') Map value, + ); } Request customConvertRequest(Request req) { diff --git a/chopper_generator/test/test_without_response_service.chopper.dart b/chopper_generator/test/test_without_response_service.chopper.dart index c7b8462a..2bc8fcbd 100644 --- a/chopper_generator/test/test_without_response_service.chopper.dart +++ b/chopper_generator/test/test_without_response_service.chopper.dart @@ -616,6 +616,22 @@ final class _$HttpTestService extends HttpTestService { return $response.bodyOrThrow; } + @override + Future getUsingListQueryParamWithBracketsLegacy( + List value) async { + final Uri $url = Uri.parse('/test/list_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + @override Future getUsingListQueryParamWithBrackets(List value) async { final Uri $url = Uri.parse('/test/list_query_param_with_brackets'); @@ -625,7 +641,52 @@ final class _$HttpTestService extends HttpTestService { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingListQueryParamWithIndices(List value) async { + final Uri $url = Uri.parse('/test/list_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingListQueryParamWithRepeat(List value) async { + final Uri $url = Uri.parse('/test/list_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingListQueryParamWithComma(List value) async { + final Uri $url = Uri.parse('/test/list_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); final Response $response = await client.send($request); return $response.bodyOrThrow; @@ -661,6 +722,22 @@ final class _$HttpTestService extends HttpTestService { return $response.bodyOrThrow; } + @override + Future getUsingMapQueryParamWithBracketsLegacy( + Map value) async { + final Uri $url = Uri.parse('/test/map_query_param_with_brackets_legacy'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + useBrackets: true, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + @override Future getUsingMapQueryParamWithBrackets( Map value) async { @@ -671,7 +748,55 @@ final class _$HttpTestService extends HttpTestService { $url, client.baseUrl, parameters: $params, - useBrackets: true, + listFormat: ListFormat.brackets, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingMapQueryParamWithIndices( + Map value) async { + final Uri $url = Uri.parse('/test/map_query_param_with_indices'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.indices, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingMapQueryParamWithRepeat( + Map value) async { + final Uri $url = Uri.parse('/test/map_query_param_with_repeat'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.repeat, + ); + final Response $response = await client.send($request); + return $response.bodyOrThrow; + } + + @override + Future getUsingMapQueryParamWithComma( + Map value) async { + final Uri $url = Uri.parse('/test/map_query_param_with_comma'); + final Map $params = {'value': value}; + final Request $request = Request( + 'GET', + $url, + client.baseUrl, + parameters: $params, + listFormat: ListFormat.comma, ); final Response $response = await client.send($request); return $response.bodyOrThrow; diff --git a/chopper_generator/test/test_without_response_service.dart b/chopper_generator/test/test_without_response_service.dart index d8a95417..d29c0eb9 100644 --- a/chopper_generator/test/test_without_response_service.dart +++ b/chopper_generator/test/test_without_response_service.dart @@ -166,11 +166,31 @@ abstract class HttpTestService extends ChopperService { @Query('value') List value, ); - @Get(path: '/list_query_param_with_brackets', useBrackets: true) + @Get(path: '/list_query_param_with_brackets_legacy', useBrackets: true) + Future getUsingListQueryParamWithBracketsLegacy( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_brackets', listFormat: ListFormat.brackets) Future getUsingListQueryParamWithBrackets( @Query('value') List value, ); + @Get(path: '/list_query_param_with_indices', listFormat: ListFormat.indices) + Future getUsingListQueryParamWithIndices( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_repeat', listFormat: ListFormat.repeat) + Future getUsingListQueryParamWithRepeat( + @Query('value') List value, + ); + + @Get(path: '/list_query_param_with_comma', listFormat: ListFormat.comma) + Future getUsingListQueryParamWithComma( + @Query('value') List value, + ); + @Get(path: '/map_query_param') Future getUsingMapQueryParam( @Query('value') Map value, @@ -184,11 +204,31 @@ abstract class HttpTestService extends ChopperService { @Query('value') Map value, ); - @Get(path: '/map_query_param_with_brackets', useBrackets: true) + @Get(path: '/map_query_param_with_brackets_legacy', useBrackets: true) + Future getUsingMapQueryParamWithBracketsLegacy( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_brackets', listFormat: ListFormat.brackets) Future getUsingMapQueryParamWithBrackets( @Query('value') Map value, ); + @Get(path: '/map_query_param_with_indices', listFormat: ListFormat.indices) + Future getUsingMapQueryParamWithIndices( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_repeat', listFormat: ListFormat.repeat) + Future getUsingMapQueryParamWithRepeat( + @Query('value') Map value, + ); + + @Get(path: '/map_query_param_with_comma', listFormat: ListFormat.comma) + Future getUsingMapQueryParamWithComma( + @Query('value') Map value, + ); + @Get(path: 'headers') Future getHeaders({ @Header('x-string') required String stringHeader, diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 1e603f80..8ed20e77 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -3,6 +3,7 @@ include: package:lints/recommended.yaml analyzer: exclude: - "**.g.dart" + - "**.chopper.dart" - "**.mocks.dart" linter: