From c94b76e08ce417c7f96e67d58561006588c02c39 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 21 Sep 2023 14:16:28 -0400 Subject: [PATCH] - fixes race condition with composed type generation Signed-off-by: Vincent Biret --- it/config.json | 6 +++++- .../CodeDOM/CodeComposedTypeBase.cs | 19 +++++++++++++++---- src/Kiota.Builder/KiotaBuilder.cs | 16 ++++++++-------- .../Kiota.Builder.Tests/KiotaBuilderTests.cs | 4 ++-- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/it/config.json b/it/config.json index 0231bdd269..8e654b74f0 100644 --- a/it/config.json +++ b/it/config.json @@ -258,6 +258,10 @@ } ], "IdempotencySuppressions": [ + { + "Language": "csharp", + "Rationale": "https://github.com/microsoft/kiota/issues/2952" + }, { "Language": "go", "Rationale": "https://github.com/microsoft/kiota/issues/2834" @@ -326,4 +330,4 @@ } ] } -} +} \ No newline at end of file diff --git a/src/Kiota.Builder/CodeDOM/CodeComposedTypeBase.cs b/src/Kiota.Builder/CodeDOM/CodeComposedTypeBase.cs index c885ce5c93..f7d5e291c0 100644 --- a/src/Kiota.Builder/CodeDOM/CodeComposedTypeBase.cs +++ b/src/Kiota.Builder/CodeDOM/CodeComposedTypeBase.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -8,16 +9,26 @@ namespace Kiota.Builder.CodeDOM; /// public abstract class CodeComposedTypeBase : CodeTypeBase, IDiscriminatorInformationHolder { + private static string NormalizeKey(CodeType codeType) => $"{codeType.Name}_{codeType.CollectionKind}"; public void AddType(params CodeType[] codeTypes) { + ArgumentNullException.ThrowIfNull(codeTypes); + if (codeTypes.Any(x => x == null)) + throw new ArgumentNullException(nameof(codeTypes), "One of the provided types was null"); EnsureElementsAreChildren(codeTypes); - foreach (var codeType in codeTypes.Where(x => x != null && !Types.Contains(x))) - types.Add(codeType); + foreach (var codeType in codeTypes) + if (!types.TryAdd(NormalizeKey(codeType), codeType)) + throw new InvalidOperationException($"The type {codeType.Name} was already added"); } - private readonly List types = new(); + public bool ContainsType(CodeType codeType) + { + ArgumentNullException.ThrowIfNull(codeType); + return types.ContainsKey(NormalizeKey(codeType)); + } + private readonly ConcurrentDictionary types = new(StringComparer.OrdinalIgnoreCase); public IEnumerable Types { - get => types; + get => types.Values.OrderBy(NormalizeKey, StringComparer.OrdinalIgnoreCase); } private DiscriminatorInformation? _discriminatorInformation; /// diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index 6ff302cd4e..e4d1bfba5b 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -1654,16 +1654,18 @@ private CodeTypeBase CreateComposedModelDeclaration(OpenApiUrlTreeNode currentNo if (string.IsNullOrEmpty(className)) if (GetPrimitiveType(currentSchema) is CodeType primitiveType && !string.IsNullOrEmpty(primitiveType.Name)) { - unionType.AddType(primitiveType); + if (!unionType.ContainsType(primitiveType)) + unionType.AddType(primitiveType); continue; } else className = $"{unionType.Name}Member{++membersWithNoName}"; - var codeDeclaration = AddModelDeclarationIfDoesntExist(currentNode, currentSchema, className, shortestNamespace); - unionType.AddType(new CodeType + var declarationType = new CodeType { - TypeDefinition = codeDeclaration, - }); + TypeDefinition = AddModelDeclarationIfDoesntExist(currentNode, currentSchema, className, shortestNamespace), + }; + if (!unionType.ContainsType(declarationType)) + unionType.AddType(declarationType); } return unionType; } @@ -2067,9 +2069,7 @@ private void CreatePropertiesForModelClass(OpenApiUrlTreeNode currentNode, OpenA .Select(x => { var propertySchema = x.Value; - var className = propertySchema.GetSchemaName().CleanupSymbolName(); - if (string.IsNullOrEmpty(className)) - className = $"{model.Name}_{x.Key.CleanupSymbolName()}"; + var className = $"{model.Name}_{x.Key.CleanupSymbolName()}"; var shortestNamespaceName = GetModelsNamespaceNameFromReferenceId(propertySchema.Reference?.Id); var targetNamespace = string.IsNullOrEmpty(shortestNamespaceName) ? ns : rootNamespace?.FindOrAddNamespace(shortestNamespaceName) ?? ns; diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs index fa592bef7c..457127d7ac 100644 --- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs @@ -336,8 +336,8 @@ public async Task NamesComponentsInlineSchemasProperly() Assert.Equal("directoryObject_deletedDateTime", unionType.Name, StringComparer.OrdinalIgnoreCase); Assert.Equal(3, unionType.Types.Count()); Assert.Equal("DateTimeOffset", unionType.Types.First().Name, StringComparer.OrdinalIgnoreCase); - Assert.Equal("int64", unionType.Types.ElementAt(1).Name, StringComparer.OrdinalIgnoreCase); - Assert.Equal("directoryObject_deletedDateTimeMember1", unionType.Types.ElementAt(2).Name, StringComparer.OrdinalIgnoreCase); + Assert.Equal("directoryObject_deletedDateTimeMember1", unionType.Types.ElementAt(1).Name, StringComparer.OrdinalIgnoreCase); + Assert.Equal("int64", unionType.Types.ElementAt(2).Name, StringComparer.OrdinalIgnoreCase); Assert.Null(modelsNS.FindChildByName("users")); } [Theory]