Skip to content

Commit

Permalink
AllOf with one $ref generates code with a base class.
Browse files Browse the repository at this point in the history
  • Loading branch information
Corniel committed Mar 1, 2024
1 parent 9abee4c commit 68af780
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------

#nullable enable

namespace AllOfExamples;

[System.Text.Json.Serialization.JsonDerivedType(typeof(AllOfExamples.SomeChildClass))]
public partial record SomeBaseClass
{
[System.Text.Json.Serialization.JsonPropertyName("id")]
[Qowaiv.Validation.DataAnnotations.Optional]
public long Id { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------

#nullable enable

namespace AllOfExamples;

public sealed partial record SomeChildClass : AllOfExamples.SomeBaseClass
{
[System.Text.Json.Serialization.JsonPropertyName("email")]
[Qowaiv.Validation.DataAnnotations.Optional]
public Qowaiv.EmailAddress Email { get; init; }
}
38 changes: 38 additions & 0 deletions specs/Qowaiv.CodeGeneration.Specs/OpenApi/Definitions/all-of.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"openapi": "3.0.2",
"info": {
"title": "All Of example",
"version": "1.0.0"
},
"paths": {},
"components": {
"schemas": {
"SomeBaseClass": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"example": 1
}
}
},
"SomeChildClass": {
"allOf": [
{
"$ref": "#/components/schemas/SomeBaseClass"
},
{
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email"
}
}
}
]
}
}
}
}
20 changes: 19 additions & 1 deletion specs/Qowaiv.CodeGeneration.Specs/OpenApi/Resolve_specs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Open_API.Resolve_specs;
public class Generates
{
private static readonly FileInfo PetShop_json = new("./OpenApi/Definitions/pet-shop.json");
private static readonly FileInfo AllOf_json = new("./OpenApi/Definitions/all-of.json");

[Test]
public void Compiling_code()
Expand All @@ -21,7 +22,24 @@ public void Compiling_code()
{
RootDirectory = new("../../../../../specs/Qowaiv.CodeGeneration.Specs/Generated/PetShopBoys"),
RootNamespace = "PetShopBoys",
Headers = new Code[] { new AutoGeneratedHeader(), NullableHeader.Enable },
Headers = [new AutoGeneratedHeader(), NullableHeader.Enable],
};

code.Save(codeFileSettings);
}

[Test]
public void base_class_for_all_off()
{
var result = OpenApiCode.Resolve(AllOf_json, new OpenApiTypeResolver("AllOfExamples"));

var code = result.Should().BeValid().WithoutMessages().Value;

var codeFileSettings = new CodeFileWriterSettings
{
RootDirectory = new("../../../../../specs/Qowaiv.CodeGeneration.Specs/Generated/AllOfExamples"),
RootNamespace = "AllOfExamples",
Headers = [new AutoGeneratedHeader(), NullableHeader.Enable],
};

code.Save(codeFileSettings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public virtual IEnumerable<AttributeInfo> DecorateModel(Class @class, ResolveOpe
return AttributeInfo.System_Text_Json_Serialization_JsonDerivedTypeAttribute(derivedType);
}


[Pure]
protected virtual IEnumerable<AttributeInfo> DecorateProperty(Property property, ResolveOpenApiSchema schema)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ _ when schema.Enum.Any() => ResolveEnum(schema),
OpenApiType.@string => ResolveString(schema),
OpenApiType.array => ResolveArray(schema),
OpenApiType.@object => ResolveObject(schema),
_ when schema.OneOf.Any() => ResolveOneOf(schema),
_ when schema.AllOf.Any() => ResolveAllOf(schema),
_ when schema.OneOf.Any() => ResolveOneOf(schema),
_ => ResolveOther(schema),
};

Expand Down Expand Up @@ -236,33 +236,46 @@ static bool IsNone(EnumerationField field)
/// Resolves the <see cref="Type"/> when there is at least one <see cref="ResolveOpenApiSchema.AllOf"/>'s.
/// </summary>
[Pure]
[Obsolete("Will become private. Use ResolveCustomization(schema) instead.")]
protected virtual Type? ResolveAllOf(ResolveOpenApiSchema schema)
private Type? ResolveAllOf(ResolveOpenApiSchema schema)
{
if (schema.AllOf.Count() == 1)
var bases = schema.AllOf.Where(b => b.Reference is { }).ToArray();
var inherit = schema.AllOf.Where(b => b.Reference is null).ToArray();

if (bases.Length == 1)
{
return ResolveDerivedFrom(schema, schema.AllOf.First());
}
else throw new NotSupportedException($"Schema with type multiple all-off is not supported.");
}
if (inherit.Length == 0)
{
return Resolve(bases[0]);
}
else if (inherit.Length == 1
&& Resolve(bases[0]) is { } @base
&& Resolve(inherit[0].WithBase(@base)) is { } type)
{
AddDerivedType(@base, type, schema);

[Pure]
private Type? ResolveDerivedFrom(ResolveOpenApiSchema schema, ResolveOpenApiSchema parent)
{
var type = Resolve(parent);
return type;
}
}
throw new NotSupportedException($"Schema '{schema.Path}' with type multiple all-off is not supported.");

if (type is Class @class
&& @class.GetDerivedTypes() is List<Type> derivedTypes
&& @class.GetAttributeInfos() is List<AttributeInfo> infos)
void AddDerivedType(Type @base, Type type, ResolveOpenApiSchema schema)
{
derivedTypes.AddRange(schema.OneOf.Select(Resolve).OfType<Type>());
infos.AddRange(derivedTypes.Select(d => DecorateDerivedType(d, schema)).OfType<AttributeInfo>());
if (@base is Class @class
&& @class.GetDerivedTypes() is List<Type> derivedTypes
&& @class.GetAttributeInfos() is List<AttributeInfo> infos
&& !derivedTypes.Exists(t => t.FullName == type.FullName))
{
derivedTypes.Add(type);
if (DecorateDerivedType(type, schema) is { } attr)
{
infos.Add(attr);
}
}
}
return type;
}

