Skip to content

Commit

Permalink
- fixes #4074 a bug where allof would miss properties without object …
Browse files Browse the repository at this point in the history
…type

Signed-off-by: Vincent Biret <vibiret@microsoft.com>
  • Loading branch information
baywet committed May 7, 2024
1 parent 127d16d commit 6a545fd
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed incorrect optional types in method parameters in Python [#4507](https://github.com/microsoft/kiota/issues/4507)
- Fixed a bug where multiple allOf entries type would not get merged if they were part of a discriminator. [#4325](https://github.com/microsoft/kiota/issues/4325)
- Fixed a bug where allOf structure with one entry and properties would not be considered as inheritance case. [#4346](https://github.com/microsoft/kiota/issues/4346)
- Fixed a bug where some allOf scenarios would be missing properties if type object wasn't set on the schema. [#4074](https://github.com/microsoft/kiota/issues/4074)

## [1.14.0] - 2024-05-02

Expand Down
8 changes: 6 additions & 2 deletions src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,14 @@ public static bool IsArray(this OpenApiSchema? schema)
FlattenEmptyEntries([schema.Items], static x => x.AnyOf.Union(x.AllOf).Union(x.OneOf).ToList(), 1).FirstOrDefault() is OpenApiSchema flat && flat.IsSemanticallyMeaningful());
}

public static bool IsObject(this OpenApiSchema? schema)
public static bool IsObjectType(this OpenApiSchema? schema)
{
return "object".Equals(schema?.Type, StringComparison.OrdinalIgnoreCase);
}
public static bool HasAnyProperty(this OpenApiSchema? schema)
{
return schema?.Properties is { Count: > 0 };
}
public static bool IsInclusiveUnion(this OpenApiSchema? schema)
{
return schema?.AnyOf?.Count(static x => IsSemanticallyMeaningful(x, true)) > 1;
Expand Down Expand Up @@ -178,7 +182,7 @@ public static IEnumerable<string> GetSchemaReferenceIds(this OpenApiSchema schem
}
private static IEnumerable<OpenApiSchema> FlattenEmptyEntries(this IEnumerable<OpenApiSchema> schemas, Func<OpenApiSchema, IList<OpenApiSchema>> subsequentGetter, int? maxDepth = default)
{
if (schemas == null) return Enumerable.Empty<OpenApiSchema>();
if (schemas == null) return [];
ArgumentNullException.ThrowIfNull(subsequentGetter);

if ((maxDepth ?? 1) <= 0)
Expand Down
4 changes: 2 additions & 2 deletions src/Kiota.Builder/KiotaBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1698,7 +1698,7 @@ private CodeTypeBase CreateModelDeclarations(OpenApiUrlTreeNode currentNode, Ope
return CreateComposedModelDeclaration(currentNode, schema, operation, suffix, codeNamespace, isRequestBody, typeNameForInlineSchema);
}

if (schema.IsObject() || schema.Properties.Any() || schema.IsEnum() || !string.IsNullOrEmpty(schema.AdditionalProperties?.Type))
if (schema.IsObjectType() || schema.HasAnyProperty() || schema.IsEnum() || !string.IsNullOrEmpty(schema.AdditionalProperties?.Type))
{
// no inheritance or union type, often empty definitions with only additional properties are used as property bags.
return CreateModelDeclarationAndType(currentNode, schema, operation, codeNamespace, suffix, response: responseValue, typeNameForInlineSchema: typeNameForInlineSchema, isRequestBody);
Expand Down Expand Up @@ -2129,7 +2129,7 @@ private Dictionary<string, OpenApiSchema> CollectAllProperties(OpenApiSchema sch
Dictionary<string, OpenApiSchema> result = schema.Properties?.ToDictionary(static x => x.Key, static x => x.Value, StringComparer.Ordinal) ?? new(StringComparer.Ordinal);
if (schema.AllOf?.Any() ?? false)
{
foreach (var supProperty in schema.AllOf.Where(static x => x.IsObject() && !x.IsReferencedSchema() && x.Properties is not null).SelectMany(static x => x.Properties))
foreach (var supProperty in schema.AllOf.Where(static x => !x.IsReferencedSchema() && x.HasAnyProperty()).SelectMany(static x => x.Properties))
{
result.Add(supProperty.Key, supProperty.Value);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Validation/MissingDiscriminator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ private static void ValidateSchema(OpenApiSchema schema, IValidationContext cont
{
if (!schema.IsInclusiveUnion() && !schema.IsExclusiveUnion())
return;
if (schema.AnyOf.All(static x => !x.IsObject()) && schema.OneOf.All(static x => !x.IsObject()))
if (schema.AnyOf.All(static x => !x.IsObjectType()) && schema.OneOf.All(static x => !x.IsObjectType()))
return;
if (string.IsNullOrEmpty(schema.GetDiscriminatorPropertyName()) || !schema.GetDiscriminatorMappings(idx).Any())
context.CreateWarning(nameof(MissingDiscriminator), $"The schema {address} is a polymorphic type but does not define a discriminator. This will result in a serialization errors.");
Expand Down
4 changes: 2 additions & 2 deletions src/Kiota.Builder/Validation/UrlFormEncodedComplex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public UrlFormEncodedComplex() : base(nameof(UrlFormEncodedComplex), static (con
private static void ValidateSchema(OpenApiSchema schema, IValidationContext context, string operationId, string schemaName)
{
if (schema == null) return;
if (!schema.IsObject())
if (!schema.IsObjectType())
context.CreateWarning(nameof(UrlFormEncodedComplex), $"The operation {operationId} has a {schemaName} which is not an object type. This is not supported by Kiota and serialization will fail.");
if (schema.Properties.Any(static x => x.Value.IsObject()))
if (schema.Properties.Any(static x => x.Value.IsObjectType()))
context.CreateWarning(nameof(UrlFormEncodedComplex), $"The operation {operationId} has a {schemaName} with a complex properties and the url form encoded content type. This is not supported by Kiota and serialization of complex properties will fail.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,19 @@ public void Defensive()
Assert.False(OpenApiSchemaExtensions.IsInclusiveUnion(null));
Assert.False(OpenApiSchemaExtensions.IsExclusiveUnion(null));
Assert.False(OpenApiSchemaExtensions.IsArray(null));
Assert.False(OpenApiSchemaExtensions.IsObject(null));
Assert.False(OpenApiSchemaExtensions.IsObjectType(null));
Assert.False(OpenApiSchemaExtensions.HasAnyProperty(null));
Assert.False(OpenApiSchemaExtensions.IsReferencedSchema(null));
Assert.Null(OpenApiSchemaExtensions.MergeIntersectionSchemaEntries(null));

Assert.False(new OpenApiSchema { Reference = null }.IsReferencedSchema());
Assert.False(new OpenApiSchema { Type = null }.IsArray());
Assert.False(new OpenApiSchema { Type = null }.IsObject());
Assert.False(new OpenApiSchema { Type = null }.IsObjectType());
Assert.False(new OpenApiSchema { AnyOf = null }.IsInclusiveUnion());
Assert.False(new OpenApiSchema { AllOf = null }.IsInherited());
Assert.False(new OpenApiSchema { AllOf = null }.IsIntersection());
Assert.False(new OpenApiSchema { OneOf = null }.IsExclusiveUnion());
Assert.False(new OpenApiSchema { Properties = null }.HasAnyProperty());
var original = new OpenApiSchema { AllOf = null };
Assert.Equal(original, original.MergeIntersectionSchemaEntries());

Expand Down
49 changes: 49 additions & 0 deletions tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7702,6 +7702,55 @@ public async Task InheritanceWithAllOfWith3Parts1Schema2Inline(bool reverseOrder
Assert.Single(resultClass.Properties.Where(static x => x.Name.Equals("groupprop2", StringComparison.OrdinalIgnoreCase)));
}
[Fact]
public async Task InheritanceWithoutObjectTypeHasAllProperties()
{
var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
await using var fs = await GetDocumentStream(@"openapi: 3.0.3
servers:
- url: 'https://example.com'
info:
title: example
version: 0.0.1
paths:
/path:
post:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/outerPayload'
responses:
'201':
description: Created
content:
application/json:
schema:
type: string
components:
schemas:
outerPayload:
allOf:
- $ref: '#/components/schemas/innerPayload'
- properties:
someField:
type: string
innerPayload:
properties:
anotherField:
type: string");
var mockLogger = new Mock<ILogger<KiotaBuilder>>();
var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
var document = await builder.CreateOpenApiDocumentAsync(fs);
var node = builder.CreateUriSpace(document);
var codeModel = builder.CreateSourceModel(node);
var outerPayloadClass = codeModel.FindChildByName<CodeClass>("outerPayload");
Assert.NotNull(outerPayloadClass);
Assert.Equal("innerPayload", outerPayloadClass.StartBlock.Inherits?.Name, StringComparer.OrdinalIgnoreCase);
Assert.Single(outerPayloadClass.Properties);
Assert.Single(outerPayloadClass.Properties.Where(static x => x.Name.Equals("someField", StringComparison.OrdinalIgnoreCase)));
}
[Fact]
public async Task EnumsWithNullableDoesNotResultInInlineType()
{
var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
Expand Down

0 comments on commit 6a545fd

Please sign in to comment.