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

mapToQuery changes #364

Merged
merged 13 commits into from
Oct 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions chopper/lib/src/annotations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,21 @@ class Method {
/// Mark the body as optional to suppress warnings during code generation
final bool optionalBody;

/// Use brackets [ ] to when encoding
///
/// - lists
/// 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;

const Method(
this.method, {
this.optionalBody = false,
this.path = '',
this.headers = const {},
this.useBrackets = false,
});
}

Expand All @@ -176,6 +186,7 @@ class Get extends Method {
super.optionalBody = true,
super.path,
super.headers,
super.useBrackets,
}) : super(HttpMethod.Get);
}

Expand All @@ -188,6 +199,7 @@ class Post extends Method {
super.optionalBody,
super.path,
super.headers,
super.useBrackets,
}) : super(HttpMethod.Post);
}

Expand All @@ -198,6 +210,7 @@ class Delete extends Method {
super.optionalBody = true,
super.path,
super.headers,
super.useBrackets,
}) : super(HttpMethod.Delete);
}

Expand All @@ -210,6 +223,7 @@ class Put extends Method {
super.optionalBody,
super.path,
super.headers,
super.useBrackets,
}) : super(HttpMethod.Put);
}

Expand All @@ -221,6 +235,7 @@ class Patch extends Method {
super.optionalBody,
super.path,
super.headers,
super.useBrackets,
}) : super(HttpMethod.Patch);
}

Expand All @@ -231,6 +246,7 @@ class Head extends Method {
super.optionalBody = true,
super.path,
super.headers,
super.useBrackets,
}) : super(HttpMethod.Head);
}

Expand All @@ -240,6 +256,7 @@ class Options extends Method {
super.optionalBody = true,
super.path,
super.headers,
super.useBrackets,
}) : super(HttpMethod.Options);
}

Expand Down
20 changes: 14 additions & 6 deletions chopper/lib/src/request.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:meta/meta.dart';
Expand All @@ -18,6 +17,7 @@ class Request {
final Map<String, dynamic> parameters;
final Map<String, String> headers;
final bool multipart;
final bool useBrackets;

const Request(
this.method,
Expand All @@ -28,6 +28,7 @@ class Request {
this.headers = const {},
this.multipart = false,
this.parts = const [],
this.useBrackets = false,
});

/// Makes a copy of this request, replacing original values with the given ones.
Expand All @@ -37,23 +38,25 @@ class Request {
dynamic body,
Map<String, dynamic>? parameters,
Map<String, String>? headers,
Encoding? encoding,
List<PartValue>? parts,
bool? multipart,
String? baseUrl,
bool? useBrackets,
}) =>
Request(
(method ?? this.method) as String,
url ?? this.url,
baseUrl ?? this.baseUrl,
body: body ?? this.body,
parameters: parameters ?? this.parameters,
headers: headers ?? this.headers,
parts: parts ?? this.parts,
multipart: multipart ?? this.multipart,
baseUrl ?? this.baseUrl,
useBrackets: useBrackets ?? this.useBrackets,
);

Uri _buildUri() => buildUri(baseUrl, url, parameters);
Uri _buildUri() =>
buildUri(baseUrl, url, parameters, useBrackets: useBrackets);

Map<String, String> _buildHeaders() => {...headers};

Expand Down Expand Up @@ -110,7 +113,12 @@ class PartValueFile<T> extends PartValue<T> {
/// Builds a valid URI from [baseUrl], [url] and [parameters].
///
/// If [url] starts with 'http://' or 'https://', baseUrl is ignored.
Uri buildUri(String baseUrl, String url, Map<String, dynamic> parameters) {
Uri buildUri(
String baseUrl,
String url,
Map<String, dynamic> parameters, {
bool useBrackets = false,
}) {
// If the request's url is already a fully qualified URL, we can use it
// as-is and ignore the baseUrl.
Uri uri = url.startsWith('http://') || url.startsWith('https://')
Expand All @@ -119,7 +127,7 @@ Uri buildUri(String baseUrl, String url, Map<String, dynamic> parameters) {
? Uri.parse('$baseUrl/$url')
: Uri.parse('$baseUrl$url');

String query = mapToQuery(parameters);
String query = mapToQuery(parameters, useBrackets: useBrackets);
if (query.isNotEmpty) {
if (uri.hasQuery) {
query += '&${uri.query}';
Expand Down
56 changes: 40 additions & 16 deletions chopper/lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,29 +56,39 @@ final chopperLogger = Logger('Chopper');
/// Creates a valid URI query string from [map].
///
/// E.g., `{'foo': 'bar', 'ints': [ 1337, 42 ] }` will become 'foo=bar&ints=1337&ints=42'.
String mapToQuery(Map<String, dynamic> map) => _mapToQuery(map).join('&');
String mapToQuery(Map<String, dynamic> map, {bool useBrackets = false}) =>
_mapToQuery(map, useBrackets: useBrackets).join('&');

Iterable<_Pair<String, String>> _mapToQuery(
Map<String, dynamic> map, {
String? prefix,
bool useBrackets = false,
}) {
final Set<_Pair<String, String>> pairs = {};

map.forEach((key, value) {
if (value != null) {
String name = Uri.encodeQueryComponent(key);
String name = Uri.encodeQueryComponent(key);

if (prefix != null) {
name = '$prefix.$name';
}
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));
pairs.addAll(_iterableToQuery(name, value, useBrackets: useBrackets));
} else if (value is Map<String, dynamic>) {
pairs.addAll(_mapToQuery(value, prefix: name));
} else if (value.toString().isNotEmpty) {
pairs.add(_Pair<String, String>(name, _normalizeValue(value)));
pairs.addAll(
_mapToQuery(value, prefix: name, useBrackets: useBrackets),
);
} else {
pairs.add(
_Pair<String, String>(name, _normalizeValue(value)),
);
}
} else {
pairs.add(_Pair<String, String>(name, ''));
}
});

Expand All @@ -87,20 +97,34 @@ Iterable<_Pair<String, String>> _mapToQuery(

Iterable<_Pair<String, String>> _iterableToQuery(
String name,
Iterable values,
) =>
values.map((v) => _Pair(name, _normalizeValue(v)));
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.toString());
String _normalizeValue(value) => Uri.encodeComponent(value?.toString() ?? '');

class _Pair<A, B> {
final A first;
final B second;
final bool useBrackets;

const _Pair(this.first, this.second);
const _Pair(
this.first,
this.second, {
this.useBrackets = false,
});

@override
String toString() => '$first=$second';
String toString() => useBrackets
? '$first${Uri.encodeQueryComponent('[]')}=$second'
: '$first=$second';
}

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