From 26a71832503cde0477e12b2d968b1bf61c47cb99 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 10 Jul 2024 12:52:40 -0400 Subject: [PATCH 01/28] chore: adds a task to reset sample locks when generating Signed-off-by: Vincent Biret --- .vscode/launch.json | 40 ++++++++-------------------------------- .vscode/tasks.json | 12 +++++++++++- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index f4bcd561b3..6e77c8004f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -196,10 +196,7 @@ "request": "launch", "preLaunchTask": "build", "program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll", - "args": [ - "search", - "microsoft" - ], + "args": ["search", "microsoft"], "cwd": "${workspaceFolder}/src/kiota", "console": "internalConsole", "stopAtEntry": false @@ -210,10 +207,7 @@ "request": "launch", "preLaunchTask": "build", "program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll", - "args": [ - "search", - "test" - ], + "args": ["search", "test"], "cwd": "${workspaceFolder}/src/kiota", "console": "internalConsole", "stopAtEntry": false @@ -255,11 +249,7 @@ "request": "launch", "preLaunchTask": "build", "program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll", - "args": [ - "info", - "-l", - "CSharp" - ], + "args": ["info", "-l", "CSharp"], "cwd": "${workspaceFolder}/src/kiota", "console": "internalConsole", "stopAtEntry": false @@ -270,11 +260,7 @@ "request": "launch", "preLaunchTask": "build", "program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll", - "args": [ - "update", - "-o", - "${workspaceFolder}/samples" - ], + "args": ["update", "-o", "${workspaceFolder}/samples"], "cwd": "${workspaceFolder}/src/kiota", "console": "internalConsole", "stopAtEntry": false @@ -285,10 +271,7 @@ "request": "launch", "preLaunchTask": "build", "program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll", - "args": [ - "workspace", - "migrate" - ], + "args": ["workspace", "migrate"], "cwd": "${workspaceFolder}/samples/msgraph-mail/dotnet", "console": "internalConsole", "stopAtEntry": false, @@ -302,10 +285,7 @@ "request": "launch", "preLaunchTask": "build", "program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll", - "args": [ - "client", - "generate" - ], + "args": ["client", "generate"], "cwd": "${workspaceFolder}/samples/msgraph-mail/dotnet", "console": "internalConsole", "stopAtEntry": false, @@ -369,11 +349,7 @@ "request": "launch", "preLaunchTask": "build", "program": "${workspaceFolder}/src/kiota/bin/Debug/net8.0/kiota.dll", - "args": [ - "login", - "github", - "device" - ], + "args": ["login", "github", "device"], "cwd": "${workspaceFolder}/src/kiota", "console": "internalConsole", "stopAtEntry": false @@ -385,4 +361,4 @@ "processId": "${command:pickProcess}" } ] -} \ No newline at end of file +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 29f5089976..b125148fae 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -22,7 +22,17 @@ "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], - "problemMatcher": "$msCompile" + "problemMatcher": "$msCompile", + "dependsOn": ["checkout:sample:locks"] + }, + { + "label": "checkout:sample:locks", + "type": "process", + "command": "git", + "args": ["checkout", "**/kiota-lock.json"], + "options": { + "cwd": "${workspaceFolder}/samples" + } }, { "label": "test", From 51cd7f081ac62bded684d1f342e9f6aa528bf4ea Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 10 Jul 2024 12:58:15 -0400 Subject: [PATCH 02/28] fix: missing parent for code type --- src/Kiota.Builder/CodeDOM/CodeType.cs | 17 +++++++++++++++-- src/Kiota.Builder/Refiners/CliRefiner.cs | 4 ++-- .../Refiners/CommonLanguageRefiner.cs | 4 ++-- .../Writers/CSharp/CSharpConventionService.cs | 2 +- .../Writers/Go/GoConventionService.cs | 2 +- .../Writers/Python/PythonConventionService.cs | 2 +- .../TypeScript/TypeScriptConventionService.cs | 2 +- .../CodeDOM/CodeTypeTests.cs | 2 +- .../Writers/CLI/CliCodeMethodWriterTests.cs | 8 ++++---- 9 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/Kiota.Builder/CodeDOM/CodeType.cs b/src/Kiota.Builder/CodeDOM/CodeType.cs index c33c1e1309..3fb32eca2a 100644 --- a/src/Kiota.Builder/CodeDOM/CodeType.cs +++ b/src/Kiota.Builder/CodeDOM/CodeType.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -35,8 +36,20 @@ public override object Clone() // Clone the list so that modifications on cloned objects' property are localized // e.g. var y = x.Clone(); var z = y.Clone(); y.GenericTypeParameterValues.Add(value); // shouldn't modify x.GenericTypeParameterValues or z.GenericTypeParameterValues - GenericTypeParameterValues = new(GenericTypeParameterValues.ToList()), + genericTypeParameterValues = [.. genericTypeParameterValues], }.BaseClone(this, TypeDefinition is null || IsExternal); } - public Collection GenericTypeParameterValues { get; init; } = new(); + public IEnumerable GenericTypeParameterValues + { + get => genericTypeParameterValues; + init => AddGenericTypeParameterValue([.. value]); + } + private Collection genericTypeParameterValues = []; + public void AddGenericTypeParameterValue(params CodeType[] types) + { + if (types is null) return; + EnsureElementsAreChildren(types); + foreach (var type in types) + genericTypeParameterValues.Add(type); + } } diff --git a/src/Kiota.Builder/Refiners/CliRefiner.cs b/src/Kiota.Builder/Refiners/CliRefiner.cs index 666e7de988..4a01ed8801 100644 --- a/src/Kiota.Builder/Refiners/CliRefiner.cs +++ b/src/Kiota.Builder/Refiners/CliRefiner.cs @@ -327,10 +327,10 @@ private static void CreateCommandBuildersFromIndexer(CodeClass currentClass, Cod { Name = "Tuple", IsExternal = true, - GenericTypeParameterValues = new() { + GenericTypeParameterValues = [ CreateCommandType(collectionKind), CreateCommandType(collectionKind), - } + ] }, SimpleName = indexer.Name.CleanupSymbolName() }; diff --git a/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs b/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs index 3bff4b8ff3..bdcbba1984 100644 --- a/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs +++ b/src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs @@ -1408,7 +1408,7 @@ protected static void MoveRequestBuilderPropertiesToBaseType(CodeElement current }; if (addCurrentTypeAsGenericTypeParameter) { - currentClass.StartBlock.Inherits.GenericTypeParameterValues.Add(new CodeType + currentClass.StartBlock.Inherits.AddGenericTypeParameterValue(new CodeType { TypeDefinition = currentClass, }); @@ -1499,7 +1499,7 @@ protected static void RemoveRequestConfigurationClasses(CodeElement currentEleme private static CodeType GetGenericTypeForRequestConfiguration(CodeType configurationParameterType, CodeType genericTypeParamValue) { var newType = (CodeType)configurationParameterType.Clone(); - newType.GenericTypeParameterValues.Add(genericTypeParamValue); + newType.AddGenericTypeParameterValue(genericTypeParamValue); return newType; } diff --git a/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs b/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs index 9522fa678e..90fefbb7ef 100644 --- a/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs +++ b/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs @@ -186,7 +186,7 @@ public string GetTypeString(CodeTypeBase code, CodeElement targetElement, bool i CodeTypeCollectionKind.Array when includeCollectionInformation => "[]", _ => string.Empty, }; - var genericParameters = currentType.GenericTypeParameterValues.Count != 0 ? + var genericParameters = currentType.GenericTypeParameterValues.Any() ? $"<{string.Join(", ", currentType.GenericTypeParameterValues.Select(x => GetTypeString(x, targetElement, includeCollectionInformation)))}>" : string.Empty; if (currentType.ActionOf && includeActionInformation) diff --git a/src/Kiota.Builder/Writers/Go/GoConventionService.cs b/src/Kiota.Builder/Writers/Go/GoConventionService.cs index 02870aaca5..284a85cb5b 100644 --- a/src/Kiota.Builder/Writers/Go/GoConventionService.cs +++ b/src/Kiota.Builder/Writers/Go/GoConventionService.cs @@ -71,7 +71,7 @@ currentType.TypeDefinition is not CodeInterface && CodeTypeBase.CodeTypeCollectionKind.Array or CodeTypeBase.CodeTypeCollectionKind.Complex when includeCollectionInformation => "[]", _ => string.Empty, }; - var genericTypeParameters = currentType.GenericTypeParameterValues.Count != 0 ? + var genericTypeParameters = currentType.GenericTypeParameterValues.Any() ? $"[{string.Join(",", currentType.GenericTypeParameterValues.Select(x => GetTypeString(x, targetElement, true, false, true)))}]" : string.Empty; if (currentType.ActionOf) diff --git a/src/Kiota.Builder/Writers/Python/PythonConventionService.cs b/src/Kiota.Builder/Writers/Python/PythonConventionService.cs index 1dd3370bb1..0daa153fd5 100644 --- a/src/Kiota.Builder/Writers/Python/PythonConventionService.cs +++ b/src/Kiota.Builder/Writers/Python/PythonConventionService.cs @@ -87,7 +87,7 @@ public override string GetTypeString(CodeTypeBase code, CodeElement targetElemen typeName = targetElement.Parent.Name; if (code.ActionOf && writer != null) return WriteInlineDeclaration(currentType, targetElement, writer); - var genericParameters = currentType.GenericTypeParameterValues.Count != 0 ? + var genericParameters = currentType.GenericTypeParameterValues.Any() ? $"[{string.Join(", ", currentType.GenericTypeParameterValues.Select(x => GetTypeString(x, targetElement, includeCollectionInformation)))}]" : string.Empty; return $"{collectionPrefix}{typeName}{genericParameters}{collectionSuffix}"; } diff --git a/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs b/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs index 9770e7ad57..ab5fdca0fb 100644 --- a/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs +++ b/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs @@ -79,7 +79,7 @@ public override string GetTypeString(CodeTypeBase code, CodeElement targetElemen if (code is CodeType currentType) { var typeName = GetTypeAlias(currentType, targetElement) is string alias && !string.IsNullOrEmpty(alias) ? alias : TranslateType(currentType); - var genericParameters = currentType.GenericTypeParameterValues.Count != 0 ? + var genericParameters = currentType.GenericTypeParameterValues.Any() ? $"<{string.Join(", ", currentType.GenericTypeParameterValues.Select(x => GetTypeString(x, targetElement, includeCollectionInformation)))}>" : string.Empty; return $"{typeName}{collectionSuffix}{genericParameters}"; diff --git a/tests/Kiota.Builder.Tests/CodeDOM/CodeTypeTests.cs b/tests/Kiota.Builder.Tests/CodeDOM/CodeTypeTests.cs index 02efa9e12b..026dd3f19d 100644 --- a/tests/Kiota.Builder.Tests/CodeDOM/CodeTypeTests.cs +++ b/tests/Kiota.Builder.Tests/CodeDOM/CodeTypeTests.cs @@ -42,7 +42,7 @@ public void ClonesGenericTypeParameterValuesProperly() Name = "class1" }; var clone = type.Clone() as CodeType; - clone.GenericTypeParameterValues.Add(new CodeType { Name = "genparam1" }); + clone.AddGenericTypeParameterValue(new CodeType { Name = "genparam1" }); Assert.Single(clone.AllTypes); Assert.Empty(type.GenericTypeParameterValues); diff --git a/tests/Kiota.Builder.Tests/Writers/CLI/CliCodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CLI/CliCodeMethodWriterTests.cs index a5b9e04432..2d5f2afcb8 100644 --- a/tests/Kiota.Builder.Tests/Writers/CLI/CliCodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/CLI/CliCodeMethodWriterTests.cs @@ -281,10 +281,10 @@ public void WritesMatchingIndexerCommandsIntoExecutableCommand() { Name = "Tuple", IsExternal = true, - GenericTypeParameterValues = new() { + GenericTypeParameterValues = [ new CodeType { CollectionKind = CodeTypeBase.CodeTypeCollectionKind.Array }, new CodeType { CollectionKind = CodeTypeBase.CodeTypeCollectionKind.Array }, - }, + ], }; type.AddMethod(new CodeMethod { Kind = CodeMethodKind.CommandBuilder, Name = "BuildTestMethod1", SimpleName = "User", ReturnType = new CodeType() }); type.AddMethod(new CodeMethod { Kind = CodeMethodKind.CommandBuilder, Name = "BuildTestMethod2", SimpleName = "User", ReturnType = tupleReturn }); @@ -374,10 +374,10 @@ public void WritesMatchingIndexerCommandsIntoContainerCommand() { Name = "Tuple", IsExternal = true, - GenericTypeParameterValues = new() { + GenericTypeParameterValues = [ new CodeType { CollectionKind = CodeTypeBase.CodeTypeCollectionKind.Array }, new CodeType { CollectionKind = CodeTypeBase.CodeTypeCollectionKind.Array }, - }, + ], }; type.AddMethod(new CodeMethod { Kind = CodeMethodKind.CommandBuilder, Name = "BuildTestMethod1", SimpleName = "User", ReturnType = new CodeType() }); type.AddMethod(new CodeMethod { Kind = CodeMethodKind.CommandBuilder, Name = "BuildTestMethod2", SimpleName = "User", ReturnType = tupleReturn }); From 9ac23390d74ff618a7fd8d0f4639e461bf0b581c Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Wed, 10 Jul 2024 12:58:31 -0400 Subject: [PATCH 03/28] feat: base infrastructure for dom export --- src/Kiota.Builder/Diff/DomExportService.cs | 26 ++++++++++++++++++++++ src/Kiota.Builder/KiotaBuilder.cs | 8 +++++++ 2 files changed, 34 insertions(+) create mode 100644 src/Kiota.Builder/Diff/DomExportService.cs diff --git a/src/Kiota.Builder/Diff/DomExportService.cs b/src/Kiota.Builder/Diff/DomExportService.cs new file mode 100644 index 0000000000..e8ebf3ce8a --- /dev/null +++ b/src/Kiota.Builder/Diff/DomExportService.cs @@ -0,0 +1,26 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Kiota.Builder.CodeDOM; + +namespace Kiota.Builder.Diff; + +public class DomExportService +{ + public DomExportService(string outputDirectoryPath) + { + ArgumentException.ThrowIfNullOrEmpty(outputDirectoryPath); + OutputDirectoryPath = outputDirectoryPath; + } + private readonly string OutputDirectoryPath; + private const string DomExportFileName = "kiota-dom-export.txt"; + public async Task SerializeDomAsync(CodeNamespace rootNamespace, CancellationToken cancellationToken = default) + { + //TODO there are things were order matter like enum options and parameters + var filePath = Path.Combine(OutputDirectoryPath, DomExportFileName); + using var fileStream = File.Create(filePath); + await Task.Delay(1, cancellationToken).ConfigureAwait(false); + //TODO implement linear export + } +} diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index f3535b3cf0..5e6372b6e2 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -17,6 +17,7 @@ using Kiota.Builder.CodeDOM; using Kiota.Builder.CodeRenderers; using Kiota.Builder.Configuration; +using Kiota.Builder.Diff; using Kiota.Builder.EqualityComparers; using Kiota.Builder.Exceptions; using Kiota.Builder.Extensions; @@ -268,6 +269,13 @@ public async Task GenerateClientAsync(CancellationToken cancellationToken) await ApplyLanguageRefinement(config, generatedCode, cancellationToken).ConfigureAwait(false); StopLogAndReset(sw, $"step {++stepId} - refine by language - took"); + // Generate dom export + //TODO hide this behind an environment variable + sw.Start(); + var diffService = new DomExportService(config.OutputPath); + await diffService.SerializeDomAsync(generatedCode, cancellationToken).ConfigureAwait(false); + StopLogAndReset(sw, $"step {++stepId} - generated dom export - took"); + // Write language source sw.Start(); await CreateLanguageSourceFilesAsync(config.Language, generatedCode, cancellationToken).ConfigureAwait(false); From bea1c09f740be14ea99591134d93db9c8596664f Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 12 Jul 2024 10:53:59 -0400 Subject: [PATCH 04/28] fix: adds interface for accessibility elements Signed-off-by: Vincent Biret --- src/Kiota.Builder/CodeDOM/CodeMethod.cs | 2 +- src/Kiota.Builder/CodeDOM/CodeProperty.cs | 2 +- src/Kiota.Builder/CodeDOM/IAccessibleElement.cs | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 src/Kiota.Builder/CodeDOM/IAccessibleElement.cs diff --git a/src/Kiota.Builder/CodeDOM/CodeMethod.cs b/src/Kiota.Builder/CodeDOM/CodeMethod.cs index 006960ec8c..fb287c2788 100644 --- a/src/Kiota.Builder/CodeDOM/CodeMethod.cs +++ b/src/Kiota.Builder/CodeDOM/CodeMethod.cs @@ -85,7 +85,7 @@ public object Clone() } } -public class CodeMethod : CodeTerminalWithKind, ICloneable, IDocumentedElement, IDeprecableElement +public class CodeMethod : CodeTerminalWithKind, ICloneable, IDocumentedElement, IDeprecableElement, IAccessibleElement { public static readonly CodeParameterKind ParameterKindForConvertedIndexers = CodeParameterKind.Custom; public static CodeMethod FromIndexer(CodeIndexer originalIndexer, Func methodNameCallback, Func parameterNameCallback, bool parameterNullable, bool typeSpecificOverload = false) diff --git a/src/Kiota.Builder/CodeDOM/CodeProperty.cs b/src/Kiota.Builder/CodeDOM/CodeProperty.cs index 74994cb893..8b93244db6 100644 --- a/src/Kiota.Builder/CodeDOM/CodeProperty.cs +++ b/src/Kiota.Builder/CodeDOM/CodeProperty.cs @@ -40,7 +40,7 @@ public enum CodePropertyKind ErrorMessageOverride } -public class CodeProperty : CodeTerminalWithKind, IDocumentedElement, IAlternativeName, ICloneable, IDeprecableElement +public class CodeProperty : CodeTerminalWithKind, IDocumentedElement, IAlternativeName, ICloneable, IDeprecableElement, IAccessibleElement { public bool ReadOnly { diff --git a/src/Kiota.Builder/CodeDOM/IAccessibleElement.cs b/src/Kiota.Builder/CodeDOM/IAccessibleElement.cs new file mode 100644 index 0000000000..f7e897ef35 --- /dev/null +++ b/src/Kiota.Builder/CodeDOM/IAccessibleElement.cs @@ -0,0 +1,8 @@ +namespace Kiota.Builder.CodeDOM; +public interface IAccessibleElement +{ + AccessModifier Access + { + get; set; + } +} From f7226c0ee9b45e0b84e02b9d474ae77761bc7d44 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 12 Jul 2024 10:54:34 -0400 Subject: [PATCH 05/28] feat: early prortotype of linear export Signed-off-by: Vincent Biret --- src/Kiota.Builder/Diff/DomExportService.cs | 59 +++++++++++++++++++--- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/src/Kiota.Builder/Diff/DomExportService.cs b/src/Kiota.Builder/Diff/DomExportService.cs index e8ebf3ce8a..133347790c 100644 --- a/src/Kiota.Builder/Diff/DomExportService.cs +++ b/src/Kiota.Builder/Diff/DomExportService.cs @@ -1,26 +1,73 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Kiota.Builder.CodeDOM; namespace Kiota.Builder.Diff; -public class DomExportService +internal class DomExportService { - public DomExportService(string outputDirectoryPath) + internal DomExportService(string outputDirectoryPath) { ArgumentException.ThrowIfNullOrEmpty(outputDirectoryPath); OutputDirectoryPath = outputDirectoryPath; } private readonly string OutputDirectoryPath; private const string DomExportFileName = "kiota-dom-export.txt"; - public async Task SerializeDomAsync(CodeNamespace rootNamespace, CancellationToken cancellationToken = default) + internal async Task SerializeDomAsync(CodeNamespace rootNamespace, CancellationToken cancellationToken = default) { - //TODO there are things were order matter like enum options and parameters var filePath = Path.Combine(OutputDirectoryPath, DomExportFileName); using var fileStream = File.Create(filePath); - await Task.Delay(1, cancellationToken).ConfigureAwait(false); - //TODO implement linear export + var entries = GetEntriesFromDom(rootNamespace).Order(StringComparer.OrdinalIgnoreCase).ToArray(); + var content = string.Join(Environment.NewLine, entries); + var contentBytes = System.Text.Encoding.UTF8.GetBytes(content); + await fileStream.WriteAsync(contentBytes, cancellationToken).ConfigureAwait(false); + } + private static IEnumerable GetEntriesFromDom(CodeElement currentElement) + { + if (GetEntry(currentElement) is string currentElementEntry && !string.IsNullOrEmpty(currentElementEntry)) + yield return currentElementEntry; + foreach (var childElement in currentElement.GetChildElements()) + foreach (var childElementEntry in GetEntriesFromDom(childElement)) + yield return childElementEntry; + } + private static string GetEntry(CodeElement codeElement) + { + if (codeElement is IAccessibleElement accessibleElement && accessibleElement.Access is AccessModifier.Private) + return string.Empty; + return codeElement switch + { + CodeProperty property when property.Parent is not null => + $"{GetEntryPath(property.Parent)}::{property.Name}:{GetEntryType(property.Type)}", + //TODO method + //TODO index + //TODO enum member + //TODO functions + //TODO class/interface inheritance + _ => string.Empty, + }; + } + private static string GetEntryType(CodeTypeBase codeElementTypeBase) + { + return codeElementTypeBase switch + { + CodeType codeElementType when codeElementType.TypeDefinition is not null => GetEntry(codeElementType.TypeDefinition), + CodeType codeElementType when codeElementType.TypeDefinition is null => codeElementType.Name, + _ => codeElementTypeBase.Name, + }; + } + private static string GetEntryPath(CodeElement codeElement) + { + return codeElement switch + { + CodeClass x when x.Parent is not null => $"{GetEntryPath(x.Parent)}.{codeElement.Name}", + CodeEnum x when x.Parent is not null => $"{GetEntryPath(x.Parent)}.{codeElement.Name}", + CodeInterface x when x.Parent is not null => $"{GetEntryPath(x.Parent)}.{codeElement.Name}", + CodeNamespace => $"{codeElement.Name}", + _ => string.Empty, + }; } } From 3b7257e2f4c9a50b119dce614763f22ab8cca585 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 12 Jul 2024 12:28:48 -0400 Subject: [PATCH 06/28] fix: property custom types Signed-off-by: Vincent Biret --- src/Kiota.Builder/Diff/DomExportService.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Kiota.Builder/Diff/DomExportService.cs b/src/Kiota.Builder/Diff/DomExportService.cs index 133347790c..33b9fd8451 100644 --- a/src/Kiota.Builder/Diff/DomExportService.cs +++ b/src/Kiota.Builder/Diff/DomExportService.cs @@ -34,7 +34,7 @@ private static IEnumerable GetEntriesFromDom(CodeElement currentElement) foreach (var childElementEntry in GetEntriesFromDom(childElement)) yield return childElementEntry; } - private static string GetEntry(CodeElement codeElement) + private static string GetEntry(CodeElement codeElement, bool includeDefinitions = false) { if (codeElement is IAccessibleElement accessibleElement && accessibleElement.Access is AccessModifier.Private) return string.Empty; @@ -47,16 +47,22 @@ private static string GetEntry(CodeElement codeElement) //TODO enum member //TODO functions //TODO class/interface inheritance + CodeClass codeClass when includeDefinitions => GetEntryPath(codeClass), + CodeEnum codeEnum when includeDefinitions => GetEntryPath(codeEnum), + CodeInterface codeInterface when includeDefinitions => GetEntryPath(codeInterface), _ => string.Empty, }; } private static string GetEntryType(CodeTypeBase codeElementTypeBase) { + var collectionPrefix = codeElementTypeBase.IsArray ? "[" : codeElementTypeBase.CollectionKind is CodeTypeBase.CodeTypeCollectionKind.Complex ? "[" : string.Empty; + var collectionSuffix = codeElementTypeBase.IsArray ? "]" : codeElementTypeBase.CollectionKind is CodeTypeBase.CodeTypeCollectionKind.Complex ? "]" : string.Empty; + //TODO use the collection types from the convention service return codeElementTypeBase switch { - CodeType codeElementType when codeElementType.TypeDefinition is not null => GetEntry(codeElementType.TypeDefinition), - CodeType codeElementType when codeElementType.TypeDefinition is null => codeElementType.Name, - _ => codeElementTypeBase.Name, + CodeType codeElementType when codeElementType.TypeDefinition is not null => $"{collectionPrefix}{GetEntry(codeElementType.TypeDefinition, true)}{collectionSuffix}", + CodeType codeElementType when codeElementType.TypeDefinition is null => $"{collectionPrefix}{codeElementType.Name}{collectionSuffix}", + _ => $"{collectionPrefix}{codeElementTypeBase.Name}{collectionSuffix}", }; } private static string GetEntryPath(CodeElement codeElement) From 7a3d261441c8fc4f5732976161ab6c2a90ffc9a2 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 12 Jul 2024 12:42:33 -0400 Subject: [PATCH 07/28] feat: adds indexer support Signed-off-by: Vincent Biret --- src/Kiota.Builder/Diff/DomExportService.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Kiota.Builder/Diff/DomExportService.cs b/src/Kiota.Builder/Diff/DomExportService.cs index 33b9fd8451..568265ac3f 100644 --- a/src/Kiota.Builder/Diff/DomExportService.cs +++ b/src/Kiota.Builder/Diff/DomExportService.cs @@ -42,10 +42,13 @@ private static string GetEntry(CodeElement codeElement, bool includeDefinitions { CodeProperty property when property.Parent is not null => $"{GetEntryPath(property.Parent)}::{property.Name}:{GetEntryType(property.Type)}", - //TODO method - //TODO index + CodeMethod method when method.Parent is not null => + $"{GetEntryPath(method.Parent)}::{method.Name}({GetParameters(method.Parameters)}):{GetEntryType(method.ReturnType)}", + CodeFunction function when function.Parent is not null => + $"{GetEntryPath(function.Parent)}::{function.Name}({GetParameters(function.OriginalLocalMethod.Parameters)}):{GetEntryType(function.OriginalLocalMethod.ReturnType)}", + CodeIndexer codeIndexer when codeIndexer.Parent is not null => + $"{GetEntryPath(codeIndexer.Parent)}::[{GetParameters([codeIndexer.IndexParameter])}]:{GetEntryType(codeIndexer.ReturnType)}", //TODO enum member - //TODO functions //TODO class/interface inheritance CodeClass codeClass when includeDefinitions => GetEntryPath(codeClass), CodeEnum codeEnum when includeDefinitions => GetEntryPath(codeEnum), @@ -53,6 +56,10 @@ private static string GetEntry(CodeElement codeElement, bool includeDefinitions _ => string.Empty, }; } + private static string GetParameters(IEnumerable parameters) + { + return string.Join(", ", parameters.Select(static x => $"{x.Name}:{GetEntryType(x.Type)}")); + } private static string GetEntryType(CodeTypeBase codeElementTypeBase) { var collectionPrefix = codeElementTypeBase.IsArray ? "[" : codeElementTypeBase.CollectionKind is CodeTypeBase.CodeTypeCollectionKind.Complex ? "[" : string.Empty; From 8d9816bb46d447e4305ec7e376fbd5234c3d9317 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 12 Jul 2024 12:56:42 -0400 Subject: [PATCH 08/28] feat: adds support for enums Signed-off-by: Vincent Biret --- src/Kiota.Builder/Diff/DomExportService.cs | 27 +++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Kiota.Builder/Diff/DomExportService.cs b/src/Kiota.Builder/Diff/DomExportService.cs index 568265ac3f..858937c008 100644 --- a/src/Kiota.Builder/Diff/DomExportService.cs +++ b/src/Kiota.Builder/Diff/DomExportService.cs @@ -28,32 +28,33 @@ internal async Task SerializeDomAsync(CodeNamespace rootNamespace, CancellationT } private static IEnumerable GetEntriesFromDom(CodeElement currentElement) { - if (GetEntry(currentElement) is string currentElementEntry && !string.IsNullOrEmpty(currentElementEntry)) + foreach (var currentElementEntry in GetEntry(currentElement).Where(static x => !string.IsNullOrEmpty(x))) yield return currentElementEntry; foreach (var childElement in currentElement.GetChildElements()) foreach (var childElementEntry in GetEntriesFromDom(childElement)) yield return childElementEntry; } - private static string GetEntry(CodeElement codeElement, bool includeDefinitions = false) + private static IEnumerable GetEntry(CodeElement codeElement, bool includeDefinitions = false) { if (codeElement is IAccessibleElement accessibleElement && accessibleElement.Access is AccessModifier.Private) - return string.Empty; + return []; return codeElement switch { CodeProperty property when property.Parent is not null => - $"{GetEntryPath(property.Parent)}::{property.Name}:{GetEntryType(property.Type)}", + [$"{GetEntryPath(property.Parent)}::{property.Name}:{GetEntryType(property.Type)}"], CodeMethod method when method.Parent is not null => - $"{GetEntryPath(method.Parent)}::{method.Name}({GetParameters(method.Parameters)}):{GetEntryType(method.ReturnType)}", + [$"{GetEntryPath(method.Parent)}::{method.Name}({GetParameters(method.Parameters)}):{GetEntryType(method.ReturnType)}"], CodeFunction function when function.Parent is not null => - $"{GetEntryPath(function.Parent)}::{function.Name}({GetParameters(function.OriginalLocalMethod.Parameters)}):{GetEntryType(function.OriginalLocalMethod.ReturnType)}", + [$"{GetEntryPath(function.Parent)}::{function.Name}({GetParameters(function.OriginalLocalMethod.Parameters)}):{GetEntryType(function.OriginalLocalMethod.ReturnType)}"], CodeIndexer codeIndexer when codeIndexer.Parent is not null => - $"{GetEntryPath(codeIndexer.Parent)}::[{GetParameters([codeIndexer.IndexParameter])}]:{GetEntryType(codeIndexer.ReturnType)}", - //TODO enum member + [$"{GetEntryPath(codeIndexer.Parent)}::[{GetParameters([codeIndexer.IndexParameter])}]:{GetEntryType(codeIndexer.ReturnType)}"], + CodeEnum codeEnum1 when !includeDefinitions => + codeEnum1.Options.Select((x, y) => $"{GetEntryPath(codeEnum1)}::{y:D4}-{x.Name}"), //TODO class/interface inheritance - CodeClass codeClass when includeDefinitions => GetEntryPath(codeClass), - CodeEnum codeEnum when includeDefinitions => GetEntryPath(codeEnum), - CodeInterface codeInterface when includeDefinitions => GetEntryPath(codeInterface), - _ => string.Empty, + CodeClass codeClass when includeDefinitions => [GetEntryPath(codeClass)], + CodeEnum codeEnum when includeDefinitions => [GetEntryPath(codeEnum)], + CodeInterface codeInterface when includeDefinitions => [GetEntryPath(codeInterface)], + _ => [], }; } private static string GetParameters(IEnumerable parameters) @@ -67,7 +68,7 @@ private static string GetEntryType(CodeTypeBase codeElementTypeBase) //TODO use the collection types from the convention service return codeElementTypeBase switch { - CodeType codeElementType when codeElementType.TypeDefinition is not null => $"{collectionPrefix}{GetEntry(codeElementType.TypeDefinition, true)}{collectionSuffix}", + CodeType codeElementType when codeElementType.TypeDefinition is not null => $"{collectionPrefix}{GetEntry(codeElementType.TypeDefinition, true).First()}{collectionSuffix}", CodeType codeElementType when codeElementType.TypeDefinition is null => $"{collectionPrefix}{codeElementType.Name}{collectionSuffix}", _ => $"{collectionPrefix}{codeElementTypeBase.Name}{collectionSuffix}", }; From 7b10f859507d84f3d6d83822cfcde55ef5c958da Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 12 Jul 2024 13:43:58 -0400 Subject: [PATCH 09/28] feat: adds support for inheritance and implement Signed-off-by: Vincent Biret --- src/Kiota.Builder/Diff/DomExportService.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Kiota.Builder/Diff/DomExportService.cs b/src/Kiota.Builder/Diff/DomExportService.cs index 858937c008..23f30c3c78 100644 --- a/src/Kiota.Builder/Diff/DomExportService.cs +++ b/src/Kiota.Builder/Diff/DomExportService.cs @@ -38,6 +38,9 @@ private static IEnumerable GetEntry(CodeElement codeElement, bool includ { if (codeElement is IAccessibleElement accessibleElement && accessibleElement.Access is AccessModifier.Private) return []; + //TODO access modifiers + //TODO static modifiers + //TODO optional parameters return codeElement switch { CodeProperty property when property.Parent is not null => @@ -50,13 +53,20 @@ private static IEnumerable GetEntry(CodeElement codeElement, bool includ [$"{GetEntryPath(codeIndexer.Parent)}::[{GetParameters([codeIndexer.IndexParameter])}]:{GetEntryType(codeIndexer.ReturnType)}"], CodeEnum codeEnum1 when !includeDefinitions => codeEnum1.Options.Select((x, y) => $"{GetEntryPath(codeEnum1)}::{y:D4}-{x.Name}"), - //TODO class/interface inheritance + CodeClass codeClass1 when !includeDefinitions && codeClass1.StartBlock.Inherits is not null => + [$"{GetEntryPath(codeClass1)}{InheritsSymbol}{GetEntryType(codeClass1.StartBlock.Inherits)}"], + CodeClass codeClass2 when !includeDefinitions && codeClass2.StartBlock.Implements.Any() => + [$"{GetEntryPath(codeClass2)}{ImplementsSymbol}{string.Join(", ", codeClass2.StartBlock.Implements.Select(static x => GetEntryType(x)))}"], + CodeInterface codeInterface1 when !includeDefinitions && codeInterface1.StartBlock.Implements.Any() => + [$"{GetEntryPath(codeInterface1)}{ImplementsSymbol}{string.Join(", ", codeInterface1.StartBlock.Implements.Select(static x => GetEntryType(x)))}"], CodeClass codeClass when includeDefinitions => [GetEntryPath(codeClass)], CodeEnum codeEnum when includeDefinitions => [GetEntryPath(codeEnum)], CodeInterface codeInterface when includeDefinitions => [GetEntryPath(codeInterface)], _ => [], }; } + private const string InheritsSymbol = "-->"; + private const string ImplementsSymbol = "~~>"; private static string GetParameters(IEnumerable parameters) { return string.Join(", ", parameters.Select(static x => $"{x.Name}:{GetEntryType(x.Type)}")); From bae68354546b80b61852492a5e588dbecb5c64c8 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 12 Jul 2024 14:14:57 -0400 Subject: [PATCH 10/28] feat: adds support for optional and defaulted parameters Signed-off-by: Vincent Biret --- src/Kiota.Builder/Diff/DomExportService.cs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Kiota.Builder/Diff/DomExportService.cs b/src/Kiota.Builder/Diff/DomExportService.cs index 23f30c3c78..bd9b00b1a6 100644 --- a/src/Kiota.Builder/Diff/DomExportService.cs +++ b/src/Kiota.Builder/Diff/DomExportService.cs @@ -9,7 +9,7 @@ namespace Kiota.Builder.Diff; internal class DomExportService -{ +{//TODO rename the service and the namespace to something more meaningful internal DomExportService(string outputDirectoryPath) { ArgumentException.ThrowIfNullOrEmpty(outputDirectoryPath); @@ -36,17 +36,20 @@ private static IEnumerable GetEntriesFromDom(CodeElement currentElement) } private static IEnumerable GetEntry(CodeElement codeElement, bool includeDefinitions = false) { - if (codeElement is IAccessibleElement accessibleElement && accessibleElement.Access is AccessModifier.Private) - return []; - //TODO access modifiers - //TODO static modifiers - //TODO optional parameters + string accessModifierValue; + if (codeElement is IAccessibleElement accessibleElement) + if (accessibleElement.Access is AccessModifier.Private) + return []; + else + accessModifierValue = $"|{accessibleElement.Access.ToString().ToLowerInvariant()}|"; + else + accessModifierValue = string.Empty; return codeElement switch { CodeProperty property when property.Parent is not null => - [$"{GetEntryPath(property.Parent)}::{property.Name}:{GetEntryType(property.Type)}"], + [$"{GetEntryPath(property.Parent)}::{accessModifierValue}{property.Name}:{GetEntryType(property.Type)}"], CodeMethod method when method.Parent is not null => - [$"{GetEntryPath(method.Parent)}::{method.Name}({GetParameters(method.Parameters)}):{GetEntryType(method.ReturnType)}"], + [$"{GetEntryPath(method.Parent)}::{(method.IsStatic ? "|static" : string.Empty)}{accessModifierValue}{method.Name}({GetParameters(method.Parameters)}):{GetEntryType(method.ReturnType)}"], CodeFunction function when function.Parent is not null => [$"{GetEntryPath(function.Parent)}::{function.Name}({GetParameters(function.OriginalLocalMethod.Parameters)}):{GetEntryType(function.OriginalLocalMethod.ReturnType)}"], CodeIndexer codeIndexer when codeIndexer.Parent is not null => @@ -69,7 +72,7 @@ private static IEnumerable GetEntry(CodeElement codeElement, bool includ private const string ImplementsSymbol = "~~>"; private static string GetParameters(IEnumerable parameters) { - return string.Join(", ", parameters.Select(static x => $"{x.Name}:{GetEntryType(x.Type)}")); + return string.Join(", ", parameters.Select(static x => $"{x.Name}{(x.Optional ? "?" : string.Empty)}:{GetEntryType(x.Type)}{(string.IsNullOrEmpty(x.DefaultValue) ? string.Empty : $"={x.DefaultValue}")}")); } private static string GetEntryType(CodeTypeBase codeElementTypeBase) { From 45397fedcaf81837c6a09b0559fd13ef858e6e44 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 18 Jul 2024 10:39:16 -0400 Subject: [PATCH 11/28] fix: parameters separation to support generic types parsing Signed-off-by: Vincent Biret --- src/Kiota.Builder/Diff/DomExportService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Kiota.Builder/Diff/DomExportService.cs b/src/Kiota.Builder/Diff/DomExportService.cs index bd9b00b1a6..615eadda64 100644 --- a/src/Kiota.Builder/Diff/DomExportService.cs +++ b/src/Kiota.Builder/Diff/DomExportService.cs @@ -59,9 +59,9 @@ private static IEnumerable GetEntry(CodeElement codeElement, bool includ CodeClass codeClass1 when !includeDefinitions && codeClass1.StartBlock.Inherits is not null => [$"{GetEntryPath(codeClass1)}{InheritsSymbol}{GetEntryType(codeClass1.StartBlock.Inherits)}"], CodeClass codeClass2 when !includeDefinitions && codeClass2.StartBlock.Implements.Any() => - [$"{GetEntryPath(codeClass2)}{ImplementsSymbol}{string.Join(", ", codeClass2.StartBlock.Implements.Select(static x => GetEntryType(x)))}"], + [$"{GetEntryPath(codeClass2)}{ImplementsSymbol}{string.Join("; ", codeClass2.StartBlock.Implements.Select(static x => GetEntryType(x)))}"], CodeInterface codeInterface1 when !includeDefinitions && codeInterface1.StartBlock.Implements.Any() => - [$"{GetEntryPath(codeInterface1)}{ImplementsSymbol}{string.Join(", ", codeInterface1.StartBlock.Implements.Select(static x => GetEntryType(x)))}"], + [$"{GetEntryPath(codeInterface1)}{ImplementsSymbol}{string.Join("; ", codeInterface1.StartBlock.Implements.Select(static x => GetEntryType(x)))}"], CodeClass codeClass when includeDefinitions => [GetEntryPath(codeClass)], CodeEnum codeEnum when includeDefinitions => [GetEntryPath(codeEnum)], CodeInterface codeInterface when includeDefinitions => [GetEntryPath(codeInterface)], @@ -72,7 +72,7 @@ private static IEnumerable GetEntry(CodeElement codeElement, bool includ private const string ImplementsSymbol = "~~>"; private static string GetParameters(IEnumerable parameters) { - return string.Join(", ", parameters.Select(static x => $"{x.Name}{(x.Optional ? "?" : string.Empty)}:{GetEntryType(x.Type)}{(string.IsNullOrEmpty(x.DefaultValue) ? string.Empty : $"={x.DefaultValue}")}")); + return string.Join("; ", parameters.Select(static x => $"{x.Name}{(x.Optional ? "?" : string.Empty)}:{GetEntryType(x.Type)}{(string.IsNullOrEmpty(x.DefaultValue) ? string.Empty : $"={x.DefaultValue}")}")); } private static string GetEntryType(CodeTypeBase codeElementTypeBase) { From 570b508da64fb3fa7d296ac8073fb63951582309 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 23 Jul 2024 15:05:32 -0400 Subject: [PATCH 12/28] feat: enables public API export service behind a flag Signed-off-by: Vincent Biret --- .../Configuration/GenerationConfiguration.cs | 5 +++++ .../PublicAPIExportService.cs} | 6 +++--- src/Kiota.Builder/KiotaBuilder.cs | 16 +++++++++------- src/kiota/KiotaConfigurationExtensions.cs | 1 + 4 files changed, 18 insertions(+), 10 deletions(-) rename src/Kiota.Builder/{Diff/DomExportService.cs => Export/PublicAPIExportService.cs} (97%) diff --git a/src/Kiota.Builder/Configuration/GenerationConfiguration.cs b/src/Kiota.Builder/Configuration/GenerationConfiguration.cs index 1f9de43628..52fc3f1965 100644 --- a/src/Kiota.Builder/Configuration/GenerationConfiguration.cs +++ b/src/Kiota.Builder/Configuration/GenerationConfiguration.cs @@ -39,6 +39,10 @@ public ConsumerOperation? Operation public string ClientClassName { get; set; } = "ApiClient"; public string ClientNamespaceName { get; set; } = "ApiSdk"; public string NamespaceNameSeparator { get; set; } = "."; + public bool ExportPublicApi + { + get; set; + } internal const string ModelsNamespaceSegmentName = "models"; public string ModelsNamespaceName { @@ -152,6 +156,7 @@ public object Clone() PatternsOverride = new(PatternsOverride ?? Enumerable.Empty(), StringComparer.OrdinalIgnoreCase), PluginTypes = new(PluginTypes ?? Enumerable.Empty()), DisableSSLValidation = DisableSSLValidation, + ExportPublicApi = ExportPublicApi, }; } private static readonly StringIEnumerableDeepComparer comparer = new(); diff --git a/src/Kiota.Builder/Diff/DomExportService.cs b/src/Kiota.Builder/Export/PublicAPIExportService.cs similarity index 97% rename from src/Kiota.Builder/Diff/DomExportService.cs rename to src/Kiota.Builder/Export/PublicAPIExportService.cs index 615eadda64..90138c03bb 100644 --- a/src/Kiota.Builder/Diff/DomExportService.cs +++ b/src/Kiota.Builder/Export/PublicAPIExportService.cs @@ -6,11 +6,11 @@ using System.Threading.Tasks; using Kiota.Builder.CodeDOM; -namespace Kiota.Builder.Diff; +namespace Kiota.Builder.Export; -internal class DomExportService +internal class PublicApiExportService {//TODO rename the service and the namespace to something more meaningful - internal DomExportService(string outputDirectoryPath) + internal PublicApiExportService(string outputDirectoryPath) { ArgumentException.ThrowIfNullOrEmpty(outputDirectoryPath); OutputDirectoryPath = outputDirectoryPath; diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index 5e6372b6e2..65d8bb350c 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -17,7 +17,7 @@ using Kiota.Builder.CodeDOM; using Kiota.Builder.CodeRenderers; using Kiota.Builder.Configuration; -using Kiota.Builder.Diff; +using Kiota.Builder.Export; using Kiota.Builder.EqualityComparers; using Kiota.Builder.Exceptions; using Kiota.Builder.Extensions; @@ -269,12 +269,14 @@ public async Task GenerateClientAsync(CancellationToken cancellationToken) await ApplyLanguageRefinement(config, generatedCode, cancellationToken).ConfigureAwait(false); StopLogAndReset(sw, $"step {++stepId} - refine by language - took"); - // Generate dom export - //TODO hide this behind an environment variable - sw.Start(); - var diffService = new DomExportService(config.OutputPath); - await diffService.SerializeDomAsync(generatedCode, cancellationToken).ConfigureAwait(false); - StopLogAndReset(sw, $"step {++stepId} - generated dom export - took"); + if (config.ExportPublicApi) + { + // Generate public API export + sw.Start(); + var diffService = new PublicApiExportService(config.OutputPath); + await diffService.SerializeDomAsync(generatedCode, cancellationToken).ConfigureAwait(false); + StopLogAndReset(sw, $"step {++stepId} - generated public API export - took"); + } // Write language source sw.Start(); diff --git a/src/kiota/KiotaConfigurationExtensions.cs b/src/kiota/KiotaConfigurationExtensions.cs index f0c6b89f44..8c212b694c 100644 --- a/src/kiota/KiotaConfigurationExtensions.cs +++ b/src/kiota/KiotaConfigurationExtensions.cs @@ -60,6 +60,7 @@ public static void BindConfiguration(this KiotaConfiguration configObject, IConf configObject.Generation.ClearCache = bool.TryParse(configuration[$"{nameof(configObject.Generation)}:{nameof(GenerationConfiguration.ClearCache)}"], out var clearCache) && clearCache; configObject.Generation.ExcludeBackwardCompatible = bool.TryParse(configuration[$"{nameof(configObject.Generation)}:{nameof(GenerationConfiguration.ExcludeBackwardCompatible)}"], out var excludeBackwardCompatible) && excludeBackwardCompatible; configObject.Generation.MaxDegreeOfParallelism = int.TryParse(configuration[$"{nameof(configObject.Generation)}:{nameof(GenerationConfiguration.MaxDegreeOfParallelism)}"], out var maxDegreeOfParallelism) ? maxDegreeOfParallelism : configObject.Generation.MaxDegreeOfParallelism; + configObject.Generation.ExportPublicApi = bool.TryParse(configuration[$"{nameof(configObject.Generation)}:{nameof(GenerationConfiguration.ExportPublicApi)}"], out var exportPublicApi) && exportPublicApi; configuration.GetSection($"{nameof(configObject.Generation)}:{nameof(GenerationConfiguration.StructuredMimeTypes)}").LoadCollection(configObject.Generation.StructuredMimeTypes); configuration.GetSection($"{nameof(configObject.Generation)}:{nameof(GenerationConfiguration.Serializers)}").LoadHashSet(configObject.Generation.Serializers); configuration.GetSection($"{nameof(configObject.Generation)}:{nameof(GenerationConfiguration.Deserializers)}").LoadHashSet(configObject.Generation.Deserializers); From 3720261caf6ed2bf508d01e471278507b4348ee2 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 23 Jul 2024 15:05:57 -0400 Subject: [PATCH 13/28] chore: comment removal Signed-off-by: Vincent Biret --- src/Kiota.Builder/Export/PublicAPIExportService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kiota.Builder/Export/PublicAPIExportService.cs b/src/Kiota.Builder/Export/PublicAPIExportService.cs index 90138c03bb..25b62e15dc 100644 --- a/src/Kiota.Builder/Export/PublicAPIExportService.cs +++ b/src/Kiota.Builder/Export/PublicAPIExportService.cs @@ -9,7 +9,7 @@ namespace Kiota.Builder.Export; internal class PublicApiExportService -{//TODO rename the service and the namespace to something more meaningful +{ internal PublicApiExportService(string outputDirectoryPath) { ArgumentException.ThrowIfNullOrEmpty(outputDirectoryPath); From e209e3f11ea175f6902cb96e7b42d920ba8e0826 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 2 Aug 2024 14:27:04 -0400 Subject: [PATCH 14/28] chore: formatting --- src/Kiota.Builder/KiotaBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index 65d8bb350c..54f67cfec1 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -17,9 +17,9 @@ using Kiota.Builder.CodeDOM; using Kiota.Builder.CodeRenderers; using Kiota.Builder.Configuration; -using Kiota.Builder.Export; using Kiota.Builder.EqualityComparers; using Kiota.Builder.Exceptions; +using Kiota.Builder.Export; using Kiota.Builder.Extensions; using Kiota.Builder.Logging; using Kiota.Builder.Manifest; From 4a1635c8e57f934015a68d0d968c9aceaafd5e07 Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Mon, 19 Aug 2024 10:34:59 +0300 Subject: [PATCH 15/28] Resolve PR comment --- src/Kiota.Builder/Export/PublicAPIExportService.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Kiota.Builder/Export/PublicAPIExportService.cs b/src/Kiota.Builder/Export/PublicAPIExportService.cs index 25b62e15dc..8e1af861d4 100644 --- a/src/Kiota.Builder/Export/PublicAPIExportService.cs +++ b/src/Kiota.Builder/Export/PublicAPIExportService.cs @@ -20,7 +20,9 @@ internal PublicApiExportService(string outputDirectoryPath) internal async Task SerializeDomAsync(CodeNamespace rootNamespace, CancellationToken cancellationToken = default) { var filePath = Path.Combine(OutputDirectoryPath, DomExportFileName); - using var fileStream = File.Create(filePath); +#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task + await using var fileStream = File.Create(filePath); +#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task var entries = GetEntriesFromDom(rootNamespace).Order(StringComparer.OrdinalIgnoreCase).ToArray(); var content = string.Join(Environment.NewLine, entries); var contentBytes = System.Text.Encoding.UTF8.GetBytes(content); From 41862118e1e1cc3d279480575d4f128303189f86 Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Mon, 19 Aug 2024 14:28:20 +0300 Subject: [PATCH 16/28] golang: ensure elements in code files have the namespace element extracted. --- .../Export/PublicAPIExportService.cs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Kiota.Builder/Export/PublicAPIExportService.cs b/src/Kiota.Builder/Export/PublicAPIExportService.cs index 8e1af861d4..0955fe8ead 100644 --- a/src/Kiota.Builder/Export/PublicAPIExportService.cs +++ b/src/Kiota.Builder/Export/PublicAPIExportService.cs @@ -17,16 +17,21 @@ internal PublicApiExportService(string outputDirectoryPath) } private readonly string OutputDirectoryPath; private const string DomExportFileName = "kiota-dom-export.txt"; + private const string InheritsSymbol = "-->"; + private const string ImplementsSymbol = "~~>"; + private const string OptionalSymbol = "?"; internal async Task SerializeDomAsync(CodeNamespace rootNamespace, CancellationToken cancellationToken = default) { var filePath = Path.Combine(OutputDirectoryPath, DomExportFileName); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task await using var fileStream = File.Create(filePath); + await using var streamWriter = new StreamWriter(fileStream); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task var entries = GetEntriesFromDom(rootNamespace).Order(StringComparer.OrdinalIgnoreCase).ToArray(); - var content = string.Join(Environment.NewLine, entries); - var contentBytes = System.Text.Encoding.UTF8.GetBytes(content); - await fileStream.WriteAsync(contentBytes, cancellationToken).ConfigureAwait(false); + foreach (var entry in entries) + { + await streamWriter.WriteLineAsync(entry.AsMemory(), cancellationToken).ConfigureAwait(false); + } } private static IEnumerable GetEntriesFromDom(CodeElement currentElement) { @@ -70,16 +75,14 @@ private static IEnumerable GetEntry(CodeElement codeElement, bool includ _ => [], }; } - private const string InheritsSymbol = "-->"; - private const string ImplementsSymbol = "~~>"; private static string GetParameters(IEnumerable parameters) { - return string.Join("; ", parameters.Select(static x => $"{x.Name}{(x.Optional ? "?" : string.Empty)}:{GetEntryType(x.Type)}{(string.IsNullOrEmpty(x.DefaultValue) ? string.Empty : $"={x.DefaultValue}")}")); + return string.Join("; ", parameters.Select(static x => $"{x.Name}{(x.Optional ? OptionalSymbol : string.Empty)}:{GetEntryType(x.Type)}{(string.IsNullOrEmpty(x.DefaultValue) ? string.Empty : $"={x.DefaultValue}")}")); } private static string GetEntryType(CodeTypeBase codeElementTypeBase) { - var collectionPrefix = codeElementTypeBase.IsArray ? "[" : codeElementTypeBase.CollectionKind is CodeTypeBase.CodeTypeCollectionKind.Complex ? "[" : string.Empty; - var collectionSuffix = codeElementTypeBase.IsArray ? "]" : codeElementTypeBase.CollectionKind is CodeTypeBase.CodeTypeCollectionKind.Complex ? "]" : string.Empty; + var collectionPrefix = codeElementTypeBase.IsArray || codeElementTypeBase.CollectionKind is CodeTypeBase.CodeTypeCollectionKind.Complex ? "[" : string.Empty; + var collectionSuffix = codeElementTypeBase.IsArray || codeElementTypeBase.CollectionKind is CodeTypeBase.CodeTypeCollectionKind.Complex ? "]" : string.Empty; //TODO use the collection types from the convention service return codeElementTypeBase switch { @@ -95,7 +98,8 @@ private static string GetEntryPath(CodeElement codeElement) CodeClass x when x.Parent is not null => $"{GetEntryPath(x.Parent)}.{codeElement.Name}", CodeEnum x when x.Parent is not null => $"{GetEntryPath(x.Parent)}.{codeElement.Name}", CodeInterface x when x.Parent is not null => $"{GetEntryPath(x.Parent)}.{codeElement.Name}", - CodeNamespace => $"{codeElement.Name}", + CodeFile x when x.Parent is CodeNamespace codeNamespace => GetEntryPath(codeNamespace), // get back the namespace names instead + CodeNamespace x => x.Name, _ => string.Empty, }; } From 4dcf5e64abf16a5ccd538498c1837960484b508f Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Mon, 19 Aug 2024 15:53:13 +0300 Subject: [PATCH 17/28] fix: TS does not generate getters and setters. This is making properties not show up in export as the are set as private by adding of the unused dom elements. In general TS export is smaller that C# as it has - no constructors - no generated derived requestConfiuration classes. - no withUrl method. --- src/Kiota.Builder/Refiners/TypeScriptRefiner.cs | 14 +------------- .../Writers/TypeScript/CodeMethodWriter.cs | 8 +------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs index 92a5618a92..2e79ddf9bf 100644 --- a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs +++ b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs @@ -79,16 +79,6 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance true, true ); - AddGetterAndSetterMethods(generatedCode, - [ - CodePropertyKind.Custom, - CodePropertyKind.AdditionalData, - ], - static (_, s) => s.ToCamelCase(UnderscoreArray), - false, - false, - string.Empty, - string.Empty); AddConstructorsForDefaultValues(generatedCode, true); cancellationToken.ThrowIfCancellationRequested(); var defaultConfiguration = new GenerationConfiguration(); @@ -1154,9 +1144,7 @@ private static void ProcessModelClassProperties(CodeClass modelClass, CodeInterf * Add properties to interfaces * Replace model classes by interfaces for property types */ - var serializationFunctions = GetSerializationFunctionsForNamespace(modelClass); - var serializer = serializationFunctions.Item1; - var deserializer = serializationFunctions.Item2; + var (serializer, deserializer) = GetSerializationFunctionsForNamespace(modelClass); foreach (var mProp in properties) { diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeMethodWriter.cs index 5261c960ac..7ef1f50958 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeMethodWriter.cs @@ -83,16 +83,10 @@ internal static void WriteMethodPrototypeInternal(CodeMethod code, LanguageWrite var asyncReturnTypePrefix = code.IsAsync ? "Promise<" : string.Empty; var asyncReturnTypeSuffix = code.IsAsync ? ">" : string.Empty; var nullableSuffix = code.ReturnType.IsNullable && !isVoid ? " | undefined" : string.Empty; - var accessorPrefix = code.Kind switch - { - CodeMethodKind.Getter => "get ", - CodeMethodKind.Setter => "set ", - _ => string.Empty - }; var shouldHaveTypeSuffix = !code.IsAccessor && !isConstructor && !string.IsNullOrEmpty(returnType); var returnTypeSuffix = shouldHaveTypeSuffix ? $" : {asyncReturnTypePrefix}{returnType}{nullableSuffix}{asyncReturnTypeSuffix}" : string.Empty; var openBracketSuffix = code.Parent is CodeClass || isFunction ? " {" : ";"; - writer.WriteLine($"{accessModifier}{functionPrefix}{accessorPrefix}{staticPrefix}{methodName}{(isFunction ? string.Empty : asyncPrefix)}({parameters}){returnTypeSuffix}{openBracketSuffix}"); + writer.WriteLine($"{accessModifier}{functionPrefix}{staticPrefix}{methodName}{(isFunction ? string.Empty : asyncPrefix)}({parameters}){returnTypeSuffix}{openBracketSuffix}"); } internal static void WriteMethodTypecheckIgnoreInternal(CodeMethod code, LanguageWriter writer) From a9d97782087bb3f74103ee1a60b92cb333914175 Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Tue, 20 Aug 2024 12:08:56 +0300 Subject: [PATCH 18/28] fixes PHP generation due to broken tree. --- .../Export/PublicAPIExportService.cs | 18 +++++++++++------- src/Kiota.Builder/Refiners/PhpRefiner.cs | 3 ++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Kiota.Builder/Export/PublicAPIExportService.cs b/src/Kiota.Builder/Export/PublicAPIExportService.cs index 0955fe8ead..2214e000c6 100644 --- a/src/Kiota.Builder/Export/PublicAPIExportService.cs +++ b/src/Kiota.Builder/Export/PublicAPIExportService.cs @@ -41,16 +41,18 @@ private static IEnumerable GetEntriesFromDom(CodeElement currentElement) foreach (var childElementEntry in GetEntriesFromDom(childElement)) yield return childElementEntry; } + private static IEnumerable GetEntry(CodeElement codeElement, bool includeDefinitions = false) { - string accessModifierValue; + string accessModifierValue = string.Empty; if (codeElement is IAccessibleElement accessibleElement) + { if (accessibleElement.Access is AccessModifier.Private) - return []; - else - accessModifierValue = $"|{accessibleElement.Access.ToString().ToLowerInvariant()}|"; - else - accessModifierValue = string.Empty; + return []; // we are not interested in private props as they are not used externally + + accessModifierValue = $"|{accessibleElement.Access.ToString().ToLowerInvariant()}|"; + } + return codeElement switch { CodeProperty property when property.Parent is not null => @@ -72,6 +74,7 @@ private static IEnumerable GetEntry(CodeElement codeElement, bool includ CodeClass codeClass when includeDefinitions => [GetEntryPath(codeClass)], CodeEnum codeEnum when includeDefinitions => [GetEntryPath(codeEnum)], CodeInterface codeInterface when includeDefinitions => [GetEntryPath(codeInterface)], + CodeConstant codeConstant => [GetEntryPath(codeConstant)], _ => [], }; } @@ -98,7 +101,8 @@ private static string GetEntryPath(CodeElement codeElement) CodeClass x when x.Parent is not null => $"{GetEntryPath(x.Parent)}.{codeElement.Name}", CodeEnum x when x.Parent is not null => $"{GetEntryPath(x.Parent)}.{codeElement.Name}", CodeInterface x when x.Parent is not null => $"{GetEntryPath(x.Parent)}.{codeElement.Name}", - CodeFile x when x.Parent is CodeNamespace codeNamespace => GetEntryPath(codeNamespace), // get back the namespace names instead + CodeConstant x when x.Parent is not null => $"{GetEntryPath(x.Parent)}.{codeElement.Name}", + CodeFile x when x.Parent is not null => GetEntryPath(x.Parent), CodeNamespace x => x.Name, _ => string.Empty, }; diff --git a/src/Kiota.Builder/Refiners/PhpRefiner.cs b/src/Kiota.Builder/Refiners/PhpRefiner.cs index 95985de135..1fab2f08f1 100644 --- a/src/Kiota.Builder/Refiners/PhpRefiner.cs +++ b/src/Kiota.Builder/Refiners/PhpRefiner.cs @@ -433,6 +433,7 @@ private static void AddQueryParameterFactoryMethod(CodeElement codeElement) var queryParameterProperty = codeClass.GetPropertyOfKind(CodePropertyKind.QueryParameters); if (queryParameterProperty != null) { + var returnTypeCodeType = queryParameterProperty.Type as CodeType; var queryParamFactoryMethod = new CodeMethod { Name = "createQueryParameters", @@ -445,7 +446,7 @@ private static void AddQueryParameterFactoryMethod(CodeElement codeElement) { DescriptionTemplate = "Instantiates a new {TypeName}." }, - ReturnType = new CodeType { Name = queryParameterProperty.Type.Name, TypeDefinition = queryParameterProperty.Type, IsNullable = false } + ReturnType = new CodeType { Name = queryParameterProperty.Type.Name, TypeDefinition = returnTypeCodeType?.TypeDefinition ?? queryParameterProperty.Type, IsNullable = false } }; if (queryParameterProperty.Type is CodeType codeType && codeType.TypeDefinition is CodeClass queryParamsClass) { From 01210123e6464374e60812090857b3d53356d7eb Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Tue, 20 Aug 2024 18:00:56 +0300 Subject: [PATCH 19/28] adds validation tests. --- .../Export/PublicAPIExportServiceTests.cs | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs diff --git a/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs b/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs new file mode 100644 index 0000000000..bce955132f --- /dev/null +++ b/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Kiota.Builder.Configuration; +using Kiota.Builder.Export; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace Kiota.Builder.Tests.Export; + +public class PublicApiExportServiceTests +{ + private readonly HttpClient _httpClient = new(); + private static Task GetTestDocumentStream() + { + return KiotaBuilderTests.GetDocumentStream(@"openapi: 3.0.0 +info: + title: Microsoft Graph get user API + version: 1.0.0 +servers: + - url: https://graph.microsoft.com/v1.0/ +paths: + /me: + get: + responses: + 200: + description: Success! + content: + application/json: + schema: + $ref: '#/components/schemas/microsoft.graph.user' + /me/get: + get: + responses: + 200: + description: Success! + content: + application/json: + schema: + $ref: '#/components/schemas/microsoft.graph.user' +components: + schemas: + microsoft.graph.user: + type: object + properties: + id: + type: string + displayName: + type: string + importance: + $ref: '#/components/schemas/microsoft.graph.importance' + microsoft.graph.importance: + title: importance + enum: + - low + - normal + - high + type: string"); + } + + [Fact] + public void Defensive() + { + Assert.Throws(() => new PublicApiExportService(null)); + Assert.Throws(() => new PublicApiExportService(string.Empty)); + } + + private static readonly Dictionary> Validators = new() + { + { GenerationLanguage.CSharp, ValidateExportCSharp }, + { GenerationLanguage.Go, ValidateExportGo }, + { GenerationLanguage.Python, ValidateExportPython }, + { GenerationLanguage.TypeScript, ValidateExportTypeScript }, + { GenerationLanguage.Java, ValidateExportJava }, + { GenerationLanguage.PHP, ValidateExportPhp }, + }; + + [Theory] + [InlineData(GenerationLanguage.CSharp)] + [InlineData(GenerationLanguage.Go)] + [InlineData(GenerationLanguage.Python)] + [InlineData(GenerationLanguage.TypeScript)] + [InlineData(GenerationLanguage.Java)] + [InlineData(GenerationLanguage.PHP)] + public async Task GeneratesExportsAndFileHasExpectedAssertions(GenerationLanguage generationLanguage) + { + var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + await using var testDocumentStream = await GetTestDocumentStream(); + var mockLogger = new Mock>(); + var generationConfig = new GenerationConfiguration + { + ClientClassName = "Graph", + OpenAPIFilePath = tempFilePath, + Language = generationLanguage, + ClientNamespaceName = "exportNamespace" + }; + var builder = new KiotaBuilder(mockLogger.Object, generationConfig, _httpClient); + var document = await builder.CreateOpenApiDocumentAsync(testDocumentStream); + + Assert.NotNull(document); + + var node = builder.CreateUriSpace(document); + var codeModel = builder.CreateSourceModel(node); + await builder.ApplyLanguageRefinement(generationConfig, codeModel, default); + + // serialize the dom model + var exportService = new PublicApiExportService(Path.GetTempPath()); + await exportService.SerializeDomAsync(codeModel); + + // validate the export exists + var exportPath = Path.Join(Path.GetTempPath(), "kiota-dom-export.txt"); + Assert.True(File.Exists(exportPath)); + if (!Validators.TryGetValue(generationLanguage, out var validator)) + { + Assert.Fail($"No Validator present for language {generationLanguage}"); + } + // run the language validator + var contents = File.ReadLines(exportPath).ToArray(); + validator.Invoke(contents); + } + + private static void ValidateExportCSharp(string[] exportContents) + { + Assert.NotEmpty(exportContents); + Assert.Contains("ExportNamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance + Assert.Contains("ExportNamespace.Models.Microsoft.Graph.user~~>IAdditionalDataHolder; IParsable", exportContents);// captures implemented interfaces + Assert.Contains("ExportNamespace.Models.Microsoft.Graph.user::|public|Id:string", exportContents);// captures property location,type and access + Assert.Contains("ExportNamespace.Me.Get.getRequestBuilder::|public|constructor(rawUrl:string; requestAdapter:IRequestAdapter):void", exportContents); // captures constructors, their parameters(name and types), return and access + Assert.Contains("ExportNamespace.Me.Get.getRequestBuilder::|public|ToGetRequestInformation(requestConfiguration?:RequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("ExportNamespace.Models.Microsoft.Graph.user::|static|public|CreateFromDiscriminatorValue(parseNode:IParseNode):ExportNamespace.Models.Microsoft.Graph.user", exportContents);// captures static methods too :) + Assert.Contains("ExportNamespace.Models.Microsoft.Graph.importance::0000-low", exportContents);// captures enum members + } + + private static void ValidateExportJava(string[] exportContents) + { + Assert.NotEmpty(exportContents); + Assert.Contains("exportnamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance + Assert.Contains("exportnamespace.models.microsoft.graph.User~~>AdditionalDataHolder; Parsable", exportContents);// captures implemented interfaces + Assert.Contains("exportnamespace.models.microsoft.graph.User::|public|setId(value?:String):void", exportContents);// captures property setter location,type and access + Assert.Contains("exportnamespace.models.microsoft.graph.User::|public|getId():String", exportContents);// captures property getter location,type and access + Assert.Contains("exportnamespace.models.microsoft.graph.User::|public|constructor():void", exportContents); // captures constructors, their parameters(name and types), return and access + Assert.Contains("exportnamespace.me.MeRequestBuilder::|public|toGetRequestInformation(requestConfiguration?:exportnamespace.me.MeRequestBuilder.GetRequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("exportnamespace.models.microsoft.graph.User::|static|public|createFromDiscriminatorValue(parseNode:ParseNode):exportnamespace.models.microsoft.graph.User", exportContents);// captures static methods too :) + Assert.Contains("exportnamespace.models.microsoft.graph.Importance::0000-Low", exportContents);// captures enum members + } + + private static void ValidateExportGo(string[] exportContents) + { + Assert.NotEmpty(exportContents); + Assert.Contains("exportNamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance + Assert.Contains("exportNamespace.models.microsoft.graph.userable~~>AdditionalDataHolder; Parsable", exportContents);// captures implemented interfaces + Assert.Contains("exportNamespace.models.microsoft.graph.user~~>exportNamespace.models.microsoft.graph.userable", exportContents);// captures implemented MODEL interfaces + Assert.Contains("exportNamespace.models.microsoft.graph.userable::|public|GetId():string", exportContents);// captures property getter location,type and access inheritance + Assert.Contains("exportNamespace.models.microsoft.graph.userable::|public|SetId(value:string):void", exportContents);// captures property setter location,type and access inheritance + Assert.Contains("exportNamespace.me.GetRequestBuilder::|public|constructor(rawUrl:string; requestAdapter:RequestAdapter):void", exportContents); // captures constructors, their parameters(name and types), return and access + Assert.Contains("exportNamespace.me.GetRequestBuilder::|public|ToGetRequestInformation(ctx:context.Context; requestConfiguration?:exportNamespace.me.GetRequestBuilder.GetRequestBuilderGetRequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("exportNamespace.models.microsoft.graph.user::|static|public|CreateFromDiscriminatorValue(parseNode:ParseNode):Parsable", exportContents);// captures static methods too :) + Assert.Contains("exportNamespace.models.microsoft.graph.importance::0000-low", exportContents);// captures enum members + } + + private static void ValidateExportPython(string[] exportContents) + { + Assert.NotEmpty(exportContents); + Assert.Contains("exportNamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance + Assert.Contains("exportNamespace.models.microsoft.graph.User~~>AdditionalDataHolder; Parsable", exportContents);// captures implemented interfaces + Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|id():String", exportContents);// captures property getter location,type and access inheritance + Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|id(value:String):void", exportContents);// captures property setter location,type and access inheritance + Assert.Contains("exportNamespace.me.MeRequestBuilder::|public|constructor(path_parameters:Union[str, Dict[str, Any]]; request_adapter:RequestAdapter):void", exportContents); // captures constructors, their parameters(name and types), return and access + Assert.Contains("exportNamespace.me.get.GetRequestBuilder::|public|to_get_request_information(request_configuration?:RequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("exportNamespace.models.microsoft.graph.User::|static|public|create_from_discriminator_value(parse_node:ParseNode):exportNamespace.models.microsoft.graph.User", exportContents);// captures static methods too :) + Assert.Contains("exportNamespace.models.microsoft.graph.Importance::0000-Low", exportContents);// captures enum members + } + + private static void ValidateExportTypeScript(string[] exportContents) + { + Assert.NotEmpty(exportContents); + Assert.Contains("exportNamespace.Graph~~>BaseRequestBuilder", exportContents); // captures class inheritance. TS does not do inheritance due to interfaces. + Assert.Contains("exportNamespace.models.microsoft.graph.User~~>AdditionalDataHolder; Parsable", exportContents);// captures implemented interfaces + Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|id:string", exportContents);// captures property location,type and access inheritance. No getter/setter in TS + // NOTE: No constructors in TS + Assert.Contains("exportNamespace.me.meRequestBuilder::|public|ToGetRequestInformation(requestConfiguration?:RequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("exportNamespace.models.microsoft.graph::createUserFromDiscriminatorValue(parseNode:ParseNode):exportNamespace.models.microsoft.graph.user", exportContents);// captures code functions + Assert.Contains("exportNamespace.models.microsoft.graph::deserializeIntoUser(User:exportNamespace.models.microsoft.graph.User={}):Record void>", exportContents);// captures code functions and default params + Assert.Contains("exportNamespace.models.microsoft.graph.importance::0000-low", exportContents);// captures enum members + Assert.Contains("exportNamespace.models.microsoft.graph.importanceObject", exportContents);// captures enum code constant object + Assert.Contains("exportNamespace.graphNavigationMetadata", exportContents);// captures navigation metadata code constant object + Assert.Contains("exportNamespace.me.get.getRequestBuilderRequestsMetadata", exportContents);// captures request builder metadata code constant object + } + + private static void ValidateExportPhp(string[] exportContents) + { + Assert.NotEmpty(exportContents); + Assert.Contains("exportNamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance + Assert.Contains("exportNamespace.models.microsoft.graph.user~~>AdditionalDataHolder; Parsable", exportContents);// captures implemented interfaces + Assert.Contains("exportNamespace.models.microsoft.graph.user::|public|getId():string", exportContents);// captures property getter location,type and access inheritance + Assert.Contains("exportNamespace.models.microsoft.graph.user::|public|setId(value?:string):void", exportContents);// captures property setter location,type and access inheritance + Assert.Contains("exportNamespace.me.get.getRequestBuilderGetRequestConfiguration::|public|constructor(headers?:[array]; options?:[array]):void", exportContents); // captures constructors, their parameters(name and types), return and access + Assert.Contains("exportNamespace.me.MeRequestBuilder::|public|constructor(pathParameters:[array]; requestAdapter:RequestAdapter):void", exportContents); // captures constructors, their parameters(name and types), return and access + Assert.Contains("exportNamespace.me.get.GetRequestBuilder::|public|ToGetRequestInformation(requestConfiguration?:exportNamespace.me.get.getRequestBuilderGetRequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("exportNamespace.models.microsoft.graph.user::|static|public|CreateFromDiscriminatorValue(parseNode:ParseNode):exportNamespace.models.microsoft.graph.user", exportContents);// captures static methods too :) + Assert.Contains("exportNamespace.models.microsoft.graph.importance::0000-low", exportContents);// captures enum members + } +} From 8d55991ef135839a0299f193bee0bb9cc2ad1b27 Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Tue, 20 Aug 2024 18:03:40 +0300 Subject: [PATCH 20/28] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e439f607f..bb23f8a6c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added the ability to export the CodeDom to a file showing the public APIs to be generated in a given language [#4627](https://github.com/microsoft/kiota/issues/4627) + ### Changed - Fixed an issue where models would be missing when they had no properties and a single allOf entry. [#5014](https://github.com/microsoft/kiota/issues/5014) From c42e67b5cae2acd55c39281b12e8967ce5b364ce Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Wed, 21 Aug 2024 13:23:39 +0300 Subject: [PATCH 21/28] more language specific improvements on type information --- .../Export/PublicAPIExportService.cs | 78 ++++++++++++------- src/Kiota.Builder/KiotaBuilder.cs | 2 +- .../Export/PublicAPIExportServiceTests.cs | 69 +++++++++------- 3 files changed, 94 insertions(+), 55 deletions(-) diff --git a/src/Kiota.Builder/Export/PublicAPIExportService.cs b/src/Kiota.Builder/Export/PublicAPIExportService.cs index 2214e000c6..b8b3b9caf8 100644 --- a/src/Kiota.Builder/Export/PublicAPIExportService.cs +++ b/src/Kiota.Builder/Export/PublicAPIExportService.cs @@ -5,24 +5,38 @@ using System.Threading; using System.Threading.Tasks; using Kiota.Builder.CodeDOM; +using Kiota.Builder.Configuration; +using Kiota.Builder.Writers; +using Kiota.Builder.Writers.CSharp; +using Kiota.Builder.Writers.Go; +using Kiota.Builder.Writers.Java; +using Kiota.Builder.Writers.Php; +using Kiota.Builder.Writers.Python; +using Kiota.Builder.Writers.Ruby; +using Kiota.Builder.Writers.Swift; +using Kiota.Builder.Writers.TypeScript; namespace Kiota.Builder.Export; internal class PublicApiExportService { - internal PublicApiExportService(string outputDirectoryPath) + internal PublicApiExportService(GenerationConfiguration generationConfiguration) { - ArgumentException.ThrowIfNullOrEmpty(outputDirectoryPath); - OutputDirectoryPath = outputDirectoryPath; + ArgumentNullException.ThrowIfNull(generationConfiguration); + _outputDirectoryPath = generationConfiguration.OutputPath; + _conventionService = GetLanguageConventionServiceFromConfiguration(generationConfiguration); } - private readonly string OutputDirectoryPath; + private readonly string _outputDirectoryPath; + private readonly ILanguageConventionService _conventionService; private const string DomExportFileName = "kiota-dom-export.txt"; private const string InheritsSymbol = "-->"; private const string ImplementsSymbol = "~~>"; private const string OptionalSymbol = "?"; + private const string ParentElementAndChildSeparator = "::"; + internal async Task SerializeDomAsync(CodeNamespace rootNamespace, CancellationToken cancellationToken = default) { - var filePath = Path.Combine(OutputDirectoryPath, DomExportFileName); + var filePath = Path.Combine(_outputDirectoryPath, DomExportFileName); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task await using var fileStream = File.Create(filePath); await using var streamWriter = new StreamWriter(fileStream); @@ -33,7 +47,7 @@ internal async Task SerializeDomAsync(CodeNamespace rootNamespace, CancellationT await streamWriter.WriteLineAsync(entry.AsMemory(), cancellationToken).ConfigureAwait(false); } } - private static IEnumerable GetEntriesFromDom(CodeElement currentElement) + private IEnumerable GetEntriesFromDom(CodeElement currentElement) { foreach (var currentElementEntry in GetEntry(currentElement).Where(static x => !string.IsNullOrEmpty(x))) yield return currentElementEntry; @@ -42,7 +56,7 @@ private static IEnumerable GetEntriesFromDom(CodeElement currentElement) yield return childElementEntry; } - private static IEnumerable GetEntry(CodeElement codeElement, bool includeDefinitions = false) + private IEnumerable GetEntry(CodeElement codeElement, bool includeDefinitions = false) { string accessModifierValue = string.Empty; if (codeElement is IAccessibleElement accessibleElement) @@ -56,21 +70,21 @@ private static IEnumerable GetEntry(CodeElement codeElement, bool includ return codeElement switch { CodeProperty property when property.Parent is not null => - [$"{GetEntryPath(property.Parent)}::{accessModifierValue}{property.Name}:{GetEntryType(property.Type)}"], + [$"{GetEntryPath(property.Parent)}{ParentElementAndChildSeparator}{accessModifierValue}{property.Name}:{GetEntryType(property.Type, property)}"], CodeMethod method when method.Parent is not null => - [$"{GetEntryPath(method.Parent)}::{(method.IsStatic ? "|static" : string.Empty)}{accessModifierValue}{method.Name}({GetParameters(method.Parameters)}):{GetEntryType(method.ReturnType)}"], + [$"{GetEntryPath(method.Parent)}{ParentElementAndChildSeparator}{(method.IsStatic ? "|static" : string.Empty)}{accessModifierValue}{method.Name}({GetParameters(method.Parameters)}):{((GetEntryType(method.ReturnType, method) is { } stringValue && !string.IsNullOrEmpty(stringValue)) ? stringValue : method.ReturnType.Name)}"], CodeFunction function when function.Parent is not null => - [$"{GetEntryPath(function.Parent)}::{function.Name}({GetParameters(function.OriginalLocalMethod.Parameters)}):{GetEntryType(function.OriginalLocalMethod.ReturnType)}"], + [$"{GetEntryPath(function.Parent)}{ParentElementAndChildSeparator}{function.Name}({GetParameters(function.OriginalLocalMethod.Parameters)}):{GetEntryType(function.OriginalLocalMethod.ReturnType, function)}"], CodeIndexer codeIndexer when codeIndexer.Parent is not null => - [$"{GetEntryPath(codeIndexer.Parent)}::[{GetParameters([codeIndexer.IndexParameter])}]:{GetEntryType(codeIndexer.ReturnType)}"], + [$"{GetEntryPath(codeIndexer.Parent)}{ParentElementAndChildSeparator}[{GetParameters([codeIndexer.IndexParameter])}]:{GetEntryType(codeIndexer.ReturnType, codeIndexer)}"], CodeEnum codeEnum1 when !includeDefinitions => codeEnum1.Options.Select((x, y) => $"{GetEntryPath(codeEnum1)}::{y:D4}-{x.Name}"), CodeClass codeClass1 when !includeDefinitions && codeClass1.StartBlock.Inherits is not null => - [$"{GetEntryPath(codeClass1)}{InheritsSymbol}{GetEntryType(codeClass1.StartBlock.Inherits)}"], + [$"{GetEntryPath(codeClass1)}{InheritsSymbol}{GetEntryType(codeClass1.StartBlock.Inherits, codeClass1)}"], CodeClass codeClass2 when !includeDefinitions && codeClass2.StartBlock.Implements.Any() => - [$"{GetEntryPath(codeClass2)}{ImplementsSymbol}{string.Join("; ", codeClass2.StartBlock.Implements.Select(static x => GetEntryType(x)))}"], + [$"{GetEntryPath(codeClass2)}{ImplementsSymbol}{string.Join("; ", codeClass2.StartBlock.Implements.Select(x => GetEntryType(x, codeClass2)))}"], CodeInterface codeInterface1 when !includeDefinitions && codeInterface1.StartBlock.Implements.Any() => - [$"{GetEntryPath(codeInterface1)}{ImplementsSymbol}{string.Join("; ", codeInterface1.StartBlock.Implements.Select(static x => GetEntryType(x)))}"], + [$"{GetEntryPath(codeInterface1)}{ImplementsSymbol}{string.Join("; ", codeInterface1.StartBlock.Implements.Select(x => GetEntryType(x, codeInterface1)))}"], CodeClass codeClass when includeDefinitions => [GetEntryPath(codeClass)], CodeEnum codeEnum when includeDefinitions => [GetEntryPath(codeEnum)], CodeInterface codeInterface when includeDefinitions => [GetEntryPath(codeInterface)], @@ -78,22 +92,14 @@ private static IEnumerable GetEntry(CodeElement codeElement, bool includ _ => [], }; } - private static string GetParameters(IEnumerable parameters) - { - return string.Join("; ", parameters.Select(static x => $"{x.Name}{(x.Optional ? OptionalSymbol : string.Empty)}:{GetEntryType(x.Type)}{(string.IsNullOrEmpty(x.DefaultValue) ? string.Empty : $"={x.DefaultValue}")}")); - } - private static string GetEntryType(CodeTypeBase codeElementTypeBase) + private string GetParameters(IEnumerable parameters) { - var collectionPrefix = codeElementTypeBase.IsArray || codeElementTypeBase.CollectionKind is CodeTypeBase.CodeTypeCollectionKind.Complex ? "[" : string.Empty; - var collectionSuffix = codeElementTypeBase.IsArray || codeElementTypeBase.CollectionKind is CodeTypeBase.CodeTypeCollectionKind.Complex ? "]" : string.Empty; - //TODO use the collection types from the convention service - return codeElementTypeBase switch - { - CodeType codeElementType when codeElementType.TypeDefinition is not null => $"{collectionPrefix}{GetEntry(codeElementType.TypeDefinition, true).First()}{collectionSuffix}", - CodeType codeElementType when codeElementType.TypeDefinition is null => $"{collectionPrefix}{codeElementType.Name}{collectionSuffix}", - _ => $"{collectionPrefix}{codeElementTypeBase.Name}{collectionSuffix}", - }; + return string.Join("; ", parameters.Select(x => $"{x.Name}{(x.Optional ? OptionalSymbol : string.Empty)}:{_conventionService.GetTypeString(x.Type, x)}{(string.IsNullOrEmpty(x.DefaultValue) ? string.Empty : $"={x.DefaultValue}")}")); } + + private string GetEntryType(CodeTypeBase codeElementTypeBase, CodeElement targetElement) => _conventionService.GetTypeString(codeElementTypeBase, targetElement) + .Replace(ParentElementAndChildSeparator, ".", StringComparison.OrdinalIgnoreCase);//ensure language specific stuff doesn't break things like global:: in dotnet + private static string GetEntryPath(CodeElement codeElement) { return codeElement switch @@ -107,4 +113,20 @@ private static string GetEntryPath(CodeElement codeElement) _ => string.Empty, }; } + private static ILanguageConventionService GetLanguageConventionServiceFromConfiguration(GenerationConfiguration generationConfiguration) + { + return generationConfiguration.Language switch + { + GenerationLanguage.CSharp => new CSharpConventionService(), + GenerationLanguage.Java => new JavaConventionService(), + GenerationLanguage.TypeScript => new TypeScriptConventionService(), + GenerationLanguage.PHP => new PhpConventionService(), + GenerationLanguage.Python => new PythonConventionService(), + GenerationLanguage.Go => new GoConventionService(), + GenerationLanguage.Swift => new SwiftConventionService(generationConfiguration.ClientNamespaceName), + GenerationLanguage.Ruby => new RubyConventionService(), + GenerationLanguage.CLI => new CSharpConventionService(), + _ => throw new ArgumentOutOfRangeException(nameof(generationConfiguration), generationConfiguration.Language, null) + }; + } } diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index 14c1cef7da..6e455c2224 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -273,7 +273,7 @@ public async Task GenerateClientAsync(CancellationToken cancellationToken) { // Generate public API export sw.Start(); - var diffService = new PublicApiExportService(config.OutputPath); + var diffService = new PublicApiExportService(config); await diffService.SerializeDomAsync(generatedCode, cancellationToken).ConfigureAwait(false); StopLogAndReset(sw, $"step {++stepId} - generated public API export - took"); } diff --git a/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs b/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs index bce955132f..e8c9718c55 100644 --- a/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs @@ -51,6 +51,11 @@ private static Task GetTestDocumentStream() type: string displayName: type: string + otherNames: + type: array + items: + type: string + nullable: true importance: $ref: '#/components/schemas/microsoft.graph.importance' microsoft.graph.importance: @@ -66,7 +71,6 @@ private static Task GetTestDocumentStream() public void Defensive() { Assert.Throws(() => new PublicApiExportService(null)); - Assert.Throws(() => new PublicApiExportService(string.Empty)); } private static readonly Dictionary> Validators = new() @@ -96,7 +100,8 @@ public async Task GeneratesExportsAndFileHasExpectedAssertions(GenerationLanguag ClientClassName = "Graph", OpenAPIFilePath = tempFilePath, Language = generationLanguage, - ClientNamespaceName = "exportNamespace" + ClientNamespaceName = "exportNamespace", + OutputPath = Path.GetTempPath() }; var builder = new KiotaBuilder(mockLogger.Object, generationConfig, _httpClient); var document = await builder.CreateOpenApiDocumentAsync(testDocumentStream); @@ -108,7 +113,7 @@ public async Task GeneratesExportsAndFileHasExpectedAssertions(GenerationLanguag await builder.ApplyLanguageRefinement(generationConfig, codeModel, default); // serialize the dom model - var exportService = new PublicApiExportService(Path.GetTempPath()); + var exportService = new PublicApiExportService(generationConfig); await exportService.SerializeDomAsync(codeModel); // validate the export exists @@ -130,9 +135,10 @@ private static void ValidateExportCSharp(string[] exportContents) Assert.Contains("ExportNamespace.Models.Microsoft.Graph.user~~>IAdditionalDataHolder; IParsable", exportContents);// captures implemented interfaces Assert.Contains("ExportNamespace.Models.Microsoft.Graph.user::|public|Id:string", exportContents);// captures property location,type and access Assert.Contains("ExportNamespace.Me.Get.getRequestBuilder::|public|constructor(rawUrl:string; requestAdapter:IRequestAdapter):void", exportContents); // captures constructors, their parameters(name and types), return and access - Assert.Contains("ExportNamespace.Me.Get.getRequestBuilder::|public|ToGetRequestInformation(requestConfiguration?:RequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access - Assert.Contains("ExportNamespace.Models.Microsoft.Graph.user::|static|public|CreateFromDiscriminatorValue(parseNode:IParseNode):ExportNamespace.Models.Microsoft.Graph.user", exportContents);// captures static methods too :) + Assert.Contains("ExportNamespace.Me.Get.getRequestBuilder::|public|ToGetRequestInformation(requestConfiguration?:Action>):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("ExportNamespace.Models.Microsoft.Graph.user::|static|public|CreateFromDiscriminatorValue(parseNode:IParseNode):global.ExportNamespace.Models.Microsoft.Graph.User", exportContents);// captures static methods too :) Assert.Contains("ExportNamespace.Models.Microsoft.Graph.importance::0000-low", exportContents);// captures enum members + Assert.Contains("ExportNamespace.Models.Microsoft.Graph.user::|public|OtherNames:List", exportContents);// captures collection info in language specific format } private static void ValidateExportJava(string[] exportContents) @@ -143,23 +149,29 @@ private static void ValidateExportJava(string[] exportContents) Assert.Contains("exportnamespace.models.microsoft.graph.User::|public|setId(value?:String):void", exportContents);// captures property setter location,type and access Assert.Contains("exportnamespace.models.microsoft.graph.User::|public|getId():String", exportContents);// captures property getter location,type and access Assert.Contains("exportnamespace.models.microsoft.graph.User::|public|constructor():void", exportContents); // captures constructors, their parameters(name and types), return and access - Assert.Contains("exportnamespace.me.MeRequestBuilder::|public|toGetRequestInformation(requestConfiguration?:exportnamespace.me.MeRequestBuilder.GetRequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access - Assert.Contains("exportnamespace.models.microsoft.graph.User::|static|public|createFromDiscriminatorValue(parseNode:ParseNode):exportnamespace.models.microsoft.graph.User", exportContents);// captures static methods too :) + Assert.Contains("exportnamespace.me.MeRequestBuilder::|public|toGetRequestInformation(requestConfiguration?:java.util.function.Consumer):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("exportnamespace.models.microsoft.graph.User::|static|public|createFromDiscriminatorValue(parseNode:ParseNode):User", exportContents);// captures static methods too :) Assert.Contains("exportnamespace.models.microsoft.graph.Importance::0000-Low", exportContents);// captures enum members + Assert.Contains("exportnamespace.models.microsoft.graph.User::|public|getOtherNames():java.util.List", exportContents);// captures collection info in language specific format + Assert.Contains("exportnamespace.models.microsoft.graph.User::|public|setOtherNames(value?:java.util.List):void", exportContents);// captures collection info in language specific format } private static void ValidateExportGo(string[] exportContents) { Assert.NotEmpty(exportContents); - Assert.Contains("exportNamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance - Assert.Contains("exportNamespace.models.microsoft.graph.userable~~>AdditionalDataHolder; Parsable", exportContents);// captures implemented interfaces - Assert.Contains("exportNamespace.models.microsoft.graph.user~~>exportNamespace.models.microsoft.graph.userable", exportContents);// captures implemented MODEL interfaces - Assert.Contains("exportNamespace.models.microsoft.graph.userable::|public|GetId():string", exportContents);// captures property getter location,type and access inheritance - Assert.Contains("exportNamespace.models.microsoft.graph.userable::|public|SetId(value:string):void", exportContents);// captures property setter location,type and access inheritance + Assert.Contains("exportNamespace.Graph-->*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.BaseRequestBuilder", exportContents); // captures class inheritance + Assert.Contains("exportNamespace.models.microsoft.graph.userable~~>*i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder; *i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable", exportContents);// captures implemented interfaces + Assert.Contains("exportNamespace.models.microsoft.graph.user~~>Userable", exportContents);// captures implemented MODEL interfaces + Assert.Contains("exportNamespace.models.microsoft.graph.userable::|public|GetId():*string", exportContents);// captures property getter location,type and access inheritance + Assert.Contains("exportNamespace.models.microsoft.graph.userable::|public|SetId(value:*string):void", exportContents);// captures property setter location,type and access inheritance + Assert.Contains("exportNamespace.models.microsoft.graph.user::|public|GetId():*string", exportContents);// captures property getter location,type and access inheritance + Assert.Contains("exportNamespace.models.microsoft.graph.user::|public|SetId(value:*string):void", exportContents);// captures property setter location,type and access inheritance Assert.Contains("exportNamespace.me.GetRequestBuilder::|public|constructor(rawUrl:string; requestAdapter:RequestAdapter):void", exportContents); // captures constructors, their parameters(name and types), return and access - Assert.Contains("exportNamespace.me.GetRequestBuilder::|public|ToGetRequestInformation(ctx:context.Context; requestConfiguration?:exportNamespace.me.GetRequestBuilder.GetRequestBuilderGetRequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("exportNamespace.me.GetRequestBuilder::|public|ToGetRequestInformation(ctx:context.Context; requestConfiguration?:*GetRequestBuilderGetRequestConfiguration):*RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access Assert.Contains("exportNamespace.models.microsoft.graph.user::|static|public|CreateFromDiscriminatorValue(parseNode:ParseNode):Parsable", exportContents);// captures static methods too :) Assert.Contains("exportNamespace.models.microsoft.graph.importance::0000-low", exportContents);// captures enum members + Assert.Contains("exportNamespace.models.microsoft.graph.user::|public|GetOtherNames():[]string", exportContents);// captures collection info in language specific format + Assert.Contains("exportNamespace.models.microsoft.graph.user::|public|SetOtherNames(value:[]string):void", exportContents);// captures collection info in language specific format } private static void ValidateExportPython(string[] exportContents) @@ -167,28 +179,31 @@ private static void ValidateExportPython(string[] exportContents) Assert.NotEmpty(exportContents); Assert.Contains("exportNamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance Assert.Contains("exportNamespace.models.microsoft.graph.User~~>AdditionalDataHolder; Parsable", exportContents);// captures implemented interfaces - Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|id():String", exportContents);// captures property getter location,type and access inheritance - Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|id(value:String):void", exportContents);// captures property setter location,type and access inheritance - Assert.Contains("exportNamespace.me.MeRequestBuilder::|public|constructor(path_parameters:Union[str, Dict[str, Any]]; request_adapter:RequestAdapter):void", exportContents); // captures constructors, their parameters(name and types), return and access - Assert.Contains("exportNamespace.me.get.GetRequestBuilder::|public|to_get_request_information(request_configuration?:RequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access - Assert.Contains("exportNamespace.models.microsoft.graph.User::|static|public|create_from_discriminator_value(parse_node:ParseNode):exportNamespace.models.microsoft.graph.User", exportContents);// captures static methods too :) + Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|id():str", exportContents);// captures property getter location,type and access inheritance + Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|id(value:str):None", exportContents);// captures property setter location,type and access inheritance + Assert.Contains("exportNamespace.me.MeRequestBuilder::|public|constructor(path_parameters:Union[str, Dict[str, Any]]; request_adapter:RequestAdapter):None", exportContents); // captures constructors, their parameters(name and types), return and access + Assert.Contains("exportNamespace.me.get.GetRequestBuilder::|public|to_get_request_information(request_configuration?:RequestConfiguration[QueryParameters]):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("exportNamespace.models.microsoft.graph.User::|static|public|create_from_discriminator_value(parse_node:ParseNode):User", exportContents);// captures static methods too :) Assert.Contains("exportNamespace.models.microsoft.graph.Importance::0000-Low", exportContents);// captures enum members + Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|other_names():List[str]", exportContents);// captures collection info in language specific format + Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|other_names(value:List[str]):None", exportContents);// captures collection info in language specific format } private static void ValidateExportTypeScript(string[] exportContents) { Assert.NotEmpty(exportContents); - Assert.Contains("exportNamespace.Graph~~>BaseRequestBuilder", exportContents); // captures class inheritance. TS does not do inheritance due to interfaces. + Assert.Contains("exportNamespace.Graph~~>BaseRequestBuilder", exportContents); // captures class inheritance. TS does not do inheritance due to interfaces. Assert.Contains("exportNamespace.models.microsoft.graph.User~~>AdditionalDataHolder; Parsable", exportContents);// captures implemented interfaces Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|id:string", exportContents);// captures property location,type and access inheritance. No getter/setter in TS // NOTE: No constructors in TS - Assert.Contains("exportNamespace.me.meRequestBuilder::|public|ToGetRequestInformation(requestConfiguration?:RequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access - Assert.Contains("exportNamespace.models.microsoft.graph::createUserFromDiscriminatorValue(parseNode:ParseNode):exportNamespace.models.microsoft.graph.user", exportContents);// captures code functions - Assert.Contains("exportNamespace.models.microsoft.graph::deserializeIntoUser(User:exportNamespace.models.microsoft.graph.User={}):Record void>", exportContents);// captures code functions and default params + Assert.Contains("exportNamespace.me.meRequestBuilder::|public|ToGetRequestInformation(requestConfiguration?:RequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("exportNamespace.models.microsoft.graph::createUserFromDiscriminatorValue(parseNode:ParseNode):User", exportContents);// captures code functions + Assert.Contains("exportNamespace.models.microsoft.graph::deserializeIntoUser(User:User={}):Record void>", exportContents);// captures code functions and default params Assert.Contains("exportNamespace.models.microsoft.graph.importance::0000-low", exportContents);// captures enum members Assert.Contains("exportNamespace.models.microsoft.graph.importanceObject", exportContents);// captures enum code constant object Assert.Contains("exportNamespace.graphNavigationMetadata", exportContents);// captures navigation metadata code constant object Assert.Contains("exportNamespace.me.get.getRequestBuilderRequestsMetadata", exportContents);// captures request builder metadata code constant object + Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|otherNames:string[]", exportContents);// captures collection info in language specific format } private static void ValidateExportPhp(string[] exportContents) @@ -198,10 +213,12 @@ private static void ValidateExportPhp(string[] exportContents) Assert.Contains("exportNamespace.models.microsoft.graph.user~~>AdditionalDataHolder; Parsable", exportContents);// captures implemented interfaces Assert.Contains("exportNamespace.models.microsoft.graph.user::|public|getId():string", exportContents);// captures property getter location,type and access inheritance Assert.Contains("exportNamespace.models.microsoft.graph.user::|public|setId(value?:string):void", exportContents);// captures property setter location,type and access inheritance - Assert.Contains("exportNamespace.me.get.getRequestBuilderGetRequestConfiguration::|public|constructor(headers?:[array]; options?:[array]):void", exportContents); // captures constructors, their parameters(name and types), return and access - Assert.Contains("exportNamespace.me.MeRequestBuilder::|public|constructor(pathParameters:[array]; requestAdapter:RequestAdapter):void", exportContents); // captures constructors, their parameters(name and types), return and access - Assert.Contains("exportNamespace.me.get.GetRequestBuilder::|public|ToGetRequestInformation(requestConfiguration?:exportNamespace.me.get.getRequestBuilderGetRequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access - Assert.Contains("exportNamespace.models.microsoft.graph.user::|static|public|CreateFromDiscriminatorValue(parseNode:ParseNode):exportNamespace.models.microsoft.graph.user", exportContents);// captures static methods too :) + Assert.Contains("exportNamespace.me.get.getRequestBuilderGetRequestConfiguration::|public|constructor(headers?:array; options?:array):void", exportContents); // captures constructors, their parameters(name and types), return and access + Assert.Contains("exportNamespace.me.MeRequestBuilder::|public|constructor(pathParameters:array; requestAdapter:RequestAdapter):void", exportContents); // captures constructors, their parameters(name and types), return and access + Assert.Contains("exportNamespace.me.get.GetRequestBuilder::|public|ToGetRequestInformation(requestConfiguration?:GetRequestBuilderGetRequestConfiguration):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access + Assert.Contains("exportNamespace.models.microsoft.graph.user::|static|public|CreateFromDiscriminatorValue(parseNode:ParseNode):User", exportContents);// captures static methods too :) Assert.Contains("exportNamespace.models.microsoft.graph.importance::0000-low", exportContents);// captures enum members + Assert.Contains("exportNamespace.models.microsoft.graph.user::|public|getOtherNames():array", exportContents);// captures collection info in language specific format + Assert.Contains("exportNamespace.models.microsoft.graph.user::|public|setOtherNames(value?:array):void", exportContents);// captures collection info in language specific format } } From 9afcb9b57affe0ca4e62d09ce7b4dc0f53c0b2fa Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Wed, 21 Aug 2024 16:08:21 +0300 Subject: [PATCH 22/28] Get the entry so that its cleaned up. --- src/Kiota.Builder/Export/PublicAPIExportService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kiota.Builder/Export/PublicAPIExportService.cs b/src/Kiota.Builder/Export/PublicAPIExportService.cs index b8b3b9caf8..097adb3f21 100644 --- a/src/Kiota.Builder/Export/PublicAPIExportService.cs +++ b/src/Kiota.Builder/Export/PublicAPIExportService.cs @@ -94,7 +94,7 @@ private IEnumerable GetEntry(CodeElement codeElement, bool includeDefini } private string GetParameters(IEnumerable parameters) { - return string.Join("; ", parameters.Select(x => $"{x.Name}{(x.Optional ? OptionalSymbol : string.Empty)}:{_conventionService.GetTypeString(x.Type, x)}{(string.IsNullOrEmpty(x.DefaultValue) ? string.Empty : $"={x.DefaultValue}")}")); + return string.Join("; ", parameters.Select(x => $"{x.Name}{(x.Optional ? OptionalSymbol : string.Empty)}:{GetEntryType(x.Type, x)}{(string.IsNullOrEmpty(x.DefaultValue) ? string.Empty : $"={x.DefaultValue}")}")); } private string GetEntryType(CodeTypeBase codeElementTypeBase, CodeElement targetElement) => _conventionService.GetTypeString(codeElementTypeBase, targetElement) From a778e9f1cac0fc8bca7dae891e44a64cb760676b Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Fri, 23 Aug 2024 13:44:30 +0300 Subject: [PATCH 23/28] Update src/Kiota.Builder/KiotaBuilder.cs Co-authored-by: Vincent Biret --- src/Kiota.Builder/KiotaBuilder.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index 6e455c2224..5e55cb1fce 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -273,8 +273,7 @@ public async Task GenerateClientAsync(CancellationToken cancellationToken) { // Generate public API export sw.Start(); - var diffService = new PublicApiExportService(config); - await diffService.SerializeDomAsync(generatedCode, cancellationToken).ConfigureAwait(false); + await new PublicApiExportService(config).SerializeDomAsync(generatedCode, cancellationToken).ConfigureAwait(false); StopLogAndReset(sw, $"step {++stepId} - generated public API export - took"); } From 1e47fad2cb337550cb1f515c9429d6b142fd348d Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Wed, 28 Aug 2024 10:55:03 +0300 Subject: [PATCH 24/28] fix: pr review feedback --- .../CodeDOM/IAccessibleElement.cs | 4 ++++ .../Export/PublicAPIExportService.cs | 18 +++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Kiota.Builder/CodeDOM/IAccessibleElement.cs b/src/Kiota.Builder/CodeDOM/IAccessibleElement.cs index f7e897ef35..ed437586a9 100644 --- a/src/Kiota.Builder/CodeDOM/IAccessibleElement.cs +++ b/src/Kiota.Builder/CodeDOM/IAccessibleElement.cs @@ -1,4 +1,8 @@ namespace Kiota.Builder.CodeDOM; + +/// +/// Defines a contract for elements that can have configurable access to them using +/// public interface IAccessibleElement { AccessModifier Access diff --git a/src/Kiota.Builder/Export/PublicAPIExportService.cs b/src/Kiota.Builder/Export/PublicAPIExportService.cs index 097adb3f21..4e625baedf 100644 --- a/src/Kiota.Builder/Export/PublicAPIExportService.cs +++ b/src/Kiota.Builder/Export/PublicAPIExportService.cs @@ -37,14 +37,18 @@ internal PublicApiExportService(GenerationConfiguration generationConfiguration) internal async Task SerializeDomAsync(CodeNamespace rootNamespace, CancellationToken cancellationToken = default) { var filePath = Path.Combine(_outputDirectoryPath, DomExportFileName); -#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task - await using var fileStream = File.Create(filePath); - await using var streamWriter = new StreamWriter(fileStream); -#pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - var entries = GetEntriesFromDom(rootNamespace).Order(StringComparer.OrdinalIgnoreCase).ToArray(); - foreach (var entry in entries) + var fileStream = File.Create(filePath); + await using (fileStream.ConfigureAwait(false)) { - await streamWriter.WriteLineAsync(entry.AsMemory(), cancellationToken).ConfigureAwait(false); + var streamWriter = new StreamWriter(fileStream); + await using (streamWriter.ConfigureAwait(false)) + { + var entries = GetEntriesFromDom(rootNamespace).Order(StringComparer.OrdinalIgnoreCase).ToArray(); + foreach (var entry in entries) + { + await streamWriter.WriteLineAsync(entry.AsMemory(), cancellationToken).ConfigureAwait(false); + } + } } } private IEnumerable GetEntriesFromDom(CodeElement currentElement) From a8a09ec21ce0fa0141b0a86451e6522eb9b93056 Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Wed, 28 Aug 2024 11:38:09 +0300 Subject: [PATCH 25/28] fix: pr review feedback --- .../Export/PublicAPIExportService.cs | 27 +++++++++---------- src/Kiota.Builder/KiotaBuilder.cs | 6 ++++- .../Export/PublicAPIExportServiceTests.cs | 12 ++++++--- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/Kiota.Builder/Export/PublicAPIExportService.cs b/src/Kiota.Builder/Export/PublicAPIExportService.cs index 4e625baedf..11c9bfb129 100644 --- a/src/Kiota.Builder/Export/PublicAPIExportService.cs +++ b/src/Kiota.Builder/Export/PublicAPIExportService.cs @@ -23,32 +23,26 @@ internal class PublicApiExportService internal PublicApiExportService(GenerationConfiguration generationConfiguration) { ArgumentNullException.ThrowIfNull(generationConfiguration); - _outputDirectoryPath = generationConfiguration.OutputPath; _conventionService = GetLanguageConventionServiceFromConfiguration(generationConfiguration); } - private readonly string _outputDirectoryPath; private readonly ILanguageConventionService _conventionService; - private const string DomExportFileName = "kiota-dom-export.txt"; + internal const string DomExportFileName = "kiota-dom-export.txt"; private const string InheritsSymbol = "-->"; private const string ImplementsSymbol = "~~>"; private const string OptionalSymbol = "?"; private const string ParentElementAndChildSeparator = "::"; - internal async Task SerializeDomAsync(CodeNamespace rootNamespace, CancellationToken cancellationToken = default) + internal async Task SerializeDomAsync(Stream fileStream, CodeNamespace rootNamespace, CancellationToken cancellationToken = default) { - var filePath = Path.Combine(_outputDirectoryPath, DomExportFileName); - var fileStream = File.Create(filePath); - await using (fileStream.ConfigureAwait(false)) + var streamWriter = new StreamWriter(fileStream, leaveOpen: true); + await using (streamWriter.ConfigureAwait(false)) { - var streamWriter = new StreamWriter(fileStream); - await using (streamWriter.ConfigureAwait(false)) + var entries = GetEntriesFromDom(rootNamespace).Order(StringComparer.OrdinalIgnoreCase).ToArray(); + foreach (var entry in entries) { - var entries = GetEntriesFromDom(rootNamespace).Order(StringComparer.OrdinalIgnoreCase).ToArray(); - foreach (var entry in entries) - { - await streamWriter.WriteLineAsync(entry.AsMemory(), cancellationToken).ConfigureAwait(false); - } + await streamWriter.WriteLineAsync(entry.AsMemory(), cancellationToken).ConfigureAwait(false); } + await streamWriter.FlushAsync(cancellationToken).ConfigureAwait(false); } } private IEnumerable GetEntriesFromDom(CodeElement currentElement) @@ -133,4 +127,9 @@ private static ILanguageConventionService GetLanguageConventionServiceFromConfig _ => throw new ArgumentOutOfRangeException(nameof(generationConfiguration), generationConfiguration.Language, null) }; } + + public void Dispose() + { + // TODO release managed resources here + } } diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index a15070443f..0c5fd1f234 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -273,7 +273,11 @@ public async Task GenerateClientAsync(CancellationToken cancellationToken) { // Generate public API export sw.Start(); - await new PublicApiExportService(config).SerializeDomAsync(generatedCode, cancellationToken).ConfigureAwait(false); + var fileStream = File.Create(Path.Combine(config.OutputPath, PublicApiExportService.DomExportFileName)); + await using (fileStream.ConfigureAwait(false)) + { + await new PublicApiExportService(config).SerializeDomAsync(fileStream, generatedCode, cancellationToken).ConfigureAwait(false); + } StopLogAndReset(sw, $"step {++stepId} - generated public API export - took"); } diff --git a/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs b/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs index e8c9718c55..afa22c9c0b 100644 --- a/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs @@ -114,17 +114,21 @@ public async Task GeneratesExportsAndFileHasExpectedAssertions(GenerationLanguag // serialize the dom model var exportService = new PublicApiExportService(generationConfig); - await exportService.SerializeDomAsync(codeModel); + using var outputStream = new MemoryStream(); + await exportService.SerializeDomAsync(outputStream, codeModel); // validate the export exists - var exportPath = Path.Join(Path.GetTempPath(), "kiota-dom-export.txt"); - Assert.True(File.Exists(exportPath)); + outputStream.Seek(0, SeekOrigin.Begin); + Assert.NotEqual(0, outputStream.Length); // output is not empty + + using var streamReader = new StreamReader(outputStream); + var contents = (await streamReader.ReadToEndAsync()).Split(Environment.NewLine); + if (!Validators.TryGetValue(generationLanguage, out var validator)) { Assert.Fail($"No Validator present for language {generationLanguage}"); } // run the language validator - var contents = File.ReadLines(exportPath).ToArray(); validator.Invoke(contents); } From 432ac3118be93726aeea8b1c799360ad781a1b0a Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Wed, 28 Aug 2024 11:40:32 +0300 Subject: [PATCH 26/28] format --- tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs b/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs index afa22c9c0b..362d92f94c 100644 --- a/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs +++ b/tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs @@ -123,7 +123,7 @@ public async Task GeneratesExportsAndFileHasExpectedAssertions(GenerationLanguag using var streamReader = new StreamReader(outputStream); var contents = (await streamReader.ReadToEndAsync()).Split(Environment.NewLine); - + if (!Validators.TryGetValue(generationLanguage, out var validator)) { Assert.Fail($"No Validator present for language {generationLanguage}"); From 59c1ea03af832e1b3cd686ef19ba4d95a042a31c Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Wed, 28 Aug 2024 11:45:48 +0300 Subject: [PATCH 27/28] cleanup --- src/Kiota.Builder/Export/PublicAPIExportService.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Kiota.Builder/Export/PublicAPIExportService.cs b/src/Kiota.Builder/Export/PublicAPIExportService.cs index 11c9bfb129..4ae6b4c99f 100644 --- a/src/Kiota.Builder/Export/PublicAPIExportService.cs +++ b/src/Kiota.Builder/Export/PublicAPIExportService.cs @@ -127,9 +127,4 @@ private static ILanguageConventionService GetLanguageConventionServiceFromConfig _ => throw new ArgumentOutOfRangeException(nameof(generationConfiguration), generationConfiguration.Language, null) }; } - - public void Dispose() - { - // TODO release managed resources here - } } From 9c9eb747c7713c04fa5b813c617ca7b6b58c760f Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Wed, 28 Aug 2024 14:01:50 +0300 Subject: [PATCH 28/28] Update src/Kiota.Builder/Export/PublicAPIExportService.cs Co-authored-by: Caleb Kiage <747955+calebkiage@users.noreply.github.com> --- src/Kiota.Builder/Export/PublicAPIExportService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Kiota.Builder/Export/PublicAPIExportService.cs b/src/Kiota.Builder/Export/PublicAPIExportService.cs index 4ae6b4c99f..9a1b249af0 100644 --- a/src/Kiota.Builder/Export/PublicAPIExportService.cs +++ b/src/Kiota.Builder/Export/PublicAPIExportService.cs @@ -32,9 +32,9 @@ internal PublicApiExportService(GenerationConfiguration generationConfiguration) private const string OptionalSymbol = "?"; private const string ParentElementAndChildSeparator = "::"; - internal async Task SerializeDomAsync(Stream fileStream, CodeNamespace rootNamespace, CancellationToken cancellationToken = default) + internal async Task SerializeDomAsync(Stream outputStream, CodeNamespace rootNamespace, CancellationToken cancellationToken = default) { - var streamWriter = new StreamWriter(fileStream, leaveOpen: true); + var streamWriter = new StreamWriter(outputStream, leaveOpen: true); await using (streamWriter.ConfigureAwait(false)) { var entries = GetEntriesFromDom(rootNamespace).Order(StringComparer.OrdinalIgnoreCase).ToArray();