diff --git a/src/Kiota.Builder/CodeDOM/CodeFile.cs b/src/Kiota.Builder/CodeDOM/CodeFile.cs index c9223cc273..27e9312598 100644 --- a/src/Kiota.Builder/CodeDOM/CodeFile.cs +++ b/src/Kiota.Builder/CodeDOM/CodeFile.cs @@ -6,7 +6,12 @@ namespace Kiota.Builder.CodeDOM; public class CodeFile : CodeBlock { + public IEnumerable Namespaces => InnerChildElements.Values.OfType(); + public IEnumerable Classes => InnerChildElements.Values.OfType(); + public IEnumerable Enums => InnerChildElements.Values.OfType(); + public IEnumerable Functions => InnerChildElements.Values.OfType(); public IEnumerable Interfaces => InnerChildElements.Values.OfType(); + public IEnumerable AddElements(params T[] elements) where T : CodeElement { if (elements == null || elements.Any(static x => x == null)) diff --git a/src/Kiota.Builder/CodeDOM/CodeNamespace.cs b/src/Kiota.Builder/CodeDOM/CodeNamespace.cs index e4776d6149..55a0bb456d 100644 --- a/src/Kiota.Builder/CodeDOM/CodeNamespace.cs +++ b/src/Kiota.Builder/CodeDOM/CodeNamespace.cs @@ -169,7 +169,7 @@ public NamespaceDifferentialTracker GetDifferential(CodeNamespace importNamespac ArgumentException.ThrowIfNullOrEmpty(namespacePrefix); if (this == importNamespace || Name.Equals(importNamespace.Name, StringComparison.OrdinalIgnoreCase)) // we're in the same namespace return new(); - var prefixLength = (namespacePrefix.Length > Math.Min(Name.Length, importNamespace.Name.Length) ? 0 : namespacePrefix.Length); + var prefixLength = namespacePrefix.Length; var currentNamespaceSegments = Name[prefixLength..] .Split(separator, StringSplitOptions.RemoveEmptyEntries); var importNamespaceSegments = importNamespace diff --git a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs index a933b31189..b9da06d27c 100644 --- a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs +++ b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs @@ -184,16 +184,17 @@ private static void GenerateModelCodeFile(CodeInterface codeInterface, CodeNames if (codeFunction.OriginalLocalMethod.IsOfKind(CodeMethodKind.Deserializer, CodeMethodKind.Serializer)) { var exists = codeFunction.OriginalLocalMethod.Parameters - .Any(x => x?.Type?.Name == codeInterface.Name); + .Any(x => x?.Type?.Name?.Equals(codeInterface.Name, StringComparison.OrdinalIgnoreCase) ?? false); if (exists) functions.Add(codeFunction); } - else if (codeFunction.OriginalLocalMethod.IsOfKind(CodeMethodKind.Factory)) + else if (codeFunction.OriginalLocalMethod.IsOfKind(CodeMethodKind.Factory) && + codeInterface.Name.EqualsIgnoreCase(codeFunction.OriginalMethodParentClass.Name) && + codeFunction.OriginalMethodParentClass.IsChildOf(codeNamespace)) { - if (codeInterface.Name.EqualsIgnoreCase(codeFunction.OriginalMethodParentClass.Name) && codeFunction.OriginalMethodParentClass.IsChildOf(codeNamespace)) - functions.Add(codeFunction); + functions.Add(codeFunction); } } } @@ -208,24 +209,24 @@ private static void GenerateRequestBuilderCodeFile(CodeClass codeClass, CodeName .SelectMany(static x => x.Parameters) .Where(static x => x.IsOfKind(CodeParameterKind.RequestConfiguration)) .Select(static x => x?.Type?.Name) - .Where(static x => x != null) - .Select(static x => x!) + .Where(static x => !string.IsNullOrEmpty(x)) + .OfType() .ToList(); List configClasses = codeNamespace.FindChildrenByName(elementNames, false) .Where(static x => x != null) - .Select(static x => x!) + .OfType() .ToList(); List queryParamClassNames = configClasses.Where(x => x != null) .Select(static w => w.GetPropertyOfKind(CodePropertyKind.QueryParameters)?.Type?.Name) - .Where(static s => s != null) - .Select(static s => s!) + .Where(static x => !string.IsNullOrEmpty(x)) + .OfType() .ToList(); List queryParamClasses = codeNamespace.FindChildrenByName(queryParamClassNames, false) .Where(static x => x != null) - .Select(static x => x!) + .OfType() .ToList(); List elements = new List { codeClass }; diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeClassDeclarationWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeClassDeclarationWriter.cs index bba31ba362..06c318ff7e 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeClassDeclarationWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeClassDeclarationWriter.cs @@ -20,7 +20,7 @@ public override void WriteCodeElement(ClassDeclaration codeElement, LanguageWrit conventions.WriteAutoGeneratedStart(writer); var parentNamespace = codeElement.GetImmediateParentOfType(); - if (codeElement.Parent?.Parent is not CodeFile) + if (codeElement.Parent?.Parent is CodeNamespace) _codeUsingWriter.WriteCodeElement(codeElement.Usings, parentNamespace, writer); var inheritSymbol = codeElement.Inherits is null ? string.Empty : conventions.GetTypeString(codeElement.Inherits, codeElement); diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeFileDeclarationWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeFileDeclarationWriter.cs index 21b147492f..efb201dfe8 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeFileDeclarationWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeFileDeclarationWriter.cs @@ -6,7 +6,11 @@ namespace Kiota.Builder.Writers.TypeScript; public class CodeFileDeclarationWriter : BaseElementWriter { - public CodeFileDeclarationWriter(TypeScriptConventionService conventionService) : base(conventionService) { } + private readonly CodeUsingWriter _codeUsingWriter; + public CodeFileDeclarationWriter(TypeScriptConventionService conventionService, string clientNamespaceName) : base(conventionService) + { + _codeUsingWriter = new(clientNamespaceName); + } public override void WriteCodeElement(CodeFileDeclaration codeElement, LanguageWriter writer) { @@ -15,7 +19,6 @@ public override void WriteCodeElement(CodeFileDeclaration codeElement, LanguageW if (codeElement.Parent is CodeFile cf && cf.Parent is CodeNamespace ns) { - CodeUsingWriter codeUsingWriter = new(ns.Name); var usings = cf.GetChildElements().SelectMany(static x => { var startBlockUsings = x switch @@ -28,7 +31,15 @@ public override void WriteCodeElement(CodeFileDeclaration codeElement, LanguageW return startBlockUsings; } ); - codeUsingWriter.WriteCodeElement(usings, ns, writer); + try + { + _codeUsingWriter.WriteCodeElement(usings, ns, writer); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } } } diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs index 170950e5d7..157ba88a85 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs @@ -25,12 +25,12 @@ public override void WriteCodeElement(CodeFunction codeElement, LanguageWriter w if (codeElement.Parent is not CodeNamespace && codeElement.Parent is not CodeFile) throw new InvalidOperationException("the parent of a function should be a namespace or file"); + conventions.WriteAutoGeneratedStart(writer); if (codeElement.Parent is CodeNamespace) { - conventions.WriteAutoGeneratedStart(writer); _codeUsingWriter.WriteCodeElement(codeElement.StartBlock.Usings, codeElement.GetImmediateParentOfType(), writer); } - + var codeMethod = codeElement.OriginalLocalMethod; var returnType = codeMethod.Kind != CodeMethodKind.Factory ? conventions.GetTypeString(codeMethod.ReturnType, codeElement) : string.Empty; @@ -92,12 +92,8 @@ private void WriteFactoryMethodBody(CodeFunction codeElement, string returnType, private string getDeserializationFunction(CodeElement codeElement, string returnType) { - var parent = codeElement.Parent switch - { - CodeNamespace codeNamespace => codeNamespace.FindChildByName($"deserializeInto{returnType}"), - CodeFile codeFile => codeFile.FindChildByName($"deserializeInto{returnType}"), - _ => throw new InvalidOperationException($"{codeElement.Name} does not have a parent namespace or file") - }; + CodeNamespace codeNamespace = codeElement.GetImmediateParentOfType(); + CodeFunction parent = codeNamespace.FindChildByName($"deserializeInto{returnType}")!; return conventions.GetTypeString(new CodeType { TypeDefinition = parent }, codeElement, false); } diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeInterfaceDeclarationWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeInterfaceDeclarationWriter.cs index efbbd4bec5..c713e78e52 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeInterfaceDeclarationWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeInterfaceDeclarationWriter.cs @@ -17,7 +17,7 @@ public override void WriteCodeElement(InterfaceDeclaration codeElement, Language { ArgumentNullException.ThrowIfNull(codeElement); ArgumentNullException.ThrowIfNull(writer); - + var parentNamespace = codeElement.GetImmediateParentOfType(); if (codeElement.Parent?.Parent is CodeNamespace) { diff --git a/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs b/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs index 0e0c0a7d0f..4984b1ab56 100644 --- a/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs @@ -17,6 +17,6 @@ public TypeScriptWriter(string rootPath, string clientNamespaceName, bool usesBa AddOrReplaceCodeElementWriter(new CodeTypeWriter(conventionService)); AddOrReplaceCodeElementWriter(new CodeNameSpaceWriter(conventionService)); AddOrReplaceCodeElementWriter(new CodeInterfaceDeclarationWriter(conventionService, clientNamespaceName)); - AddOrReplaceCodeElementWriter(new CodeFileDeclarationWriter(conventionService)); + AddOrReplaceCodeElementWriter(new CodeFileDeclarationWriter(conventionService, clientNamespaceName)); } } diff --git a/tests/Kiota.Builder.Tests/CodeDOM/CodeNamespaceTests.cs b/tests/Kiota.Builder.Tests/CodeDOM/CodeNamespaceTests.cs index b4eaf8e1c2..763b4300dd 100644 --- a/tests/Kiota.Builder.Tests/CodeDOM/CodeNamespaceTests.cs +++ b/tests/Kiota.Builder.Tests/CodeDOM/CodeNamespaceTests.cs @@ -139,16 +139,4 @@ public void IsParentOf() Assert.False(grandchild.IsParentOf(child)); } - [Fact] - public void GetDifferential() - { - - var root = CodeNamespace.InitRootNamespace(); - root.Name = "Nyt.model.item"; - var child1 = root.AddNamespace("Nyt.model.item"); - var child2 = root.AddNamespace("Nyt.model"); - - - child1.GetDifferential(child2, child1.Name, '.'); - } } diff --git a/tests/Kiota.Builder.Tests/Refiners/TypeScriptLanguageRefinerTests.cs b/tests/Kiota.Builder.Tests/Refiners/TypeScriptLanguageRefinerTests.cs index a0ff8d116c..d2b44073a0 100644 --- a/tests/Kiota.Builder.Tests/Refiners/TypeScriptLanguageRefinerTests.cs +++ b/tests/Kiota.Builder.Tests/Refiners/TypeScriptLanguageRefinerTests.cs @@ -267,7 +267,8 @@ public async Task EscapesReservedKeywords() { var model = TestHelper.CreateModelClass(root, "break"); await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.TypeScript }, root); - var interFaceModel = root.Interfaces.First(x => "BreakEscaped".Equals(x.Name, StringComparison.Ordinal)); + var codeFile = root.FindChildByName(model.Name.ToFirstCharacterUpperCase()); + var interFaceModel = codeFile.Interfaces.First(x => "BreakEscaped".Equals(x.Name, StringComparison.Ordinal)); Assert.NotEqual("break", interFaceModel.Name); Assert.Contains("Escaped", interFaceModel.Name); } @@ -372,9 +373,11 @@ public async Task CorrectsCoreType() await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.TypeScript }, root); - var interFaceModel = root.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); - var deserializerFunction = root.FindChildByName($"DeserializeInto{model.Name.ToFirstCharacterUpperCase()}"); - var serializationFunction = root.FindChildByName($"Serialize{model.Name.ToFirstCharacterUpperCase()}"); + var codeFile = root.FindChildByName(model.Name.ToFirstCharacterUpperCase()); + + var interFaceModel = codeFile.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); + var deserializerFunction = codeFile.FindChildByName($"DeserializeInto{model.Name.ToFirstCharacterUpperCase()}"); + var serializationFunction = codeFile.FindChildByName($"Serialize{model.Name.ToFirstCharacterUpperCase()}"); Assert.Empty(interFaceModel.Properties.Where(x => HttpCoreDefaultName.Equals(x.Type.Name))); Assert.Empty(interFaceModel.Properties.Where(x => FactoryDefaultName.Equals(x.Type.Name))); Assert.Empty(interFaceModel.Properties.Where(x => DateTimeOffsetDefaultName.Equals(x.Type.Name))); @@ -400,7 +403,8 @@ public async Task ReplacesDateTimeOffsetByNativeType() }).First(); await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.TypeScript }, root); - var modelInterface = root.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); + var codeFile = root.FindChildByName(model.Name.ToFirstCharacterUpperCase()); + var modelInterface = codeFile.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); Assert.NotEmpty(modelInterface.StartBlock.Usings); Assert.Equal("Date", modelInterface.Properties.First(x => x.Name == codeProperty.Name).Type.Name); } @@ -419,7 +423,8 @@ public async Task ReplacesGuidsByRespectiveType() }).First(); await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.TypeScript }, root); - var modelInterface = root.Interfaces.First(x => x.Name.Equals(model.Name, StringComparison.OrdinalIgnoreCase)); + var codeFile = root.FindChildByName(model.Name.ToFirstCharacterUpperCase()); + var modelInterface = codeFile.Interfaces.First(x => x.Name.Equals(model.Name, StringComparison.OrdinalIgnoreCase)); Assert.NotEmpty(modelInterface.StartBlock.Usings); Assert.NotEmpty(modelInterface.StartBlock.Usings.Where(static x => x.Name.Equals("Guid", StringComparison.Ordinal))); Assert.Equal("Guid", modelInterface.Properties.First(x => x.Name.Equals(codeProperty.Name, StringComparison.OrdinalIgnoreCase)).Type.Name, StringComparer.OrdinalIgnoreCase); @@ -439,7 +444,8 @@ public async Task ReplacesDateOnlyByNativeType() }).First(); await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.TypeScript }, root); Assert.NotEmpty(model.StartBlock.Usings); - var modelInterface = root.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); + var codeFile = root.FindChildByName(model.Name.ToFirstCharacterUpperCase()); + var modelInterface = codeFile.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); Assert.NotEmpty(modelInterface.StartBlock.Usings); Assert.Equal("DateOnly", modelInterface.Properties.First(x => x.Name == codeProperty.Name).Type.Name); } @@ -457,7 +463,9 @@ public async Task ReplacesTimeOnlyByNativeType() }).First(); await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.TypeScript }, root); Assert.NotEmpty(model.StartBlock.Usings); - var modelInterface = root.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); + + var codeFile = root.FindChildByName(model.Name.ToFirstCharacterUpperCase()); + var modelInterface = codeFile.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); Assert.NotEmpty(modelInterface.StartBlock.Usings); Assert.Equal("TimeOnly", modelInterface.Properties.First(x => x.Name == codeProperty.Name).Type.Name); @@ -477,7 +485,8 @@ public async Task ReplacesDurationByNativeType() }).First(); await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.TypeScript }, root); Assert.NotEmpty(model.StartBlock.Usings); - var modelInterface = root.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); + var codeFile = root.FindChildByName(model.Name.ToFirstCharacterUpperCase()); + var modelInterface = codeFile.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); Assert.NotEmpty(modelInterface.StartBlock.Usings); Assert.Equal("Duration", modelInterface.Properties.First(x => x.Name == codeProperty.Name).Type.Name); } @@ -557,9 +566,16 @@ public async Task AliasesDuplicateUsingSymbols() }); model.AddUsing(using2); await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.TypeScript }, root); - var modelInterface = graphNS.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); - var source1Interface = modelsNS.Interfaces.First(x => x.Name == source1.Name.ToFirstCharacterUpperCase()); - var source2Interface = submodelsNS.Interfaces.First(x => x.Name == source2.Name.ToFirstCharacterUpperCase()); + + var modelCodeFile = graphNS.FindChildByName(model.Name.ToFirstCharacterUpperCase()); + var modelInterface = modelCodeFile.Interfaces.First(x => x.Name == model.Name.ToFirstCharacterUpperCase()); + + var source1CodeFile = modelsNS.FindChildByName(source1.Name.ToFirstCharacterUpperCase()); + var source1Interface = source1CodeFile.Interfaces.First(x => x.Name == source1.Name.ToFirstCharacterUpperCase()); + + var source2CodeFile = submodelsNS.FindChildByName(source2.Name.ToFirstCharacterUpperCase()); + var source2Interface = source2CodeFile.Interfaces.First(x => x.Name == source2.Name.ToFirstCharacterUpperCase()); + var modelUsing1 = modelInterface.Usings.First(x => x.Declaration.TypeDefinition == source2Interface); var modelUsing2 = modelInterface.Usings.First(x => x.Declaration.TypeDefinition == source1Interface); Assert.Equal(modelUsing1.Declaration.Name, modelUsing2.Declaration.Name); @@ -608,7 +624,9 @@ public async Task AddsModelInterfaceForAModelClass() var model = TestHelper.CreateModelClass(testNS, "modelA"); await ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.TypeScript }, testNS); - Assert.Contains(testNS.Interfaces, x => x.Name == "ModelA"); + + var codeFile = testNS.FindChildByName(model.Name.ToFirstCharacterUpperCase()); + Assert.Contains(codeFile.Interfaces, x => x.Name == "ModelA"); } [Fact] @@ -749,10 +767,10 @@ public async Task GeneratesCodeFiles() Assert.NotNull(codeFile.FindChildByName($"{model.Name.ToFirstCharacterUpperCase()}", false)); // model , interface, deserializer, serializer should be a direct descendant of the namespace - Assert.NotNull(root.FindChildByName($"DeserializeInto{model.Name.ToFirstCharacterUpperCase()}", false)); - Assert.NotNull(root.FindChildByName($"Serialize{model.Name.ToFirstCharacterUpperCase()}", false)); - Assert.NotNull(root.FindChildByName($"create{model.Name.ToFirstCharacterUpperCase()}FromDiscriminatorValue", false)); - Assert.NotNull(root.FindChildByName($"{model.Name.ToFirstCharacterUpperCase()}", false)); + Assert.Null(root.FindChildByName($"DeserializeInto{model.Name.ToFirstCharacterUpperCase()}", false)); + Assert.Null(root.FindChildByName($"Serialize{model.Name.ToFirstCharacterUpperCase()}", false)); + Assert.Null(root.FindChildByName($"create{model.Name.ToFirstCharacterUpperCase()}FromDiscriminatorValue", false)); + Assert.Null(root.FindChildByName($"{model.Name.ToFirstCharacterUpperCase()}", false)); } #endregion