diff --git a/modules/openapi-generator/src/main/resources/dart-dio/api.mustache b/modules/openapi-generator/src/main/resources/dart-dio/api.mustache index c82e4582e9e..11ece09eaca 100644 --- a/modules/openapi-generator/src/main/resources/dart-dio/api.mustache +++ b/modules/openapi-generator/src/main/resources/dart-dio/api.mustache @@ -77,7 +77,7 @@ class {{classname}} { _bodyData = {{#isMultipart}}FormData.fromMap({{/isMultipart}}{ {{#formParams}} - {{^required}}{{^nullable}}if ({{{paramName}}} != null) {{/nullable}}{{/required}}r'{{{baseName}}}': {{#isFile}}MultipartFile.fromBytes({{{paramName}}}, filename: r'{{{baseName}}}'){{/isFile}}{{^isFile}}parameterToString(_serializers, {{{paramName}}}){{/isFile}}, + {{^required}}{{^nullable}}if ({{{paramName}}} != null) {{/nullable}}{{/required}}r'{{{baseName}}}': {{#isFile}}MultipartFile.fromBytes({{{paramName}}}, filename: r'{{{baseName}}}'){{/isFile}}{{^isFile}}encodeFormParameter(_serializers, {{{paramName}}}, const FullType({{^isContainer}}{{{baseType}}}{{/isContainer}}{{#isContainer}}Built{{#isMap}}Map{{/isMap}}{{#isArray}}{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}{{/isArray}}, [{{#isMap}}FullType(String), {{/isMap}}FullType({{{baseType}}})]{{/isContainer}})){{/isFile}}, {{/formParams}} }{{#isMultipart}}){{/isMultipart}}; {{/hasFormParams}} diff --git a/modules/openapi-generator/src/main/resources/dart-dio/api_util.mustache b/modules/openapi-generator/src/main/resources/dart-dio/api_util.mustache index f3a39d4e80a..12cd528d2d7 100644 --- a/modules/openapi-generator/src/main/resources/dart-dio/api_util.mustache +++ b/modules/openapi-generator/src/main/resources/dart-dio/api_util.mustache @@ -1,15 +1,25 @@ {{>header}} import 'dart:convert'; +import 'package:built_collection/built_collection.dart'; import 'package:built_value/serializer.dart'; -/// Format the given parameter object into string. -String parameterToString(Serializers serializers, dynamic value) { - if (value == null) { - return ''; - } else if (value is String || value is num) { - return value.toString(); - } else { - return json.encode(serializers.serialize(value)); - } -} \ No newline at end of file +/// Format the given form parameter object into something that Dio can handle. +/// Returns primitive or String. +/// Returns List/Map if the value is BuildList/BuiltMap. +dynamic encodeFormParameter(Serializers serializers, dynamic value, FullType type) { + if (value == null) { + return ''; + } + if (value is String || value is num || value is bool) { + return value; + } + final serialized = serializers.serialize(value, specifiedType: type); + if (serialized is String) { + return serialized; + } + if (value is BuiltList || value is BuiltMap) { + return serialized; + } + return json.encode(serialized); +} diff --git a/samples/client/petstore/dart-dio/petstore_client_lib/lib/api/pet_api.dart b/samples/client/petstore/dart-dio/petstore_client_lib/lib/api/pet_api.dart index 31ad6c101bb..3baaf489520 100644 --- a/samples/client/petstore/dart-dio/petstore_client_lib/lib/api/pet_api.dart +++ b/samples/client/petstore/dart-dio/petstore_client_lib/lib/api/pet_api.dart @@ -419,8 +419,8 @@ class PetApi { dynamic _bodyData; _bodyData = { - if (name != null) r'name': parameterToString(_serializers, name), - if (status != null) r'status': parameterToString(_serializers, status), + if (name != null) r'name': encodeFormParameter(_serializers, name, const FullType(String)), + if (status != null) r'status': encodeFormParameter(_serializers, status, const FullType(String)), }; final _response = await _dio.request( @@ -475,7 +475,7 @@ class PetApi { dynamic _bodyData; _bodyData = FormData.fromMap({ - if (additionalMetadata != null) r'additionalMetadata': parameterToString(_serializers, additionalMetadata), + if (additionalMetadata != null) r'additionalMetadata': encodeFormParameter(_serializers, additionalMetadata, const FullType(String)), if (file != null) r'file': MultipartFile.fromBytes(file, filename: r'file'), }); diff --git a/samples/client/petstore/dart-dio/petstore_client_lib/lib/api_util.dart b/samples/client/petstore/dart-dio/petstore_client_lib/lib/api_util.dart index 150ffb3f9bb..a6ce78558cf 100644 --- a/samples/client/petstore/dart-dio/petstore_client_lib/lib/api_util.dart +++ b/samples/client/petstore/dart-dio/petstore_client_lib/lib/api_util.dart @@ -7,15 +7,25 @@ import 'dart:convert'; +import 'package:built_collection/built_collection.dart'; import 'package:built_value/serializer.dart'; -/// Format the given parameter object into string. -String parameterToString(Serializers serializers, dynamic value) { - if (value == null) { - return ''; - } else if (value is String || value is num) { - return value.toString(); - } else { - return json.encode(serializers.serialize(value)); - } -} \ No newline at end of file +/// Format the given form parameter object into something that Dio can handle. +/// Returns primitive or String. +/// Returns List/Map if the value is BuildList/BuiltMap. +dynamic encodeFormParameter(Serializers serializers, dynamic value, FullType type) { + if (value == null) { + return ''; + } + if (value is String || value is num || value is bool) { + return value; + } + final serialized = serializers.serialize(value, specifiedType: type); + if (serialized is String) { + return serialized; + } + if (value is BuiltList || value is BuiltMap) { + return serialized; + } + return json.encode(serialized); +} diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib/lib/api/pet_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib/lib/api/pet_api.dart index 13e809798f7..ff33d7ca5d8 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib/lib/api/pet_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib/lib/api/pet_api.dart @@ -449,8 +449,8 @@ class PetApi { dynamic _bodyData; _bodyData = { - if (name != null) r'name': parameterToString(_serializers, name), - if (status != null) r'status': parameterToString(_serializers, status), + if (name != null) r'name': encodeFormParameter(_serializers, name, const FullType(String)), + if (status != null) r'status': encodeFormParameter(_serializers, status, const FullType(String)), }; final _response = await _dio.request( @@ -505,7 +505,7 @@ class PetApi { dynamic _bodyData; _bodyData = FormData.fromMap({ - if (additionalMetadata != null) r'additionalMetadata': parameterToString(_serializers, additionalMetadata), + if (additionalMetadata != null) r'additionalMetadata': encodeFormParameter(_serializers, additionalMetadata, const FullType(String)), if (file != null) r'file': MultipartFile.fromBytes(file, filename: r'file'), }); diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib/lib/api_util.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib/lib/api_util.dart index 150ffb3f9bb..a6ce78558cf 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib/lib/api_util.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib/lib/api_util.dart @@ -7,15 +7,25 @@ import 'dart:convert'; +import 'package:built_collection/built_collection.dart'; import 'package:built_value/serializer.dart'; -/// Format the given parameter object into string. -String parameterToString(Serializers serializers, dynamic value) { - if (value == null) { - return ''; - } else if (value is String || value is num) { - return value.toString(); - } else { - return json.encode(serializers.serialize(value)); - } -} \ No newline at end of file +/// Format the given form parameter object into something that Dio can handle. +/// Returns primitive or String. +/// Returns List/Map if the value is BuildList/BuiltMap. +dynamic encodeFormParameter(Serializers serializers, dynamic value, FullType type) { + if (value == null) { + return ''; + } + if (value is String || value is num || value is bool) { + return value; + } + final serialized = serializers.serialize(value, specifiedType: type); + if (serialized is String) { + return serialized; + } + if (value is BuiltList || value is BuiltMap) { + return serialized; + } + return json.encode(serialized); +} diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api/fake_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api/fake_api.dart index 59106fd810e..61849d6e4e0 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api/fake_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api/fake_api.dart @@ -587,20 +587,20 @@ class FakeApi { dynamic _bodyData; _bodyData = { - if (integer != null) r'integer': parameterToString(_serializers, integer), - if (int32 != null) r'int32': parameterToString(_serializers, int32), - if (int64 != null) r'int64': parameterToString(_serializers, int64), - r'number': parameterToString(_serializers, number), - if (float != null) r'float': parameterToString(_serializers, float), - r'double': parameterToString(_serializers, double_), - if (string != null) r'string': parameterToString(_serializers, string), - r'pattern_without_delimiter': parameterToString(_serializers, patternWithoutDelimiter), - r'byte': parameterToString(_serializers, byte), + if (integer != null) r'integer': encodeFormParameter(_serializers, integer, const FullType(int)), + if (int32 != null) r'int32': encodeFormParameter(_serializers, int32, const FullType(int)), + if (int64 != null) r'int64': encodeFormParameter(_serializers, int64, const FullType(int)), + r'number': encodeFormParameter(_serializers, number, const FullType(num)), + if (float != null) r'float': encodeFormParameter(_serializers, float, const FullType(double)), + r'double': encodeFormParameter(_serializers, double_, const FullType(double)), + if (string != null) r'string': encodeFormParameter(_serializers, string, const FullType(String)), + r'pattern_without_delimiter': encodeFormParameter(_serializers, patternWithoutDelimiter, const FullType(String)), + r'byte': encodeFormParameter(_serializers, byte, const FullType(String)), if (binary != null) r'binary': MultipartFile.fromBytes(binary, filename: r'binary'), - if (date != null) r'date': parameterToString(_serializers, date), - if (dateTime != null) r'dateTime': parameterToString(_serializers, dateTime), - if (password != null) r'password': parameterToString(_serializers, password), - if (callback != null) r'callback': parameterToString(_serializers, callback), + if (date != null) r'date': encodeFormParameter(_serializers, date, const FullType(DateTime)), + if (dateTime != null) r'dateTime': encodeFormParameter(_serializers, dateTime, const FullType(DateTime)), + if (password != null) r'password': encodeFormParameter(_serializers, password, const FullType(String)), + if (callback != null) r'callback': encodeFormParameter(_serializers, callback, const FullType(String)), }; final _response = await _dio.request( @@ -661,8 +661,8 @@ class FakeApi { dynamic _bodyData; _bodyData = { - if (enumFormStringArray != null) r'enum_form_string_array': parameterToString(_serializers, enumFormStringArray), - if (enumFormString != null) r'enum_form_string': parameterToString(_serializers, enumFormString), + if (enumFormStringArray != null) r'enum_form_string_array': encodeFormParameter(_serializers, enumFormStringArray, const FullType(BuiltList, [FullType(String)])), + if (enumFormString != null) r'enum_form_string': encodeFormParameter(_serializers, enumFormString, const FullType(String)), }; final _response = await _dio.request( @@ -818,8 +818,8 @@ class FakeApi { dynamic _bodyData; _bodyData = { - r'param': parameterToString(_serializers, param), - r'param2': parameterToString(_serializers, param2), + r'param': encodeFormParameter(_serializers, param, const FullType(String)), + r'param2': encodeFormParameter(_serializers, param2, const FullType(String)), }; final _response = await _dio.request( diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api/pet_api.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api/pet_api.dart index fd5bb342a49..857a07bf209 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api/pet_api.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api/pet_api.dart @@ -419,8 +419,8 @@ class PetApi { dynamic _bodyData; _bodyData = { - if (name != null) r'name': parameterToString(_serializers, name), - if (status != null) r'status': parameterToString(_serializers, status), + if (name != null) r'name': encodeFormParameter(_serializers, name, const FullType(String)), + if (status != null) r'status': encodeFormParameter(_serializers, status, const FullType(String)), }; final _response = await _dio.request( @@ -475,7 +475,7 @@ class PetApi { dynamic _bodyData; _bodyData = FormData.fromMap({ - if (additionalMetadata != null) r'additionalMetadata': parameterToString(_serializers, additionalMetadata), + if (additionalMetadata != null) r'additionalMetadata': encodeFormParameter(_serializers, additionalMetadata, const FullType(String)), if (file != null) r'file': MultipartFile.fromBytes(file, filename: r'file'), }); @@ -546,7 +546,7 @@ class PetApi { dynamic _bodyData; _bodyData = FormData.fromMap({ - if (additionalMetadata != null) r'additionalMetadata': parameterToString(_serializers, additionalMetadata), + if (additionalMetadata != null) r'additionalMetadata': encodeFormParameter(_serializers, additionalMetadata, const FullType(String)), r'requiredFile': MultipartFile.fromBytes(requiredFile, filename: r'requiredFile'), }); diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api_util.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api_util.dart index 150ffb3f9bb..a6ce78558cf 100644 --- a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api_util.dart +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake/lib/api_util.dart @@ -7,15 +7,25 @@ import 'dart:convert'; +import 'package:built_collection/built_collection.dart'; import 'package:built_value/serializer.dart'; -/// Format the given parameter object into string. -String parameterToString(Serializers serializers, dynamic value) { - if (value == null) { - return ''; - } else if (value is String || value is num) { - return value.toString(); - } else { - return json.encode(serializers.serialize(value)); - } -} \ No newline at end of file +/// Format the given form parameter object into something that Dio can handle. +/// Returns primitive or String. +/// Returns List/Map if the value is BuildList/BuiltMap. +dynamic encodeFormParameter(Serializers serializers, dynamic value, FullType type) { + if (value == null) { + return ''; + } + if (value is String || value is num || value is bool) { + return value; + } + final serialized = serializers.serialize(value, specifiedType: type); + if (serialized is String) { + return serialized; + } + if (value is BuiltList || value is BuiltMap) { + return serialized; + } + return json.encode(serialized); +} diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake_tests/test/api/fake_api_test.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake_tests/test/api/fake_api_test.dart new file mode 100644 index 00000000000..c634c386e92 --- /dev/null +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake_tests/test/api/fake_api_test.dart @@ -0,0 +1,119 @@ +import 'dart:typed_data'; + +import 'package:built_collection/built_collection.dart'; +import 'package:dio/dio.dart'; +import 'package:http_mock_adapter/http_mock_adapter.dart'; +import 'package:openapi/api.dart'; +import 'package:openapi/api/fake_api.dart'; +import 'package:test/test.dart'; + +void main() { + Openapi client; + DioAdapter server; + + setUp(() { + server = DioAdapter(); + client = Openapi(dio: Dio()..httpClientAdapter = server); + }); + + tearDown(() { + server.close(); + }); + + group(FakeApi, () { + group('testEndpointParameters', () { + test('complete', () async { + server.onPost( + '/fake', + data: { + 'number': '3', + 'double': '-13.57', + 'pattern_without_delimiter': 'patternWithoutDelimiter', + 'byte': '0', + 'float': '1.23', + 'integer': '45', + 'int32': '2147483647', + 'int64': '9223372036854775807', + 'date': '2020-08-11T00:00:00.000Z', + 'dateTime': '2020-08-11T12:30:55.123Z', + 'binary': "Instance of 'MultipartFile'", + }, + headers: { + 'content-type': 'application/x-www-form-urlencoded', + 'content-length': 255, + }, + handler: (response) => response.reply(200, null), + ); + + final response = await client.getFakeApi().testEndpointParameters( + 3, + -13.57, + 'patternWithoutDelimiter', + '0', + float: 1.23, + integer: 45, + int32: 2147483647, + int64: 9223372036854775807, + date: DateTime.utc(2020, 8, 11), + dateTime: DateTime.utc(2020, 8, 11, 12, 30, 55, 123), + binary: Uint8List.fromList([0, 1, 2, 3, 4, 5]), + ); + + expect(response.statusCode, 200); + }); + + test('minimal', () async { + server.onPost( + '/fake', + data: { + 'byte': '0', + 'double': '-13.57', + 'number': '3', + 'pattern_without_delimiter': 'patternWithoutDelimiter', + }, + headers: { + 'content-type': 'application/x-www-form-urlencoded', + 'content-length': 79, + }, + handler: (response) => response.reply(200, null), + ); + + final response = await client.getFakeApi().testEndpointParameters( + 3, + -13.57, + 'patternWithoutDelimiter', + '0', + ); + + expect(response.statusCode, 200); + }); + }); + + group('testEnumParameters', () { + test('in body data', () async { + // Not sure if this is correct, we are not sending + // form data in the body but some weird map + server.onGet( + '/fake', + data: { + 'enum_form_string': 'formString', + 'enum_form_string_array': '[foo, bar]', + }, + headers: { + 'content-type': 'application/x-www-form-urlencoded', + }, + handler: (response) => response.reply(200, null), + ); + + final response = await client.getFakeApi().testEnumParameters( + enumFormString: 'formString', + enumFormStringArray: ListBuilder( + ['foo', 'bar'], + ).build(), + ); + + expect(response.statusCode, 200); + }); + }); + }); +} diff --git a/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake_tests/test/api_util_test.dart b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake_tests/test/api_util_test.dart new file mode 100644 index 00000000000..f48210be33e --- /dev/null +++ b/samples/openapi3/client/petstore/dart-dio/petstore_client_lib_fake_tests/test/api_util_test.dart @@ -0,0 +1,230 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/serializer.dart'; +import 'package:dio/dio.dart'; +import 'package:openapi/api_util.dart'; +import 'package:openapi/model/cat.dart'; +import 'package:openapi/serializers.dart'; +import 'package:test/test.dart'; + +void main() { + group('api_utils', () { + group('encodeFormParameter should return', () { + test('empty String for null', () { + expect( + encodeFormParameter( + standardSerializers, + null, + const FullType(Cat), + ), + '', + ); + }); + + test('String for String', () { + expect( + encodeFormParameter( + standardSerializers, + 'foo', + const FullType(String), + ), + 'foo', + ); + }); + + test('List for BuiltList', () { + expect( + encodeFormParameter( + standardSerializers, + ListBuilder(['foo', 'bar', 'baz']).build(), + const FullType(BuiltList, [FullType(String)]), + ), + ['foo', 'bar', 'baz'], + ); + }); + + test('Map for BuiltList', () { + expect( + encodeFormParameter( + standardSerializers, + MapBuilder({ + 'foo': 'foo-value', + 'bar': 'bar-value', + 'baz': 'baz-value', + }).build(), + const FullType(BuiltMap, [FullType(String), FullType(String)]), + ), + { + 'foo': 'foo-value', + 'bar': 'bar-value', + 'baz': 'baz-value', + }, + ); + }); + + test('num for num', () { + expect( + encodeFormParameter(standardSerializers, 0, const FullType(int)), + 0, + ); + expect( + encodeFormParameter(standardSerializers, 1, const FullType(int)), + 1, + ); + expect( + encodeFormParameter(standardSerializers, 1.0, const FullType(num)), + 1.0, + ); + expect( + encodeFormParameter( + standardSerializers, 1.234, const FullType(double)), + 1.234, + ); + }); + + test('List for BuiltList', () { + expect( + encodeFormParameter( + standardSerializers, + ListBuilder([0, 1, 2, 3, 4.5, -123.456]).build(), + const FullType(BuiltList, [FullType(num)]), + ), + [0, 1, 2, 3, 4.5, -123.456], + ); + }); + + test('bool for bool', () { + expect( + encodeFormParameter( + standardSerializers, + true, + const FullType(bool), + ), + true, + ); + expect( + encodeFormParameter( + standardSerializers, + false, + const FullType(bool), + ), + false, + ); + }); + + test('String for Date', () { + expect( + encodeFormParameter( + standardSerializers, + DateTime.utc(2020, 8, 11), + const FullType(DateTime), + ), + '2020-08-11T00:00:00.000Z', + ); + }); + + test('String for DateTime', () { + expect( + encodeFormParameter( + standardSerializers, + DateTime.utc(2020, 8, 11, 12, 30, 55, 123), + const FullType(DateTime), + ), + '2020-08-11T12:30:55.123Z', + ); + }); + + test('JSON String for Cat', () { + // Not sure that is even a valid case, + // sending complex objects via FormData may not work as expected + expect( + encodeFormParameter( + standardSerializers, + (CatBuilder() + ..color = 'black' + ..className = 'cat' + ..declawed = false) + .build(), + const FullType(Cat), + ), + '{"className":"cat","color":"black","declawed":false}', + ); + }); + }); + + test('encodes FormData correctly', () { + final data = FormData.fromMap({ + 'null': encodeFormParameter( + standardSerializers, + null, + const FullType(num), + ), + 'empty': encodeFormParameter( + standardSerializers, + '', + const FullType(String), + ), + 'string_list': encodeFormParameter( + standardSerializers, + ListBuilder(['foo', 'bar', 'baz']).build(), + const FullType(BuiltList, [FullType(String)]), + ), + 'num_list': encodeFormParameter( + standardSerializers, + ListBuilder([0, 1, 2, 3, 4.5, -123.456]).build(), + const FullType(BuiltList, [FullType(num)]), + ), + 'string_map': encodeFormParameter( + standardSerializers, + MapBuilder({ + 'foo': 'foo-value', + 'bar': 'bar-value', + 'baz': 'baz-value', + }).build(), + const FullType(BuiltMap, [FullType(String), FullType(String)]), + ), + 'bool': encodeFormParameter( + standardSerializers, + true, + const FullType(bool), + ), + 'double': encodeFormParameter( + standardSerializers, + -123.456, + const FullType(double), + ), + 'date_time': encodeFormParameter( + standardSerializers, + DateTime.utc(2020, 8, 11, 12, 30, 55, 123), + const FullType(DateTime), + ), + }); + + expect( + data.fields, + pairwiseCompare, MapEntry>( + >[ + MapEntry('null', ''), + MapEntry('empty', ''), + MapEntry('string_list[]', 'foo'), + MapEntry('string_list[]', 'bar'), + MapEntry('string_list[]', 'baz'), + MapEntry('num_list[]', '0'), + MapEntry('num_list[]', '1'), + MapEntry('num_list[]', '2'), + MapEntry('num_list[]', '3'), + MapEntry('num_list[]', '4.5'), + MapEntry('num_list[]', '-123.456'), + MapEntry('string_map[foo]', 'foo-value'), + MapEntry('string_map[bar]', 'bar-value'), + MapEntry('string_map[baz]', 'baz-value'), + MapEntry('bool', 'true'), + MapEntry('double', '-123.456'), + MapEntry('date_time', '2020-08-11T12:30:55.123Z'), + ], + (e, a) => e.key == a.key && e.value == a.value, + 'Compares map entries by key and value', + ), + ); + }); + }); +}