diff --git a/packages/dynamite/dynamite/lib/src/builder/generate_schemas.dart b/packages/dynamite/dynamite/lib/src/builder/generate_schemas.dart index 17a6c46f..604fea9e 100644 --- a/packages/dynamite/dynamite/lib/src/builder/generate_schemas.dart +++ b/packages/dynamite/dynamite/lib/src/builder/generate_schemas.dart @@ -3,6 +3,7 @@ import 'package:dynamite/src/builder/resolve_type.dart'; import 'package:dynamite/src/builder/state.dart'; import 'package:dynamite/src/helpers/dart_helpers.dart'; import 'package:dynamite/src/models/openapi.dart' as openapi; +import 'package:dynamite/src/models/type_result.dart'; Iterable generateSchemas( final openapi.OpenAPI spec, @@ -11,13 +12,21 @@ Iterable generateSchemas( if (spec.components?.schemas != null) { for (final schema in spec.components!.schemas!.entries) { final identifier = toDartName(schema.key, uppercaseFirstCharacter: true); - - resolveType( + final result = resolveType( spec, state, identifier, schema.value, ); + + // TypeDefs should only be generated for top level schemas. + if (result is TypeResultBase || result.isTypeDef) { + yield TypeDef( + (final b) => b + ..name = identifier + ..definition = refer(result.name), + ); + } } } diff --git a/packages/dynamite/dynamite/lib/src/builder/resolve_type.dart b/packages/dynamite/dynamite/lib/src/builder/resolve_type.dart index 607f5ad6..6ba67236 100644 --- a/packages/dynamite/dynamite/lib/src/builder/resolve_type.dart +++ b/packages/dynamite/dynamite/lib/src/builder/resolve_type.dart @@ -22,13 +22,15 @@ TypeResult resolveType( } if (schema.ref != null) { final name = schema.ref!.split('/').last; - result = resolveType( + final subResult = resolveType( spec, state, name, spec.components!.schemas![name]!, nullable: nullable, ); + + result = subResult.asTypeDef; } else if (schema.allOf != null) { result = resolveAllOf( spec, diff --git a/packages/dynamite/dynamite/lib/src/models/type_result/base.dart b/packages/dynamite/dynamite/lib/src/models/type_result/base.dart index aa58e38c..eba4205f 100644 --- a/packages/dynamite/dynamite/lib/src/models/type_result/base.dart +++ b/packages/dynamite/dynamite/lib/src/models/type_result/base.dart @@ -5,6 +5,7 @@ class TypeResultBase extends TypeResult { TypeResultBase( super.className, { super.nullable, + super.isTypeDef, }); @override diff --git a/packages/dynamite/dynamite/lib/src/models/type_result/enum.dart b/packages/dynamite/dynamite/lib/src/models/type_result/enum.dart index 805f9253..9a86f29f 100644 --- a/packages/dynamite/dynamite/lib/src/models/type_result/enum.dart +++ b/packages/dynamite/dynamite/lib/src/models/type_result/enum.dart @@ -6,6 +6,7 @@ class TypeResultEnum extends TypeResult { super.className, this.subType, { super.nullable, + super.isTypeDef, }); final TypeResult subType; diff --git a/packages/dynamite/dynamite/lib/src/models/type_result/list.dart b/packages/dynamite/dynamite/lib/src/models/type_result/list.dart index c56f2731..69a8e9ed 100644 --- a/packages/dynamite/dynamite/lib/src/models/type_result/list.dart +++ b/packages/dynamite/dynamite/lib/src/models/type_result/list.dart @@ -6,6 +6,7 @@ class TypeResultList extends TypeResult { super.className, final TypeResult subType, { super.nullable, + super.isTypeDef, }) : super(generics: [subType]); TypeResult get subType => generics.first; diff --git a/packages/dynamite/dynamite/lib/src/models/type_result/map.dart b/packages/dynamite/dynamite/lib/src/models/type_result/map.dart index ce18b15b..73646080 100644 --- a/packages/dynamite/dynamite/lib/src/models/type_result/map.dart +++ b/packages/dynamite/dynamite/lib/src/models/type_result/map.dart @@ -6,6 +6,7 @@ class TypeResultMap extends TypeResult { super.className, final TypeResult subType, { super.nullable, + super.isTypeDef, }) : super(generics: [TypeResultBase('String'), subType]); TypeResult get subType => generics[1]; diff --git a/packages/dynamite/dynamite/lib/src/models/type_result/object.dart b/packages/dynamite/dynamite/lib/src/models/type_result/object.dart index 830f91a7..e3d28c57 100644 --- a/packages/dynamite/dynamite/lib/src/models/type_result/object.dart +++ b/packages/dynamite/dynamite/lib/src/models/type_result/object.dart @@ -8,6 +8,7 @@ class TypeResultObject extends TypeResult { super.className, { super.generics, super.nullable, + super.isTypeDef, }) : assert( className != 'JsonObject' && className != 'Object' && className != 'dynamic', 'Use TypeResultBase instead', diff --git a/packages/dynamite/dynamite/lib/src/models/type_result/type_result.dart b/packages/dynamite/dynamite/lib/src/models/type_result/type_result.dart index 46eb94ac..c7f96eee 100644 --- a/packages/dynamite/dynamite/lib/src/models/type_result/type_result.dart +++ b/packages/dynamite/dynamite/lib/src/models/type_result/type_result.dart @@ -13,6 +13,7 @@ sealed class TypeResult { this.className, { this.generics = const [], this.nullable = false, + this.isTypeDef = false, }) : assert(!className.contains('<'), 'Specify generics in the generics parameter.'), assert(!className.contains('?'), 'Nullability should not be specified in the type.'); @@ -20,6 +21,9 @@ sealed class TypeResult { final List generics; final bool nullable; + /// Whether this type should be represented as a typedef. + final bool isTypeDef; + String get name { if (generics.isNotEmpty) { final buffer = StringBuffer('$className<') @@ -44,6 +48,10 @@ sealed class TypeResult { } Iterable get serializers sync* { + if (isTypeDef) { + return; + } + for (final class_ in generics) { yield* class_.serializers; } @@ -104,6 +112,44 @@ sealed class TypeResult { // ignore: avoid_returning_this TypeResult get dartType => this; + /// Returns `this` with a `true` value for [isTypeDef]. + TypeResult get asTypeDef { + final $this = this; + + // We need to preserve the original runtime type. + return switch ($this) { + TypeResultBase() => TypeResultBase( + className, + nullable: nullable, + isTypeDef: true, + ), + TypeResultEnum() => TypeResultEnum( + className, + $this.subType, + nullable: nullable, + isTypeDef: true, + ), + TypeResultList() => TypeResultList( + className, + $this.subType, + nullable: nullable, + isTypeDef: true, + ), + TypeResultMap() => TypeResultMap( + className, + $this.subType, + nullable: nullable, + isTypeDef: true, + ), + TypeResultObject() => TypeResultObject( + className, + generics: generics, + nullable: nullable, + isTypeDef: true, + ), + }; + } + @override bool operator ==(final Object other) => other is TypeResult && other.className == className && other.generics == generics;