Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

⚡ use qs_dart for query string encoding #592

Merged
merged 12 commits into from
Apr 4, 2024
101 changes: 11 additions & 90 deletions chopper/lib/src/utils.dart
Original file line number Diff line number Diff line change
@@ -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';

/// Creates a new [Request] by copying [request] and adding a header with the
/// provided key [name] and value [value] to the result.
Expand Down Expand Up @@ -66,98 +66,19 @@ String mapToQuery(
bool useBrackets = false,
bool includeNullQueryVars = false,
}) =>
_mapToQuery(
QS.encode(
map,
useBrackets: useBrackets,
includeNullQueryVars: includeNullQueryVars,
).join('&');

Iterable<_Pair<String, String>> _mapToQuery(
Map<String, dynamic> map, {
String? prefix,
bool useBrackets = false,
bool includeNullQueryVars = false,
}) {
final Set<_Pair<String, String>> 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<String, dynamic>) {
pairs.addAll(
_mapToQuery(
value,
prefix: name,
useBrackets: useBrackets,
includeNullQueryVars: includeNullQueryVars,
),
);
} else {
pairs.add(
_Pair<String, String>(name, _normalizeValue(value)),
);
}
} else {
if (includeNullQueryVars) {
pairs.add(_Pair<String, String>(name, ''));
}
}
});

return pairs;
}

Iterable<_Pair<String, String>> _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() ?? '',
EncodeOptions(
listFormat: useBrackets ? ListFormat.brackets : ListFormat.repeat,
techouse marked this conversation as resolved.
Show resolved Hide resolved
allowDots: !useBrackets,
encodeDotInKeys: !useBrackets,
encodeValuesOnly: !useBrackets,
skipNulls: !includeNullQueryVars,
strictNullHandling: false,
serializeDate: (DateTime date) => date.toUtc().toIso8601String(),
),
);

final class _Pair<A, B> 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<Object?> get props => [
first,
second,
];
}

bool isTypeOf<ThisType, OfType>() => _Instance<ThisType>() is _Instance<OfType>;

final class _Instance<T> {}
3 changes: 2 additions & 1 deletion chopper/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies:
http: ^1.1.0
logging: ^1.2.0
meta: ^1.9.1
qs_dart: ^1.0.1

dev_dependencies:
build_runner: ^2.4.6
Expand All @@ -35,4 +36,4 @@ topics:
- api
- client
- http
- rest
- rest
4 changes: 2 additions & 2 deletions chopper/test/base_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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'));

Expand All @@ -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'));

Expand Down
Loading
Loading