diff --git a/src/Kiota.Builder/Refiners/GoRefiner.cs b/src/Kiota.Builder/Refiners/GoRefiner.cs index 85803c82f7..47eb185b4f 100644 --- a/src/Kiota.Builder/Refiners/GoRefiner.cs +++ b/src/Kiota.Builder/Refiners/GoRefiner.cs @@ -196,6 +196,7 @@ public override Task RefineAsync(CodeNamespace generatedCode, CancellationToken generatedCode ); cancellationToken.ThrowIfCancellationRequested(); + CorrectCyclicReference(generatedCode); CopyModelClassesAsInterfaces( generatedCode, x => $"{x.Name}able" @@ -214,7 +215,6 @@ public override Task RefineAsync(CodeNamespace generatedCode, CancellationToken "Error", () => new CodeType { Name = "string", IsNullable = false, IsExternal = true } ); - CorrectCyclicReference(generatedCode); GenerateCodeFiles(generatedCode); }, cancellationToken); } @@ -240,116 +240,19 @@ private void CorrectCyclicReference(CodeElement currentElement) { var dupNS = cycleReference[^2]; // 2nd last element is target base namespace var nameSpace = modelsNameSpace.FindNamespaceByName(dupNS, true); - var rootNameSpace = cycle.Key.Equals(modelsNameSpace.Name, StringComparison.OrdinalIgnoreCase) - ? modelsNameSpace - : modelsNameSpace.FindNamespaceByName(cycle.Key, true); - migratedNamespaces[dupNS] = cycle.Key; - MigrateNameSpace(nameSpace!, rootNameSpace!); - } - } - - CorrectReferencesToMigratedModels(currentElement, migratedNamespaces); - } - private void CorrectReferencesToMigratedModels(CodeElement currentElement, Dictionary migratedNamespaces) - { - if (currentElement is CodeNamespace cn) - { - var usings = cn.GetChildElements() - .SelectMany(static x => x.GetChildElements(false)) - .OfType() - .SelectMany(static x => x.Usings) - .Where(x => migratedNamespaces.ContainsKey(x.Name)) - .ToArray(); + migratedNamespaces[dupNS] = modelsNameSpace.Name; + MigrateNameSpace(nameSpace!, modelsNameSpace); - foreach (var codeUsing in usings) - { - if (codeUsing.Parent is not ProprietableBlockDeclaration blockDeclaration || - codeUsing.Declaration?.TypeDefinition?.GetImmediateParentOfType().Name != - migratedNamespaces[codeUsing.Name]) + if (!cycle.Key.Equals(modelsNameSpace.Name, StringComparison.OrdinalIgnoreCase) && !migratedNamespaces.ContainsKey(cycle.Key)) { - continue; + migratedNamespaces[cycle.Key] = modelsNameSpace.Name; + MigrateNameSpace(modelsNameSpace.FindNamespaceByName(cycle.Key, true)!, modelsNameSpace); } - - blockDeclaration.RemoveUsings(codeUsing); - blockDeclaration.AddUsings(new CodeUsing - { - Name = migratedNamespaces[codeUsing.Name], - Declaration = codeUsing.Declaration - }); } } - CrawlTree(currentElement, x => CorrectReferencesToMigratedModels(x, migratedNamespaces)); - } - - private void MigrateReferencesToMigratedModels1(CodeElement currentElement, Dictionary migratedNamespaces) - { - if (currentElement is ProprietableBlockDeclaration bd) - { - if (bd.Usings.Any(x => migratedNamespaces.ContainsKey(x.Name))) - { - foreach (var usingStatement in bd.Usings) - { - if (migratedNamespaces.TryGetValue(usingStatement.Name, out var newName)) - { - bd.RemoveUsingsByDeclarationName(usingStatement.Name); - bd.AddUsings(new CodeUsing - { - Name = usingStatement.Declaration?.TypeDefinition?.Name!, - Declaration = new CodeType - { - Name = usingStatement.Declaration?.TypeDefinition?.Name!, - TypeDefinition = usingStatement.Declaration?.TypeDefinition, - } - }); - } - } - } - } - - CrawlTree(currentElement, x => MigrateReferencesToMigratedModels1(x, migratedNamespaces)); - } - - private void MigrateNameSpace(CodeNamespace currentNameSpace, CodeNamespace targetNameSpace) - { - currentNameSpace.Classes.ToList().ForEach(x => - { - currentNameSpace.RemoveChildElement(x); - x.Name = GetComposedName(x); - x.Parent = targetNameSpace; - targetNameSpace.AddClass(x); - }); - currentNameSpace.Enums.ToList().ForEach(x => - { - currentNameSpace.RemoveChildElement(x); - x.Name = GetComposedName(x); - x.Parent = targetNameSpace; - targetNameSpace.AddEnum(x); - }); - currentNameSpace.Interfaces.ToList().ForEach(x => - { - currentNameSpace.RemoveChildElement(x); - x.Name = GetComposedName(x); - x.Parent = targetNameSpace; - targetNameSpace.AddInterface(x); - }); - currentNameSpace.Functions.ToList().ForEach(x => - { - currentNameSpace.RemoveChildElement(x); - x.Name = GetComposedName(x); - x.Parent = targetNameSpace; - targetNameSpace.AddFunction(x); - }); - currentNameSpace.Constants.ToList().ForEach(x => - { - currentNameSpace.RemoveChildElement(x); - x.Name = GetComposedName(x); - x.Parent = targetNameSpace; - targetNameSpace.AddConstant(x); - }); - - currentNameSpace.Namespaces.ToList().ForEach(x => MigrateNameSpace(x, targetNameSpace)); + CorrectReferencesToMigratedModels(currentElement, migratedNamespaces); } private string GetComposedName(CodeElement codeClass) @@ -431,6 +334,77 @@ static void SearchCycles(string parentNode, string node, Dictionary + { + currentNameSpace.RemoveChildElement(x); + x.Name = GetComposedName(x); + x.Parent = targetNameSpace; + targetNameSpace.AddClass(x); + }); + currentNameSpace.Enums.ToList().ForEach(x => + { + currentNameSpace.RemoveChildElement(x); + x.Name = GetComposedName(x); + x.Parent = targetNameSpace; + targetNameSpace.AddEnum(x); + }); + currentNameSpace.Interfaces.ToList().ForEach(x => + { + currentNameSpace.RemoveChildElement(x); + x.Name = GetComposedName(x); + x.Parent = targetNameSpace; + targetNameSpace.AddInterface(x); + }); + currentNameSpace.Functions.ToList().ForEach(x => + { + currentNameSpace.RemoveChildElement(x); + x.Name = GetComposedName(x); + x.Parent = targetNameSpace; + targetNameSpace.AddFunction(x); + }); + currentNameSpace.Constants.ToList().ForEach(x => + { + currentNameSpace.RemoveChildElement(x); + x.Name = GetComposedName(x); + x.Parent = targetNameSpace; + targetNameSpace.AddConstant(x); + }); + + currentNameSpace.Namespaces.ToList().ForEach(x => MigrateNameSpace(x, targetNameSpace)); + } + private void CorrectReferencesToMigratedModels(CodeElement currentElement, Dictionary migratedNamespaces) + { + if (currentElement is CodeNamespace cn) + { + var usings = cn.GetChildElements() + .SelectMany(static x => x.GetChildElements(false)) + .OfType() + .SelectMany(static x => x.Usings) + .Where(x => migratedNamespaces.ContainsKey(x.Name)) + .ToArray(); + + foreach (var codeUsing in usings) + { + if (codeUsing.Parent is not ProprietableBlockDeclaration blockDeclaration || + codeUsing.Declaration?.TypeDefinition?.GetImmediateParentOfType().Name != + migratedNamespaces[codeUsing.Name]) + { + continue; + } + + blockDeclaration.RemoveUsings(codeUsing); + blockDeclaration.AddUsings(new CodeUsing + { + Name = migratedNamespaces[codeUsing.Name], + Declaration = codeUsing.Declaration + }); + } + } + + CrawlTree(currentElement, x => CorrectReferencesToMigratedModels(x, migratedNamespaces)); + } private void GenerateCodeFiles(CodeElement currentElement) { if (currentElement is CodeInterface codeInterface && currentElement.Parent is CodeNamespace codeNamespace) diff --git a/tests/Kiota.Builder.Tests/Refiners/GoLanguageRefinerTests.cs b/tests/Kiota.Builder.Tests/Refiners/GoLanguageRefinerTests.cs index 8994eb193b..995384b774 100644 --- a/tests/Kiota.Builder.Tests/Refiners/GoLanguageRefinerTests.cs +++ b/tests/Kiota.Builder.Tests/Refiners/GoLanguageRefinerTests.cs @@ -841,6 +841,53 @@ public async Task ReplacesDurationByNativeTypeAsync() Assert.NotEmpty(model.StartBlock.Usings); Assert.Equal("ISODuration", method.ReturnType.Name); } + [Fact] + public async Task CorrectesCircularDependencies() + { + var modelsNS = root.AddNamespace("ApiSdk.models"); + + var subANamespace = modelsNS.AddNamespace($"{modelsNS.Name}.suba"); + var modelA = subANamespace.AddClass(new CodeClass + { + Kind = CodeClassKind.Model, + Name = "ModelA", + }).First(); + + var subBNamespace = modelsNS.AddNamespace($"{modelsNS.Name}.subb"); + var modelB = subBNamespace.AddClass(new CodeClass + { + Kind = CodeClassKind.Model, + Name = "ModelB", + }).First(); + + modelA.StartBlock.AddUsings(new CodeUsing + { + Name = subBNamespace.Name, + Declaration = new() + { + Name = modelB.Name, + TypeDefinition = modelB, + IsExternal = false + } + }); + + modelB.StartBlock.AddUsings(new CodeUsing + { + Name = subANamespace.Name, + Declaration = new() + { + Name = modelA.Name, + TypeDefinition = modelA, + IsExternal = false + } + }); + + await ILanguageRefiner.RefineAsync(new GenerationConfiguration { Language = GenerationLanguage.Go }, root); + Assert.Equal("ApiSdk.models", modelB.GetImmediateParentOfType().Name); // migrated to root namespace + Assert.Equal("ApiSdk.models", modelA.GetImmediateParentOfType().Name); // migrated to root namespace + Assert.Equal("SubaModelA", modelA.Name); // renamed to avoid conflict + Assert.Equal("SubbModelB", modelB.Name); // renamed to avoid conflict + } #endregion #region GoRefinerTests