Browse Source

feat(dynamite): use inheritance for allOf

Signed-off-by: Nikolas Rimikis <leptopoda@users.noreply.github.com>
pull/704/head
Nikolas Rimikis 1 year ago
parent
commit
3d0508edc0
No known key found for this signature in database
GPG Key ID: 85ED1DE9786A4FF2
  1. 74
      packages/dynamite/dynamite/lib/src/builder/ofs_builder.dart
  2. 91
      packages/dynamite/dynamite/lib/src/builder/resolve_interface.dart
  3. 52
      packages/dynamite/dynamite/lib/src/builder/resolve_object.dart
  4. 8
      packages/dynamite/dynamite/lib/src/builder/resolve_type.dart
  5. 1
      packages/dynamite/dynamite/lib/src/builder/state.dart
  6. 22
      packages/dynamite/dynamite/lib/src/helpers/built_value.dart

74
packages/dynamite/dynamite/lib/src/builder/ofs_builder.dart

@ -1,5 +1,6 @@
import 'package:built_collection/built_collection.dart'; import 'package:built_collection/built_collection.dart';
import 'package:code_builder/code_builder.dart'; import 'package:code_builder/code_builder.dart';
import 'package:dynamite/src/builder/resolve_interface.dart';
import 'package:dynamite/src/builder/resolve_type.dart'; import 'package:dynamite/src/builder/resolve_type.dart';
import 'package:dynamite/src/builder/state.dart'; import 'package:dynamite/src/builder/state.dart';
import 'package:dynamite/src/helpers/built_value.dart'; import 'package:dynamite/src/helpers/built_value.dart';
@ -8,6 +9,61 @@ import 'package:dynamite/src/models/open_api.dart';
import 'package:dynamite/src/models/schema.dart'; import 'package:dynamite/src/models/schema.dart';
import 'package:dynamite/src/type_result/type_result.dart'; import 'package:dynamite/src/type_result/type_result.dart';
TypeResult resolveAllOf(
final OpenAPI spec,
final State state,
final String identifier,
final Schema schema, {
final bool nullable = false,
}) {
final result = TypeResultObject(
'${state.classPrefix}$identifier',
nullable: nullable,
);
if (state.resolvedTypes.add(result)) {
final interfaces = <TypeResultObject>{};
for (final s in schema.allOf!) {
final TypeResultObject interfaceClass;
if (s.ref != null) {
final object = resolveType(
spec,
state,
identifier,
s,
nullable: nullable,
);
if (object is! TypeResultObject) {
throw StateError('allOf does only allow objects. Please change $identifier');
}
interfaceClass = object;
} else {
final interfaceName = schema.ofs!.length == 1 ? identifier : '${identifier}_${schema.allOf!.indexOf(s)}';
interfaceClass = resolveInterface(
spec,
state,
interfaceName,
s,
);
}
interfaces.add(interfaceClass);
}
state.output.add(
buildBuiltClass(
'${state.classPrefix}$identifier',
interfaces: interfaces,
),
);
}
return result;
}
TypeResult resolveOfs( TypeResult resolveOfs(
final OpenAPI spec, final OpenAPI spec,
final State state, final State state,
@ -15,6 +71,10 @@ TypeResult resolveOfs(
final Schema schema, { final Schema schema, {
final bool nullable = false, final bool nullable = false,
}) { }) {
if (schema.allOf != null) {
throw StateError('allOf should be handled with inheritance');
}
final result = TypeResultObject( final result = TypeResultObject(
'${state.classPrefix}$identifier', '${state.classPrefix}$identifier',
nullable: nullable, nullable: nullable,
@ -28,7 +88,7 @@ TypeResult resolveOfs(
state, state,
'$identifier${schema.ofs!.indexOf(s)}', '$identifier${schema.ofs!.indexOf(s)}',
s, s,
nullable: !(schema.allOf?.contains(s) ?? false), nullable: true,
), ),
) )
.toList(); .toList();
@ -42,7 +102,7 @@ TypeResult resolveOfs(
state.output.addAll([ state.output.addAll([
buildBuiltClass( buildBuiltClass(
'${state.classPrefix}$identifier', '${state.classPrefix}$identifier',
BuiltList.build((final b) { methods: BuiltList.build((final b) {
b.add( b.add(
Method( Method(
(final b) { (final b) {
@ -152,15 +212,6 @@ TypeResult resolveOfs(
<String>[ <String>[
'final result = new ${state.classPrefix}${identifier}Builder()', 'final result = new ${state.classPrefix}${identifier}Builder()',
'..data = JsonObject(data);', '..data = JsonObject(data);',
if (schema.allOf != null) ...[
for (final result in results) ...[
if (result is TypeResultBase || result is TypeResultEnum) ...[
'result.${fields[result.name]!} = ${result.deserialize('data')};',
] else ...[
'result.${fields[result.name]!}.replace(${result.deserialize('data')});',
],
],
] else ...[
if (schema.discriminator != null) ...[ if (schema.discriminator != null) ...[
'if (data is! Iterable) {', 'if (data is! Iterable) {',
r"throw StateError('Expected an Iterable but got ${data.runtimeType}');", r"throw StateError('Expected an Iterable but got ${data.runtimeType}');",
@ -215,7 +266,6 @@ TypeResult resolveOfs(
if (schema.anyOf != null) ...[ if (schema.anyOf != null) ...[
"assert([${fields.values.map((final e) => 'result._$e').join(',')}].where((final x) => x != null).length >= 1, 'Need anyOf for \${result._data}');", "assert([${fields.values.map((final e) => 'result._$e').join(',')}].where((final x) => x != null).length >= 1, 'Need anyOf for \${result._data}');",
], ],
],
'return result.build();', 'return result.build();',
].join(), ].join(),
); );

91
packages/dynamite/dynamite/lib/src/builder/resolve_interface.dart

@ -0,0 +1,91 @@
import 'package:code_builder/code_builder.dart';
import 'package:dynamite/src/builder/resolve_type.dart';
import 'package:dynamite/src/builder/state.dart';
import 'package:dynamite/src/helpers/built_value.dart';
import 'package:dynamite/src/helpers/dart_helpers.dart';
import 'package:dynamite/src/helpers/dynamite.dart';
import 'package:dynamite/src/models/open_api.dart';
import 'package:dynamite/src/models/schema.dart';
import 'package:dynamite/src/type_result/type_result.dart';
TypeResultObject resolveInterface(
final OpenAPI spec,
final State state,
final String identifier,
final Schema schema,
) {
final result = TypeResultObject(
'${state.classPrefix}$identifier',
);
if (state.resolvedInterfaces.add(result)) {
final className = '${state.classPrefix}$identifier$interfaceSuffix';
state.output.add(
Class((final b) {
b
..abstract = true
..modifier = ClassModifier.interface
..name = className
..annotations.add(refer('BuiltValue').call([], {'instantiable': literalFalse}));
for (final property in schema.properties!.entries) {
b.methods.add(
Method(
(final b) {
final propertyName = property.key;
final propertySchema = property.value;
final result = resolveType(
spec,
state,
'${identifier}_${toDartName(propertyName, uppercaseFirstCharacter: true)}',
propertySchema,
nullable: isDartParameterNullable(
schema.required?.contains(propertyName),
propertySchema,
),
);
b
..name = toDartName(propertyName)
..returns = refer(result.nullableName)
..type = MethodType.getter
..docs.addAll(propertySchema.formattedDescription);
if (toDartName(propertyName) != propertyName) {
b.annotations.add(
refer('BuiltValueField').call([], {
'wireName': literalString(propertyName),
}),
);
}
},
),
);
}
b.methods.addAll([
Method(
(final b) => b
..returns = refer(className)
..name = 'rebuild'
..requiredParameters.add(
Parameter(
(final b) => b
..name = 'updates'
..type = refer('void Function(${className}Builder)'),
),
),
),
Method(
(final b) => b
..returns = refer('${className}Builder')
..name = 'toBuilder',
),
]);
}),
);
}
return result;
}

52
packages/dynamite/dynamite/lib/src/builder/resolve_object.dart

@ -1,17 +1,15 @@
import 'package:built_collection/built_collection.dart';
import 'package:code_builder/code_builder.dart';
import 'package:dynamite/src/builder/header_serializer.dart'; import 'package:dynamite/src/builder/header_serializer.dart';
import 'package:dynamite/src/builder/resolve_interface.dart';
import 'package:dynamite/src/builder/resolve_type.dart'; import 'package:dynamite/src/builder/resolve_type.dart';
import 'package:dynamite/src/builder/state.dart'; import 'package:dynamite/src/builder/state.dart';
import 'package:dynamite/src/helpers/built_value.dart'; import 'package:dynamite/src/helpers/built_value.dart';
import 'package:dynamite/src/helpers/dart_helpers.dart'; import 'package:dynamite/src/helpers/dart_helpers.dart';
import 'package:dynamite/src/helpers/dynamite.dart';
import 'package:dynamite/src/helpers/type_result.dart'; import 'package:dynamite/src/helpers/type_result.dart';
import 'package:dynamite/src/models/open_api.dart'; import 'package:dynamite/src/models/open_api.dart';
import 'package:dynamite/src/models/schema.dart'; import 'package:dynamite/src/models/schema.dart';
import 'package:dynamite/src/type_result/type_result.dart'; import 'package:dynamite/src/type_result/type_result.dart';
TypeResult resolveObject( TypeResultObject resolveObject(
final OpenAPI spec, final OpenAPI spec,
final State state, final State state,
final String identifier, final String identifier,
@ -39,49 +37,23 @@ TypeResult resolveObject(
} }
} }
state.output.add( final interfaceClass = resolveInterface(
buildBuiltClass(
'${state.classPrefix}$identifier',
BuiltList.build((final b) {
for (final property in schema.properties!.entries) {
b.add(
Method(
(final b) {
final propertyName = property.key;
final propertySchema = property.value;
final result = resolveType(
spec, spec,
state, state,
'${identifier}_${toDartName(propertyName, uppercaseFirstCharacter: true)}', identifier,
propertySchema, schema,
nullable: isDartParameterNullable(
schema.required?.contains(propertyName),
propertySchema,
),
); );
b state.output.addAll([
..name = toDartName(propertyName) buildBuiltClass(
..returns = refer(result.nullableName) '${state.classPrefix}$identifier',
..type = MethodType.getter
..docs.addAll(propertySchema.formattedDescription);
if (toDartName(propertyName) != propertyName) {
b.annotations.add(
refer('BuiltValueField').call([], {
'wireName': literalString(propertyName),
}),
);
}
},
),
);
}
}),
defaults: defaults, defaults: defaults,
customSerializer: isHeader, customSerializer: isHeader,
interfaces: [
interfaceClass,
],
), ),
); ]);
if (isHeader) { if (isHeader) {
state.output.add(buildHeaderSerializer(state, identifier, spec, schema)); state.output.add(buildHeaderSerializer(state, identifier, spec, schema));
} }

8
packages/dynamite/dynamite/lib/src/builder/resolve_type.dart

@ -30,6 +30,14 @@ TypeResult resolveType(
spec.components!.schemas![name]!, spec.components!.schemas![name]!,
nullable: nullable, nullable: nullable,
); );
} else if (schema.allOf != null) {
result = resolveAllOf(
spec,
state,
identifier,
schema,
nullable: nullable,
);
} else if (schema.ofs != null) { } else if (schema.ofs != null) {
result = resolveOfs( result = resolveOfs(
spec, spec,

1
packages/dynamite/dynamite/lib/src/builder/state.dart

@ -12,4 +12,5 @@ class State {
final output = <Spec>[]; final output = <Spec>[];
final resolvedTypes = <TypeResult>{}; final resolvedTypes = <TypeResult>{};
final resolvedInterfaces = <TypeResult>{};
} }

22
packages/dynamite/dynamite/lib/src/helpers/built_value.dart

@ -1,23 +1,30 @@
import 'package:built_collection/built_collection.dart'; import 'package:built_collection/built_collection.dart';
import 'package:code_builder/code_builder.dart'; import 'package:code_builder/code_builder.dart';
import 'package:dynamite/src/helpers/dart_helpers.dart'; import 'package:dynamite/src/helpers/dart_helpers.dart';
import 'package:dynamite/src/type_result/type_result.dart';
const interfaceSuffix = 'Interface';
Spec buildBuiltClass( Spec buildBuiltClass(
final String className, final String className, {
final BuiltList<Method> methods, { final BuiltList<Method>? methods,
final Iterable<String>? defaults, final Iterable<String>? defaults,
final Iterable<TypeResultObject>? interfaces,
final bool customSerializer = false, final bool customSerializer = false,
}) => }) {
Class( assert((interfaces == null) != (methods == null), 'Either provide an interface or methods.');
return Class(
(final b) { (final b) {
b b
..name = className ..name = className
..abstract = true ..abstract = true
..implements.add( ..implements.addAll([
...?interfaces?.map((final i) => refer('${i.name}$interfaceSuffix')),
refer( refer(
'Built<$className, ${className}Builder>', 'Built<$className, ${className}Builder>',
), ),
) ])
..constructors.addAll([ ..constructors.addAll([
builtValueConstructor(className), builtValueConstructor(className),
hiddenConstructor, hiddenConstructor,
@ -25,7 +32,7 @@ Spec buildBuiltClass(
]) ])
..methods.addAll([ ..methods.addAll([
toJsonMethod, toJsonMethod,
...methods, ...?methods,
buildSerializer(className, isCustom: customSerializer), buildSerializer(className, isCustom: customSerializer),
]); ]);
@ -60,6 +67,7 @@ Spec buildBuiltClass(
} }
}, },
); );
}
Method get toJsonMethod => Method( Method get toJsonMethod => Method(
(final b) => b (final b) => b

Loading…
Cancel
Save