/// <summary>Resolves the <see cref="Type"/> for <see cref="OpenApiType.None"/>.</summary>
[Pure]
protected virtual Type? ResolveOther(ResolveOpenApiSchema schema)
=> throw new NotSupportedException($"Schema with type '{schema.Type}' is not supported.");
=> throw new NotSupportedException($"Schema '{schema.Path}' with type '{schema.Type}' is not supported.");
}
37 changes: 10 additions & 27 deletions src/Qowaiv.CodeGeneration.OpenApi/ResolveOpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,13 @@ public readonly partial struct ResolveOpenApiSchema(
public string? ReferenceId => Reference?.Id;

/// <inheritdoc cref="OpenApiSchema.AllOf"/>
public IEnumerable<ResolveOpenApiSchema> AllOf
{
get
{
var self = this;
return Schema?.AllOf?.Select(self.With)
?? [];
}
}
public IEnumerable<ResolveOpenApiSchema> AllOf => Schema?.AllOf?.Select(SelectionOf) ?? [];

/// <inheritdoc cref="OpenApiSchema.AnyOf"/>
public IEnumerable<ResolveOpenApiSchema> AnyOf
{
get
{
var self = this;
return Schema?.AnyOf?.Select(self.With)
?? [];
}
}
public IEnumerable<ResolveOpenApiSchema> AnyOf => Schema?.AnyOf?.Select(SelectionOf) ?? [];

/// <inheritdoc cref="OpenApiSchema.OneOf"/>
public IEnumerable<ResolveOpenApiSchema> OneOf
{
get
{
var self = this;
return Schema?.OneOf?.Select(self.With)
?? [];
}
}
public IEnumerable<ResolveOpenApiSchema> OneOf => Schema?.OneOf?.Select(SelectionOf) ?? [];

/// <inheritdoc cref="OpenApiSchema.Description"/>
public string? Description => Schema?.Description;
Expand Down Expand Up @@ -142,4 +118,11 @@ public override string ToString()
return sb.ToString();
}
}

[Pure]
private ResolveOpenApiSchema SelectionOf(OpenApiSchema s)
=> s.Reference is { } r
? new ResolveOpenApiSchema(new(r.ReferenceV3 ?? r.ReferenceV2), s, null)
: With(s);

}
5 changes: 4 additions & 1 deletion src/Qowaiv.CodeGeneration/Types/TypeBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,10 @@ public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)

/// <inheritdoc />
[Pure]
protected override TypeAttributes GetAttributeFlagsImpl() => TypeAttributes;
protected override TypeAttributes GetAttributeFlagsImpl()
=> DerivedTypes.Any()
? TypeAttributes ^ TypeAttributes.Sealed
: TypeAttributes;

/// <inheritdoc />
[Pure]
Expand Down

0 comments on commit 68af780

Please sign in to comment.