Permalink
Browse files

Improve allOf inheritance support (#783)

* Improve allOf inheritance support

* Keep order

* Fix OpenAPI null handling

* Add braces
  • Loading branch information...
RSuter committed Sep 25, 2018
1 parent e61686c commit 3a00302b1b775d67bfa4726acb83f1362f142b8f
View
BIN +3.59 MB (330%) build/NuGet.exe
Binary file not shown.
@@ -278,7 +278,7 @@ public async Task When_class_has_description_then_csharp_has_xml_comment()
{
//// Arrange
var schema = await JsonSchema4.FromTypeAsync<Teacher>();
schema.Description = "ClassDesc.";
schema.ActualSchema.Description = "ClassDesc.";
var generator = new CSharpGenerator(schema);
//// Act
@@ -295,7 +295,7 @@ public async Task When_property_has_description_then_csharp_has_xml_comment()
{
//// Arrange
var schema = await JsonSchema4.FromTypeAsync<Teacher>();
schema.Properties["Class"].Description = "PropertyDesc.";
schema.ActualProperties["Class"].Description = "PropertyDesc.";
var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings { ClassStyle = CSharpClassStyle.Poco });
//// Act
@@ -19,13 +19,18 @@ public async Task When_class_is_abstract_then_is_abstract_TypeScript_keyword_is_
{
/// Arrange
var schema = await JsonSchema4.FromTypeAsync<AbstractClass>();
var json = schema.ToJson();
/// Act
var generator = new TypeScriptGenerator(schema, new TypeScriptGeneratorSettings { TypeScriptVersion = 2.0m });
var code = generator.GenerateFile("AbstractClass");
/// Assert
Assert.Contains("export abstract class AbstractClass", code);
Assert.Contains("base: string", code);
Assert.Contains("super: string", code);
Assert.Contains("foo: string", code);
}
public class ContainerClass
@@ -53,12 +58,12 @@ public async Task When_property_is_required_and_abstract_then_it_is_not_instanti
[JsonConverter(typeof(JsonInheritanceConverter))]
public class BaseClass
{
public string Base { get; set; }
}
public class SuperClass : AbstractClass
{
public string Super { get; set; }
}
[Fact]
@@ -154,7 +154,9 @@ public async Task When_property_has_description_then_csharp_has_xml_comment()
{
//// Arrange
var schema = await JsonSchema4.FromTypeAsync<Teacher>();
schema.Properties["Class"].Description = "PropertyDesc.";
schema.ActualProperties["Class"].Description = "PropertyDesc.";
var json = schema.ToJson();
var generator = new TypeScriptGenerator(schema);
//// Act
@@ -33,7 +33,7 @@ protected ClassTemplateModelBase(TypeResolverBase resolver, JsonSchema4 schema,
public abstract string ClassName { get; }
/// <summary>Gets or sets a value indicating whether the type is abstract.</summary>
public bool IsAbstract => _schema.IsAbstract;
public bool IsAbstract => _schema.ActualTypeSchema.IsAbstract;
/// <summary>Gets the property extension data.</summary>
public IDictionary<string, object> ExtensionData => _schema.ExtensionData;
@@ -49,7 +49,7 @@ public class DerivedClassModel
{
internal DerivedClassModel(string typeName, JsonSchema4 schema, OpenApiDiscriminator discriminator, TypeResolverBase resolver)
{
var mapping = discriminator.Mapping.SingleOrDefault(m => m.Value.ActualSchema == schema.ActualSchema);
var mapping = discriminator.Mapping.SingleOrDefault(m => m.Value.ActualTypeSchema == schema.ActualTypeSchema);
ClassName = resolver.GetOrGenerateTypeName(schema, typeName);
IsAbstract = schema.ActualTypeSchema.IsAbstract;
@@ -32,7 +32,7 @@ public async Task When_converting_type_inheriting_from_dictionary_then_it_should
var data = schema.ToJson();
//// Assert
Assert.Equal(JsonObjectType.Object, schema.Type);
Assert.Equal(JsonObjectType.Object, schema.ActualTypeSchema.Type);
Assert.DoesNotContain("Foo", json);
Assert.DoesNotContain("foo", json);
}
@@ -24,8 +24,8 @@ public async Task When_exception_schema_is_generated_then_special_properties_are
var exceptionSchema = schema.InheritedSchema.ActualSchema;
//// Assert
Assert.True(schema.Properties.ContainsKey("foo"));
Assert.True(exceptionSchema.Properties.ContainsKey("InnerException"));
Assert.True(schema.ActualProperties.ContainsKey("foo"));
Assert.True(exceptionSchema.ActualProperties.ContainsKey("InnerException"));
}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
@@ -88,9 +89,9 @@ public async Task When_generating_type_with_inheritance_then_allOf_has_one_item(
var schema = await JsonSchema4.FromTypeAsync<Teacher>();
//// Assert
Assert.NotNull(schema.Properties["Class"]);
Assert.NotNull(schema.ActualProperties["Class"]);
Assert.Equal(1, schema.AllOf.Count);
Assert.Equal(2, schema.AllOf.Count);
Assert.Contains(schema.Definitions, d => d.Key == "Person");
Assert.NotNull(schema.AllOf.First().ActualSchema.Properties["Name"]);
}
@@ -298,10 +299,45 @@ public async Task Existing_non_string_property_cant_be_discriminant()
//// Arrange
//// Act
Task<JsonSchema4> getSchema() => JsonSchema4.FromTypeAsync<BaseClass_WithIntDiscriminant>();
Task<JsonSchema4> GetSchema() => JsonSchema4.FromTypeAsync<BaseClass_WithIntDiscriminant>();
//// Assert
await Assert.ThrowsAsync<InvalidOperationException>(getSchema);
await Assert.ThrowsAsync<InvalidOperationException>(GetSchema);
}
public class Foo
{
public Bar Bar { get; set; }
}
public class Bar : Dictionary<string, string>
{
public string Baz { get; set; }
}
[Fact]
public async Task When_class_inherits_from_dictionary_then_allOf_contains_base_dictionary_schema_and_actual_schema()
{
//// Arrange
var settings = new JsonSchemaGeneratorSettings
{
SchemaType = SchemaType.OpenApi3
};
//// Act
var schema = await JsonSchema4.FromTypeAsync<Foo>(settings);
var json = schema.ToJson();
//// Assert
var bar = schema.Definitions["Bar"];
Assert.Equal(2, bar.AllOf.Count);
Assert.Equal(bar.AllOf.Last(), bar.ActualTypeSchema);
Assert.Equal(bar.AllOf.First(), bar.InheritedSchema);
Assert.True(bar.AllOf.First().IsDictionary); // base class (dictionary)
Assert.True(bar.AllOf.Last().ActualProperties.Any()); // actual class
}
}
}
@@ -143,15 +143,17 @@ public virtual async Task GenerateAsync<TSchemaType>(Type type, IEnumerable<Attr
else
{
if (schemaResolver.HasSchema(type, false))
{
schema.Reference = schemaResolver.GetSchema(type, false);
}
else if (schema.GetType() == typeof(JsonSchema4))
{
typeDescription.ApplyType(schema);
schema.Description = await type.GetTypeInfo().GetDescriptionAsync(type.GetTypeInfo().GetCustomAttributes()).ConfigureAwait(false);
await GenerateObjectAsync(type, schema, schemaResolver).ConfigureAwait(false);
await GenerateObjectAsync(type, typeDescription, schema, schemaResolver).ConfigureAwait(false);
}
else
{
schema.Reference = await GenerateAsync(type, parentAttributes, schemaResolver).ConfigureAwait(false);
}
}
}
else if (typeDescription.IsEnum)
@@ -238,15 +240,23 @@ public virtual async Task GenerateAsync<TSchemaType>(Type type, IEnumerable<Attr
else
schema.Type = schema.Type | JsonObjectType.Null;
}
else if (Settings.SchemaType == SchemaType.OpenApi3)
{
schema.IsNullableRaw = isNullable;
}
}
return schema;
}
else
else // TODO: Is this else needed?
{
referencedSchema = schema.ActualSchema;
}
}
else
{
referencedSchema = await GenerateAsync<JsonSchema4>(type, parentAttributes, schemaResolver).ConfigureAwait(false);
}
var referencingSchema = new TSchemaType();
if (transformation != null)
@@ -311,21 +321,34 @@ public virtual string GetPropertyName(Newtonsoft.Json.Serialization.JsonProperty
}
/// <summary>Generates the properties for the given type and schema.</summary>
/// <typeparam name="TSchemaType">The type of the schema type.</typeparam>
/// <param name="type">The types.</param>
/// <param name="typeDescription">The type description.</param>
/// <param name="schema">The properties</param>
/// <param name="schemaResolver">The schema resolver.</param>
/// <returns>The task.</returns>
protected virtual async Task GenerateObjectAsync<TSchemaType>(
Type type, TSchemaType schema, JsonSchemaResolver schemaResolver)
where TSchemaType : JsonSchema4, new()
protected virtual async Task GenerateObjectAsync(Type type,
JsonTypeDescription typeDescription, JsonSchema4 schema, JsonSchemaResolver schemaResolver)
{
schemaResolver.AddSchema(type, false, schema);
var rootSchema = schema;
var actualSchema = await GenerateInheritanceAsync(type, schema, schemaResolver).ConfigureAwait(false);
if (actualSchema != null)
{
schema = actualSchema;
}
else
{
await GeneratePropertiesAsync(type, schema, schemaResolver).ConfigureAwait(false);
await ApplyAdditionalPropertiesAsync(type, schema, schemaResolver).ConfigureAwait(false);
}
typeDescription.ApplyType(schema);
schema.Description = await type.GetTypeInfo().GetDescriptionAsync(type.GetTypeInfo().GetCustomAttributes()).ConfigureAwait(false);
schema.IsAbstract = type.GetTypeInfo().IsAbstract;
await GeneratePropertiesAndInheritanceAsync(type, schema, schemaResolver).ConfigureAwait(false);
await ApplyAdditionalPropertiesAsync(type, schema, schemaResolver).ConfigureAwait(false);
GenerateInheritanceDiscriminator(type, rootSchema);
if (Settings.GenerateKnownTypes)
await GenerateKnownTypesAsync(type, schemaResolver).ConfigureAwait(false);
@@ -510,7 +533,7 @@ private async Task GenerateDictionaryAsync<TSchemaType>(TSchemaType schema, Type
schema.AllowAdditionalProperties = true;
}
private async Task GeneratePropertiesAndInheritanceAsync(Type type, JsonSchema4 schema, JsonSchemaResolver schemaResolver)
private async Task GeneratePropertiesAsync(Type type, JsonSchema4 schema, JsonSchemaResolver schemaResolver)
{
#if !LEGACY
var propertiesAndFields = type.GetTypeInfo()
@@ -603,8 +626,6 @@ private async Task GeneratePropertiesAndInheritanceAsync(Type type, JsonSchema4
await LoadPropertyOrFieldAsync(property, info, type, schema, schemaResolver).ConfigureAwait(false);
}
}
await GenerateInheritanceAsync(type, schema, schemaResolver).ConfigureAwait(false);
}
/// <summary>Gets the properties of the given type or null to take all properties.</summary>
@@ -656,7 +677,7 @@ private async Task AddKnownTypeAsync(Type type, JsonSchemaResolver schemaResolve
await GenerateAsync(type, schemaResolver).ConfigureAwait(false);
}
private async Task GenerateInheritanceAsync(Type type, JsonSchema4 schema, JsonSchemaResolver schemaResolver)
private async Task<JsonSchema4> GenerateInheritanceAsync(Type type, JsonSchema4 schema, JsonSchemaResolver schemaResolver)
{
var baseType = type.GetTypeInfo().BaseType;
if (baseType != null && baseType != typeof(object) && baseType != typeof(ValueType))
@@ -669,10 +690,19 @@ private async Task GenerateInheritanceAsync(Type type, JsonSchema4 schema, JsonS
{
var typeDescription = Settings.ReflectionService.GetDescription(baseType, null, Settings);
if (!typeDescription.IsDictionary && !type.IsArray)
await GeneratePropertiesAndInheritanceAsync(baseType, schema, schemaResolver).ConfigureAwait(false);
{
await GeneratePropertiesAsync(baseType, schema, schemaResolver).ConfigureAwait(false);
await GenerateInheritanceAsync(baseType, schema, schemaResolver).ConfigureAwait(false);
GenerateInheritanceDiscriminator(baseType, schema);
}
}
else
{
var actualSchema = new JsonSchema4();
await GeneratePropertiesAsync(type, actualSchema, schemaResolver).ConfigureAwait(false);
await ApplyAdditionalPropertiesAsync(type, actualSchema, schemaResolver).ConfigureAwait(false);
var baseSchema = await GenerateAsync(baseType, schemaResolver).ConfigureAwait(false);
var baseTypeInfo = Settings.ReflectionService.GetDescription(baseType, null, Settings);
if (baseTypeInfo.RequiresSchemaReference(Settings.TypeMappers))
@@ -687,6 +717,9 @@ private async Task GenerateInheritanceAsync(Type type, JsonSchema4 schema, JsonS
}
else
schema.AllOf.Add(baseSchema);
schema.AllOf.Add(actualSchema);
return actualSchema;
}
}
}
@@ -700,12 +733,18 @@ private async Task GenerateInheritanceAsync(Type type, JsonSchema4 schema, JsonS
#endif
{
var typeDescription = Settings.ReflectionService.GetDescription(i, null, Settings);
if (!typeDescription.IsDictionary && !type.IsArray && !typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(i.GetTypeInfo()))
await GeneratePropertiesAndInheritanceAsync(i, schema, schemaResolver).ConfigureAwait(false);
if (!typeDescription.IsDictionary && !type.IsArray &&
!typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(i.GetTypeInfo()))
{
await GeneratePropertiesAsync(i, schema, schemaResolver).ConfigureAwait(false);
await GenerateInheritanceAsync(i, schema, schemaResolver).ConfigureAwait(false);
GenerateInheritanceDiscriminator(i, schema);
}
}
}
GenerateInheritanceDiscriminator(type, schema);
return null;
}
private void GenerateInheritanceDiscriminator(Type type, JsonSchema4 schema)
@@ -1002,15 +1041,15 @@ private void ApplyRangeAttribute(JsonSchema4 schema, IEnumerable<Attribute> pare
{
if (rangeAttribute.OperandType == typeof(double))
{
var minimum = (double) Convert.ChangeType(rangeAttribute.Minimum, typeof(double));
var minimum = (double)Convert.ChangeType(rangeAttribute.Minimum, typeof(double));
if (minimum > double.MinValue)
{
schema.Minimum = (decimal) minimum;
schema.Minimum = (decimal)minimum;
}
}
else
{
var minimum = (decimal) Convert.ChangeType(rangeAttribute.Minimum, typeof(decimal));
var minimum = (decimal)Convert.ChangeType(rangeAttribute.Minimum, typeof(decimal));
if (minimum > decimal.MinValue)
{
schema.Minimum = minimum;
@@ -1022,15 +1061,15 @@ private void ApplyRangeAttribute(JsonSchema4 schema, IEnumerable<Attribute> pare
{
if (rangeAttribute.OperandType == typeof(double))
{
var maximum = (double) Convert.ChangeType(rangeAttribute.Maximum, typeof(double));
var maximum = (double)Convert.ChangeType(rangeAttribute.Maximum, typeof(double));
if (maximum < double.MaxValue)
{
schema.Maximum = (decimal) maximum;
schema.Maximum = (decimal)maximum;
}
}
else
{
var maximum = (decimal) Convert.ChangeType(rangeAttribute.Maximum, typeof(decimal));
var maximum = (decimal)Convert.ChangeType(rangeAttribute.Maximum, typeof(decimal));
if (maximum < decimal.MaxValue)
{
schema.Maximum = maximum;
@@ -463,7 +463,11 @@ private static string RemoveLineBreakWhiteSpaces(string documentation)
private static string GetMemberElementName(dynamic member)
{
char prefixCode;
string memberName = member is Type ? ((Type)member).FullName : (member.DeclaringType.FullName + "." + member.Name);
var memberName = member is Type memberType && !string.IsNullOrEmpty(memberType.FullName) ?
memberType.FullName :
member.DeclaringType.FullName + "." + member.Name;
switch ((string)member.MemberType.ToString())
{
case "Constructor":
Oops, something went wrong.

0 comments on commit 3a00302

Please sign in to comment.