Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

- fixes invalid inheritance cases #4668

Merged
merged 3 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added missing nullable directives for CLI generation.
- Fixed handling of nested arrays to be handled as `UntypedNode` instances [#4549](https://github.com/microsoft/kiota/issues/4549)
- Fixed `InvalidOperationException` thrown when serializing IBacked models with no changes present in the additional data in dotnet [microsoftgraph/msgraph-sdk-dotnet#2471](https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/2471).
- Fixed a bug where enums could be considered inheritance parents. [#4640](https://github.com/microsoft/kiota/issues/4640)
- Fixed `RequestConfiguration` Classes dropped in RequestBuilder methods in python [#4535](https://github.com/microsoft/kiota/issues/4535)
- Fixed incorrect optional types in method parameters in Python [#4507](https://github.com/microsoft/kiota/issues/4507)
- Changed enum parsing methods to return nil in the default case in Go [#4621](https://github.com/microsoft/kiota/issues/4621)
Expand All @@ -31,11 +32,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- 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)
- Fixed a bug where schema with multiple allOf entries would incorrectly get merged to inherit from the first entry [#4428] (https://github.com/microsoft/kiota/issues/4428)
- Fixed a bug where schema with multiple allOf entries would incorrectly get merged to inherit from the first entry [#4428](https://github.com/microsoft/kiota/issues/4428)
- Fixes constructor generation for nullable properties that are initialized as null in C#,Java and PHP
- Fixed a bug where the hash alias in typescript wasn't being generated uniformly for similar interfaces [#84](https://github.com/microsoftgraph/msgraph-beta-sdk-typescript/issues/84)
- Fixed a bug where the hash alias in typescript wasn't being generated uniformly for similar interfaces [microsoftgraph/msgraph-beta-sdk-typescript#84](https://github.com/microsoftgraph/msgraph-beta-sdk-typescript/issues/84)
- Fixes a bug where name collisions would occur in the Typescript refiner if model name also exists with the `Interface` suffix [#4382](https://github.com/microsoft/kiota/issues/4382)


## [1.14.0] - 2024-05-02

### Added
Expand Down
12 changes: 6 additions & 6 deletions src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ public static bool IsInclusiveUnion(this OpenApiSchema? schema)
public static bool IsInherited(this OpenApiSchema? schema)
{
if (schema is null) return false;
var meaningfulMemberSchemas = schema.AllOf.FlattenSchemaIfRequired(static x => x.AllOf).Where(static x => x.IsSemanticallyMeaningful()).ToArray();
var isRootSchemaMeaningful = schema.IsSemanticallyMeaningful();
var meaningfulMemberSchemas = schema.AllOf.FlattenSchemaIfRequired(static x => x.AllOf).Where(static x => x.IsSemanticallyMeaningful(ignoreEnums: true, ignoreArrays: true, ignoreType: true)).ToArray();
var isRootSchemaMeaningful = schema.IsSemanticallyMeaningful(ignoreEnums: true, ignoreArrays: true, ignoreType: true);
return meaningfulMemberSchemas.Count(static x => !string.IsNullOrEmpty(x.Reference?.Id)) == 1 &&
(meaningfulMemberSchemas.Count(static x => string.IsNullOrEmpty(x.Reference?.Id)) == 1 ||
isRootSchemaMeaningful);
Expand Down Expand Up @@ -141,13 +141,13 @@ public static bool IsComposedEnum(this OpenApiSchema schema)
return schema.AnyOf.Count(static x => !x.IsSemanticallyMeaningful(true)) == 1 && schema.AnyOf.Count(static x => x.IsEnum()) == 1 ||
schema.OneOf.Count(static x => !x.IsSemanticallyMeaningful(true)) == 1 && schema.OneOf.Count(static x => x.IsEnum()) == 1;
}
public static bool IsSemanticallyMeaningful(this OpenApiSchema schema, bool ignoreNullableObjects = false)
public static bool IsSemanticallyMeaningful(this OpenApiSchema schema, bool ignoreNullableObjects = false, bool ignoreEnums = false, bool ignoreArrays = false, bool ignoreType = false)
{
if (schema is null) return false;
return schema.HasAnyProperty() ||
schema.Enum is { Count: > 0 } ||
schema.Items != null ||
(!string.IsNullOrEmpty(schema.Type) &&
(!ignoreEnums && schema.Enum is { Count: > 0 }) ||
(!ignoreArrays && schema.Items != null) ||
(!ignoreType && !string.IsNullOrEmpty(schema.Type) &&
((ignoreNullableObjects && !"object".Equals(schema.Type, StringComparison.OrdinalIgnoreCase)) ||
!ignoreNullableObjects)) ||
!string.IsNullOrEmpty(schema.Format) ||
Expand Down
11 changes: 6 additions & 5 deletions src/Kiota.Builder/KiotaBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
#pragma warning restore CA1031
{
if (!skipErrorLog)
logger.LogCritical("error getting the API manifest: {ExceptionMessage}", ex.Message);

Check warning on line 138 in src/Kiota.Builder/KiotaBuilder.cs

View workflow job for this annotation

GitHub Actions / Build

Logging in a catch clause should pass the caught exception as a parameter. (https://rules.sonarsource.com/csharp/RSPEC-6667)
return null;
}
}
Expand Down Expand Up @@ -657,7 +657,7 @@
methodToAdd.AddParameter(new CodeParameter
{
Name = "rawUrl",
Type = new CodeType { Name = "string", IsExternal = true },

Check warning on line 660 in src/Kiota.Builder/KiotaBuilder.cs

View workflow job for this annotation

GitHub Actions / Build

Define a constant instead of using this literal 'string' 19 times. (https://rules.sonarsource.com/csharp/RSPEC-1192)
Optional = false,
Documentation = new()
{
Expand Down Expand Up @@ -771,7 +771,7 @@
IsAsync = false,
IsStatic = false,
Documentation = new(new() {
{"TypeName", new CodeType {

Check warning on line 774 in src/Kiota.Builder/KiotaBuilder.cs

View workflow job for this annotation

GitHub Actions / Build

Define a constant instead of using this literal 'TypeName' 5 times. (https://rules.sonarsource.com/csharp/RSPEC-1192)
IsExternal = false,
TypeDefinition = currentClass,
}
Expand Down Expand Up @@ -1013,7 +1013,7 @@

if (!"string".Equals(parameter.Type.Name, StringComparison.OrdinalIgnoreCase) && config.IncludeBackwardCompatible)
{ // adding a second indexer for the string version of the parameter so we keep backward compatibility
//TODO remove for v2

Check warning on line 1016 in src/Kiota.Builder/KiotaBuilder.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
var backCompatibleValue = (CodeIndexer)result[0].Clone();
backCompatibleValue.Name += "-string";
backCompatibleValue.IndexParameter.Type = DefaultIndexerParameterType;
Expand Down Expand Up @@ -1087,15 +1087,15 @@
var primitiveTypeName = (typeName?.ToLowerInvariant(), format?.ToLowerInvariant()) switch
{
("string", "base64url") => "base64url",
("file", _) => "binary",

Check warning on line 1090 in src/Kiota.Builder/KiotaBuilder.cs

View workflow job for this annotation

GitHub Actions / Build

Define a constant instead of using this literal 'binary' 6 times. (https://rules.sonarsource.com/csharp/RSPEC-1192)
("string", "duration") => "TimeSpan",
("string", "time") => "TimeOnly",
("string", "date") => "DateOnly",
("string", "date-time") => "DateTimeOffset",
("string", "uuid") => "Guid",
("string", _) => "string", // covers commonmark and html
("number", "double" or "float" or "decimal") => format.ToLowerInvariant(),

Check warning on line 1097 in src/Kiota.Builder/KiotaBuilder.cs

View workflow job for this annotation

GitHub Actions / Build

Define a constant instead of using this literal 'number' 6 times. (https://rules.sonarsource.com/csharp/RSPEC-1192)
("number" or "integer", "int8") => "sbyte",

Check warning on line 1098 in src/Kiota.Builder/KiotaBuilder.cs

View workflow job for this annotation

GitHub Actions / Build

Define a constant instead of using this literal 'integer' 6 times. (https://rules.sonarsource.com/csharp/RSPEC-1192)
("number" or "integer", "uint8") => "byte",
("number" or "integer", "int64") => "int64",
("number", "int32") => "integer",
Expand Down Expand Up @@ -1167,7 +1167,7 @@
var suffix = $"{operationType}Response";
var modelType = CreateModelDeclarations(currentNode, schema, operation, parentClass, suffix);
if (modelType is not null && config.IncludeBackwardCompatible && config.Language is GenerationLanguage.CSharp or GenerationLanguage.Go && modelType.Name.EndsWith(suffix, StringComparison.Ordinal))
{ //TODO remove for v2

Check warning on line 1170 in src/Kiota.Builder/KiotaBuilder.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
var obsoleteTypeName = modelType.Name[..^suffix.Length] + "Response";
if (modelType is CodeType codeType &&
codeType.TypeDefinition is CodeClass codeClass)
Expand Down Expand Up @@ -1300,7 +1300,7 @@
executorMethod.AddParameter(cancellationParam);// Add cancellation token parameter

if (returnTypes.Item2 is not null && config.IncludeBackwardCompatible)
{ //TODO remove for v2

Check warning on line 1303 in src/Kiota.Builder/KiotaBuilder.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
var additionalExecutorMethod = (CodeMethod)executorMethod.Clone();
additionalExecutorMethod.ReturnType = returnTypes.Item2;
additionalExecutorMethod.OriginalMethod = executorMethod;
Expand Down Expand Up @@ -1588,9 +1588,12 @@
(false, null, not null) => AddModelDeclarationIfDoesntExist(currentNode, referencedSchema, className, shortestNamespace),
// empty schema + inline schema
(false, not null, null) => AddModelDeclarationIfDoesntExist(currentNode, inlineSchema, className, shortestNamespace),
// meaningless scenarios
// too much information but we can make a choice -> maps to properties + inline schema
(true, not null, not null) when inlineSchema.HasAnyProperty() => AddModelDeclarationIfDoesntExist(currentNode, schema, className, shortestNamespace, CreateInheritedModelDeclaration(currentNode, inlineSchema, operation, classNameSuffix, codeNamespace, isRequestBody, typeNameForInlineSchema)),
// too much information but we can make a choice -> maps to properties + referenced schema
(true, not null, not null) when referencedSchema.HasAnyProperty() => AddModelDeclarationIfDoesntExist(currentNode, schema, className, shortestNamespace, CreateInheritedModelDeclaration(currentNode, referencedSchema, operation, classNameSuffix, codeNamespace, isRequestBody, string.Empty)),
// meaningless scenario
(false, null, null) or (true, not null, not null) => throw new InvalidOperationException("invalid inheritance case"),

};
if (codeDeclaration is not CodeClass currentClass) throw new InvalidOperationException("Inheritance is only supported for classes");
if (!currentClass.Documentation.DescriptionAvailable &&
Expand Down Expand Up @@ -1894,9 +1897,7 @@
}

var mappings = GetDiscriminatorMappings(currentNode, schema, currentNamespace, newClass)
.Where(x => x.Value is CodeType type &&
type.TypeDefinition != null &&
type.TypeDefinition is CodeClass definition &&
.Where(x => x.Value is { TypeDefinition: CodeClass definition } &&
definition.DerivesFrom(newClass)); // only the mappings that derive from the current class

AddDiscriminatorMethod(newClass, schema.GetDiscriminatorPropertyName(), mappings, static s => s);
Expand Down Expand Up @@ -2338,7 +2339,7 @@
if (!parameterClass.ContainsPropertyWithWireName(prop.WireName))
{
if (addBackwardCompatibleParameter && config.IncludeBackwardCompatible && config.Language is GenerationLanguage.CSharp or GenerationLanguage.Go)
{ //TODO remove for v2

Check warning on line 2342 in src/Kiota.Builder/KiotaBuilder.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
var modernProp = (CodeProperty)prop.Clone();
modernProp.Name = $"{prop.Name}As{modernProp.Type.Name.ToFirstCharacterUpperCase()}";
modernProp.SerializationName = prop.WireName;
Expand Down
41 changes: 22 additions & 19 deletions tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3983,7 +3983,7 @@ public void IntersectionOfInlineSchemasWorks()
[Fact]
public void InheritedTypeWithInlineSchemaWorks()
{
var baseObjet = new OpenApiSchema
var baseObject = new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema> {
Expand Down Expand Up @@ -4014,12 +4014,13 @@ public void InheritedTypeWithInlineSchemaWorks()
},
UnresolvedReference = false
};
var derivedObjet = new OpenApiSchema
var derivedObject = new OpenApiSchema
{
Type = "object",
AllOf = new List<OpenApiSchema> {
baseObjet,
new OpenApiSchema {
AllOf = [
baseObject,
new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema> {
{
Expand All @@ -4028,7 +4029,8 @@ public void InheritedTypeWithInlineSchemaWorks()
}
}
},
Discriminator = new OpenApiDiscriminator {
Discriminator = new OpenApiDiscriminator
{
PropertyName = "kind",
Mapping = new Dictionary<string, string> {
{
Expand All @@ -4037,7 +4039,7 @@ public void InheritedTypeWithInlineSchemaWorks()
}
},
}
},
],
Reference = new OpenApiReference
{
Id = "subNS.derivedObject",
Expand All @@ -4048,9 +4050,10 @@ public void InheritedTypeWithInlineSchemaWorks()
var secondLevelDerivedObject = new OpenApiSchema
{
Type = "object",
AllOf = new List<OpenApiSchema> {
derivedObjet,
new OpenApiSchema {
AllOf = [
derivedObject,
new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema> {
{
Expand All @@ -4060,7 +4063,7 @@ public void InheritedTypeWithInlineSchemaWorks()
}
}
}
},
],
Reference = new OpenApiReference
{
Id = "subNS.secondLevelDerivedObject",
Expand All @@ -4082,7 +4085,7 @@ public void InheritedTypeWithInlineSchemaWorks()
["200"] = new OpenApiResponse {
Content = {
["application/json"] = new OpenApiMediaType {
Schema = derivedObjet
Schema = derivedObject
}
}
},
Expand All @@ -4095,10 +4098,10 @@ public void InheritedTypeWithInlineSchemaWorks()
{
Schemas = new Dictionary<string, OpenApiSchema> {
{
"subNS.baseObject", baseObjet
"subNS.baseObject", baseObject
},
{
"subNS.derivedObject", derivedObjet
"subNS.derivedObject", derivedObject
},
{
"subNS.secondLevelDerivedObject", secondLevelDerivedObject
Expand All @@ -4119,12 +4122,12 @@ public void InheritedTypeWithInlineSchemaWorks()
var executorReturnType = requestExecutorMethod.ReturnType as CodeType;
Assert.NotNull(executorReturnType);
Assert.Contains("derivedObject", requestExecutorMethod.ReturnType.Name);
var secondLevelDerivedClass = codeModel.FindChildByName<CodeClass>("derivedObject");
Assert.NotNull(secondLevelDerivedObject);
var factoryMethod = secondLevelDerivedClass.GetChildElements(true).OfType<CodeMethod>().FirstOrDefault(x => x.IsOfKind(CodeMethodKind.Factory));
var derivedObjectClass = codeModel.FindChildByName<CodeClass>("derivedObject");
Assert.NotNull(derivedObjectClass);
var factoryMethod = derivedObjectClass.GetChildElements(true).OfType<CodeMethod>().FirstOrDefault(x => x.IsOfKind(CodeMethodKind.Factory));
Assert.NotNull(factoryMethod);
Assert.Equal("kind", secondLevelDerivedClass.DiscriminatorInformation.DiscriminatorPropertyName);
Assert.NotEmpty(secondLevelDerivedClass.DiscriminatorInformation.DiscriminatorMappings);
Assert.Equal("kind", derivedObjectClass.DiscriminatorInformation.DiscriminatorPropertyName);
Assert.NotEmpty(derivedObjectClass.DiscriminatorInformation.DiscriminatorMappings);
}
[InlineData("string", "", "string")]// https://spec.openapis.org/registry/format/
[InlineData("string", "commonmark", "string")]
Expand Down
Loading