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: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/state.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/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(
final OpenAPI spec,
final State state,
@ -15,6 +71,10 @@ TypeResult resolveOfs(
final Schema schema, {
final bool nullable = false,
}) {
if (schema.allOf != null) {
throw StateError('allOf should be handled with inheritance');
}
final result = TypeResultObject(
'${state.classPrefix}$identifier',
nullable: nullable,
@ -28,7 +88,7 @@ TypeResult resolveOfs(
state,
'$identifier${schema.ofs!.indexOf(s)}',
s,
nullable: !(schema.allOf?.contains(s) ?? false),
nullable: true,
),
)
.toList();
@ -42,7 +102,7 @@ TypeResult resolveOfs(
state.output.addAll([
buildBuiltClass(
'${state.classPrefix}$identifier',
BuiltList.build((final b) {
methods: BuiltList.build((final b) {
b.add(
Method(
(final b) {
@ -152,15 +212,6 @@ TypeResult resolveOfs(
<String>[
'final result = new ${state.classPrefix}${identifier}Builder()',
'..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 (data is! Iterable) {',
r"throw StateError('Expected an Iterable but got ${data.runtimeType}');",
@ -215,7 +266,6 @@ TypeResult resolveOfs(
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}');",
],
],
'return result.build();',
].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/resolve_interface.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/helpers/type_result.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';
TypeResult resolveObject(
TypeResultObject resolveObject(
final OpenAPI spec,
final State state,
final String identifier,
@ -39,49 +37,23 @@ TypeResult resolveObject(
}
}
state.output.add(
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(
final interfaceClass = resolveInterface(
spec,
state,
'${identifier}_${toDartName(propertyName, uppercaseFirstCharacter: true)}',
propertySchema,
nullable: isDartParameterNullable(
schema.required?.contains(propertyName),
propertySchema,
),
identifier,
schema,
);
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),
}),
);
}
},
),
);
}
}),
state.output.addAll([
buildBuiltClass(
'${state.classPrefix}$identifier',
defaults: defaults,
customSerializer: isHeader,
interfaces: [
interfaceClass,
],
),
);
]);
if (isHeader) {
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]!,
nullable: nullable,
);
} else if (schema.allOf != null) {
result = resolveAllOf(
spec,
state,
identifier,
schema,
nullable: nullable,
);
} else if (schema.ofs != null) {
result = resolveOfs(
spec,

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

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

Loading…
Cancel
Save