Skip to content

Commit

Permalink
Fix nullable QueryMap fails to compile (#344)
Browse files Browse the repository at this point in the history
  • Loading branch information
techouse authored Jul 14, 2022
1 parent 659b9f8 commit e167ba6
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 9 deletions.
4 changes: 2 additions & 2 deletions chopper/example/definition.chopper.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

159 changes: 159 additions & 0 deletions chopper/test/base_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,165 @@ void main() {
});
});

test('Query Map 3', () async {
final httpClient = MockClient((request) async {
expect(
request.url.toString(),
equals('$baseUrl/test/query_map?name=foo&number=1234'),
);
expect(request.method, equals('GET'));

return http.Response('get response', 200);
});

final chopper = buildClient(httpClient);
final service = chopper.getService<HttpTestService>();

final response = await service.getQueryMapTest3(
name: 'foo',
number: 1234,
);

expect(response.body, equals('get response'));
expect(response.statusCode, equals(200));

httpClient.close();
});

test('Query Map 4 without QueryMap', () async {
final httpClient = MockClient((request) async {
expect(
request.url.toString(),
equals('$baseUrl/test/query_map?name=foo&number=1234'),
);
expect(request.method, equals('GET'));

return http.Response('get response', 200);
});

final chopper = buildClient(httpClient);
final service = chopper.getService<HttpTestService>();

final response = await service.getQueryMapTest4(
name: 'foo',
number: 1234,
);

expect(response.body, equals('get response'));
expect(response.statusCode, equals(200));

httpClient.close();
});

test('Query Map 4 with QueryMap', () async {
final httpClient = MockClient((request) async {
expect(
request.url.toString(),
equals(
'$baseUrl/test/query_map?name=foo&number=1234&filter_1=filter_value_1',
),
);
expect(request.method, equals('GET'));

return http.Response('get response', 200);
});

final chopper = buildClient(httpClient);
final service = chopper.getService<HttpTestService>();

final response = await service.getQueryMapTest4(
name: 'foo',
number: 1234,
filters: {
'filter_1': 'filter_value_1',
},
);

expect(response.body, equals('get response'));
expect(response.statusCode, equals(200));

httpClient.close();
});

test(
'Query Map 4 with QueryMap that overwrites a previous value from Query',
() async {
final httpClient = MockClient((request) async {
expect(
request.url.toString(),
equals('$baseUrl/test/query_map?name=bar&number=1234'),
);
expect(request.method, equals('GET'));

return http.Response('get response', 200);
});

final chopper = buildClient(httpClient);
final service = chopper.getService<HttpTestService>();

final response = await service.getQueryMapTest4(
name: 'foo',
number: 1234,
filters: {
'name': 'bar',
},
);

expect(response.body, equals('get response'));
expect(response.statusCode, equals(200));

httpClient.close();
},
);

test('Query Map 5 without QueryMap', () async {
final httpClient = MockClient((request) async {
expect(
request.url.toString(),
equals('$baseUrl/test/query_map'),
);
expect(request.method, equals('GET'));

return http.Response('get response', 200);
});

final chopper = buildClient(httpClient);
final service = chopper.getService<HttpTestService>();

final response = await service.getQueryMapTest5();

expect(response.body, equals('get response'));
expect(response.statusCode, equals(200));

httpClient.close();
});

test('Query Map 5 with QueryMap', () async {
final httpClient = MockClient((request) async {
expect(
request.url.toString(),
equals('$baseUrl/test/query_map?filter_1=filter_value_1'),
);
expect(request.method, equals('GET'));

return http.Response('get response', 200);
});

final chopper = buildClient(httpClient);
final service = chopper.getService<HttpTestService>();

final response = await service.getQueryMapTest5(
filters: {
'filter_1': 'filter_value_1',
},
);

expect(response.body, equals('get response'));
expect(response.statusCode, equals(200));

httpClient.close();
});

test('onRequest Stream', () async {
final client = MockClient((http.Request req) async {
return http.Response('ok', 200);
Expand Down
40 changes: 35 additions & 5 deletions chopper/test/test_service.chopper.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions chopper/test/test_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,25 @@ abstract class HttpTestService extends ChopperService {
@Query('test') bool? test,
});

@Get(path: 'query_map')
Future<Response> getQueryMapTest3({
@Query('name') String name = '',
@Query('number') int? number,
@QueryMap() Map<String, dynamic> filters = const {},
});

@Get(path: 'query_map')
Future<Response> getQueryMapTest4({
@Query('name') String name = '',
@Query('number') int? number,
@QueryMap() Map<String, dynamic>? filters,
});

@Get(path: 'query_map')
Future<Response> getQueryMapTest5({
@QueryMap() Map<String, dynamic>? filters,
});

@Get(path: 'get_body')
Future<Response> getBody(@Body() dynamic body);

Expand Down
24 changes: 22 additions & 2 deletions chopper_generator/lib/src/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:built_collection/built_collection.dart';
import 'package:dart_style/dart_style.dart';

import 'package:source_gen/source_gen.dart';

// TODO(lejard_h) Code builder not null safe yet
// ignore: import_of_legacy_library_into_null_safe
import 'package:code_builder/code_builder.dart';
Expand Down Expand Up @@ -171,15 +172,34 @@ class ChopperGenerator extends GeneratorForAnnotation<chopper.ChopperApi> {
blocks.add(_generateMap(queries).assignFinal(_parametersVar).statement);
}

// Build an iterable of all the parameters that are nullable
final optionalNullableParameters = [
...m.parameters.where((p) => p.isOptionalPositional),
...m.parameters.where((p) => p.isNamed),
].where((el) => el.type.isNullable).map((el) => el.name);

final hasQueryMap = queryMap.isNotEmpty;
if (hasQueryMap) {
if (queries.isNotEmpty) {
blocks.add(refer('$_parametersVar.addAll').call(
[refer(queryMap.keys.first)],
[
// Check if the parameter is nullable
optionalNullableParameters.contains(queryMap.keys.first)
? refer(queryMap.keys.first).ifNullThen(refer('{}'))
: refer(queryMap.keys.first),
],
).statement);
} else {
blocks.add(
refer(queryMap.keys.first).assignFinal(_parametersVar).statement,
// Check if the parameter is nullable
optionalNullableParameters.contains(queryMap.keys.first)
? refer(queryMap.keys.first)
.ifNullThen(refer('{}'))
.assignFinal(_parametersVar)
.statement
: refer(queryMap.keys.first)
.assignFinal(_parametersVar)
.statement,
);
}
}
Expand Down

0 comments on commit e167ba6

Please sign in to comment.