From 4bd2cb871301a90cd5ffbf165578c881f10d201f Mon Sep 17 00:00:00 2001 From: jld3103 Date: Fri, 8 Dec 2023 15:13:22 +0100 Subject: [PATCH] fix(dynamite): Fix nullable someOf parameters Signed-off-by: jld3103 Signed-off-by: Nikolas Rimikis --- .../lib/src/models/type_result/some_of.dart | 5 +- .../lib/parameters.openapi.dart | 83 ++++++++++++++++++- .../lib/parameters.openapi.json | 28 +++++++ .../test/parameters_test.dart | 59 +++++++++++++ 4 files changed, 172 insertions(+), 3 deletions(-) diff --git a/packages/dynamite/dynamite/lib/src/models/type_result/some_of.dart b/packages/dynamite/dynamite/lib/src/models/type_result/some_of.dart index 3b5ea740390..52324077695 100644 --- a/packages/dynamite/dynamite/lib/src/models/type_result/some_of.dart +++ b/packages/dynamite/dynamite/lib/src/models/type_result/some_of.dart @@ -44,7 +44,10 @@ abstract class TypeResultSomeOf extends TypeResult { return '$dartType $fieldName'; }); - return TypeResultBase('({${record.join(',')}})'); + return TypeResultBase( + '({${record.join(',')}})', + nullable: nullable, + ); } late final String typeName = _typeName; diff --git a/packages/dynamite/dynamite_end_to_end_test/lib/parameters.openapi.dart b/packages/dynamite/dynamite_end_to_end_test/lib/parameters.openapi.dart index 3e7241aef33..7ca73885299 100644 --- a/packages/dynamite/dynamite_end_to_end_test/lib/parameters.openapi.dart +++ b/packages/dynamite/dynamite_end_to_end_test/lib/parameters.openapi.dart @@ -12,6 +12,7 @@ import 'package:built_value/standard_json_plugin.dart'; import 'package:dynamite_runtime/built_value.dart'; import 'package:dynamite_runtime/http_client.dart'; import 'package:dynamite_runtime/models.dart'; +import 'package:dynamite_runtime/utils.dart' as dynamite_utils; import 'package:meta/meta.dart'; import 'package:universal_io/io.dart'; import 'package:uri/uri.dart'; @@ -48,6 +49,8 @@ class Client extends DynamiteClient { /// * [$double] /// * [$num] /// * [object] + /// * [oneOf] + /// * [anyOf] /// /// Status codes: /// * 200 @@ -65,6 +68,8 @@ class Client extends DynamiteClient { final double? $double, final num? $num, final JsonObject? object, + final GetOneOf? oneOf, + final GetAnyOf? anyOf, }) async { final rawResponse = $getRaw( contentString: contentString, @@ -77,6 +82,8 @@ class Client extends DynamiteClient { $double: $double, $num: $num, object: object, + oneOf: oneOf, + anyOf: anyOf, ); return rawResponse.future; @@ -98,6 +105,8 @@ class Client extends DynamiteClient { /// * [$double] /// * [$num] /// * [object] + /// * [oneOf] + /// * [anyOf] /// /// Status codes: /// * 200 @@ -116,6 +125,8 @@ class Client extends DynamiteClient { final double? $double, final num? $num, final JsonObject? object, + final GetOneOf? oneOf, + final GetAnyOf? anyOf, }) { final parameters = {}; final headers = { @@ -163,9 +174,15 @@ class Client extends DynamiteClient { final $object = jsonSerializers.serialize(object, specifiedType: const FullType(JsonObject)); parameters['object'] = $object; + final $oneOf = jsonSerializers.serialize(oneOf, specifiedType: const FullType(GetOneOf)); + parameters['oneOf'] = $oneOf; + + final $anyOf = jsonSerializers.serialize(anyOf, specifiedType: const FullType(GetAnyOf)); + parameters['anyOf'] = $anyOf; + final uri = Uri.parse( UriTemplate( - '/{?content_string*,content_parameter*,array*,bool*,string*,string_binary*,int*,double*,num*,object*}', + '/{?content_string*,content_parameter*,array*,bool*,string*,string_binary*,int*,double*,num*,object*,oneOf*,anyOf*}', ).expand(parameters), ); return DynamiteRawResponse( @@ -349,6 +366,67 @@ class Client extends DynamiteClient { } } +typedef GetOneOf = ({bool? $bool, String? string}); + +typedef GetAnyOf = ({bool? $bool, String? string}); + +typedef $BoolString = ({bool? $bool, String? string}); + +extension $BoolStringExtension on $BoolString { + List get _values => [$bool, string]; + void validateOneOf() => dynamite_utils.validateOneOf(_values); + void validateAnyOf() => dynamite_utils.validateAnyOf(_values); + static Serializer<$BoolString> get serializer => const _$BoolStringSerializer(); + static $BoolString fromJson(final Object? json) => jsonSerializers.deserializeWith(serializer, json)!; + Object? toJson() => jsonSerializers.serializeWith(serializer, this); +} + +class _$BoolStringSerializer implements PrimitiveSerializer<$BoolString> { + const _$BoolStringSerializer(); + + @override + Iterable get types => const [$BoolString]; + + @override + String get wireName => r'$BoolString'; + + @override + Object serialize( + final Serializers serializers, + final $BoolString object, { + final FullType specifiedType = FullType.unspecified, + }) { + dynamic value; + value = object.$bool; + if (value != null) { + return serializers.serialize(value, specifiedType: const FullType(bool))!; + } + value = object.string; + if (value != null) { + return serializers.serialize(value, specifiedType: const FullType(String))!; + } +// Should not be possible after validation. + throw StateError('Tried to serialize without any value.'); + } + + @override + $BoolString deserialize( + final Serializers serializers, + final Object data, { + final FullType specifiedType = FullType.unspecified, + }) { + bool? $bool; + try { + $bool = serializers.deserialize(data, specifiedType: const FullType(bool))! as bool; + } catch (_) {} + String? string; + try { + string = serializers.deserialize(data, specifiedType: const FullType(String))! as String; + } catch (_) {} + return ($bool: $bool, string: string); + } +} + // coverage:ignore-start @visibleForTesting final Serializers serializers = (Serializers().toBuilder() @@ -363,7 +441,8 @@ final Serializers serializers = (Serializers().toBuilder() ContentStringBuilder>.new, ) ..add(ContentString.serializer) - ..addBuilderFactory(const FullType(BuiltList, [FullType(String)]), ListBuilder.new)) + ..addBuilderFactory(const FullType(BuiltList, [FullType(String)]), ListBuilder.new) + ..add($BoolStringExtension.serializer)) .build(); @visibleForTesting diff --git a/packages/dynamite/dynamite_end_to_end_test/lib/parameters.openapi.json b/packages/dynamite/dynamite_end_to_end_test/lib/parameters.openapi.json index 079a3f9f8a0..502817b1d16 100644 --- a/packages/dynamite/dynamite_end_to_end_test/lib/parameters.openapi.json +++ b/packages/dynamite/dynamite_end_to_end_test/lib/parameters.openapi.json @@ -92,6 +92,34 @@ "schema": { "type": "object" } + }, + { + "name": "oneOf", + "in": "query", + "schema": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "boolean" + } + ] + } + }, + { + "name": "anyOf", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "boolean" + } + ] + } } ], "responses": { diff --git a/packages/dynamite/dynamite_end_to_end_test/test/parameters_test.dart b/packages/dynamite/dynamite_end_to_end_test/test/parameters_test.dart index bd4b8b653ca..ac2122b2e74 100644 --- a/packages/dynamite/dynamite_end_to_end_test/test/parameters_test.dart +++ b/packages/dynamite/dynamite_end_to_end_test/test/parameters_test.dart @@ -133,4 +133,63 @@ void main() { ); }); }); + + group('getSomeOf', () { + test('oneOf', () async { + await client.$get(oneOf: ($bool: true, string: null)); + var captured = verify(mockHttpClient.openUrl(captureAny, captureAny)).captured; + expect( + captured, + equals([ + 'get', + Uri.parse('example.com/?oneOf=true'), + ]), + ); + resetMockitoState(); + + await client.$get(oneOf: ($bool: null, string: 'value')); + captured = verify(mockHttpClient.openUrl(captureAny, captureAny)).captured; + expect( + captured, + equals([ + 'get', + Uri.parse('example.com/?oneOf=value'), + ]), + ); + }); + + test('anyOf', () async { + await client.$get(anyOf: ($bool: true, string: null)); + var captured = verify(mockHttpClient.openUrl(captureAny, captureAny)).captured; + expect( + captured, + equals([ + 'get', + Uri.parse('example.com/?anyOf=true'), + ]), + ); + resetMockitoState(); + + await client.$get(anyOf: ($bool: null, string: 'value')); + captured = verify(mockHttpClient.openUrl(captureAny, captureAny)).captured; + expect( + captured, + equals([ + 'get', + Uri.parse('example.com/?anyOf=value'), + ]), + ); + resetMockitoState(); + + await client.$get(anyOf: ($bool: true, string: 'value')); + captured = verify(mockHttpClient.openUrl(captureAny, captureAny)).captured; + expect( + captured, + equals([ + 'get', + Uri.parse('example.com/?anyOf=true'), + ]), + ); + }); + }); }