From 30b957bf942145cec853a2d25dc51f8b94d7f300 Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Thu, 30 Nov 2023 19:02:31 +0300 Subject: [PATCH 01/11] WIP: Enum objects --- src/Kiota.Builder/CodeDOM/CodeEnum.cs | 23 +++++++++- .../Refiners/TypeScriptRefiner.cs | 43 ++++++++++++++++++- src/Kiota.Builder/Writers/LanguageWriter.cs | 3 ++ .../TypeScript/CodeEnumObjectWriter.cs | 15 +++++++ .../Writers/TypeScript/TypeScriptWriter.cs | 1 + 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs diff --git a/src/Kiota.Builder/CodeDOM/CodeEnum.cs b/src/Kiota.Builder/CodeDOM/CodeEnum.cs index 61c187fdc6..917ead5d4c 100644 --- a/src/Kiota.Builder/CodeDOM/CodeEnum.cs +++ b/src/Kiota.Builder/CodeDOM/CodeEnum.cs @@ -1,4 +1,5 @@ -using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -36,4 +37,24 @@ public DeprecationInformation? Deprecation { get; set; } + + public CodeEnumObject? CodeEnumObject + { + get; + set; + } + +} + + +public class CodeEnumObject : CodeElement, ICloneable +{ + public object Clone() + { + return new CodeUsing + { + Name = Name, + Parent = Parent, + }; + } } diff --git a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs index 183ebb2573..cd9d65f1bb 100644 --- a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs +++ b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -148,6 +148,7 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance generatedCode, factoryNameCallbackFromType ); + AddStaticMethodsUsingsForRequestExecutor( generatedCode, factoryNameCallbackFromType @@ -162,6 +163,8 @@ public override Task Refine(CodeNamespace generatedCode, CancellationToken cance generatedCode ); IntroducesInterfacesAndFunctions(generatedCode, factoryNameCallbackFromType); + AddEnumObject(generatedCode); + AddEnumObjectUsings(generatedCode); AliasUsingsWithSameSymbol(generatedCode); GenerateCodeFiles(generatedCode); cancellationToken.ThrowIfCancellationRequested(); @@ -173,7 +176,35 @@ private void GenerateCodeFiles(CodeElement currentElement) MergeElementsToFile(currentElement); CorrectCodeFileUsing(currentElement); } - + + protected static void AddEnumObject(CodeElement currentElement) + { + if (currentElement is CodeEnum codeEnum) + { + codeEnum.CodeEnumObject = new CodeEnumObject { Name = codeEnum.Name + "Object", Parent = codeEnum }; + } + CrawlTree(currentElement, AddEnumObject); + } + + protected static void AddEnumObjectUsings(CodeElement currentElement) + { + if (currentElement is CodeFunction codeFunction && + codeFunction.OriginalLocalMethod.IsOfKind(CodeMethodKind.Deserializer, CodeMethodKind.Serializer)) + { + foreach (var propertyEnum in codeFunction.OriginalMethodParentClass.Properties.Select(static x => x.Type).OfType().Select( static x => x.TypeDefinition).OfType()) + { + codeFunction.AddUsing(new CodeUsing + { + Name = propertyEnum.Name, + Declaration = new CodeType + { + TypeDefinition = propertyEnum.CodeEnumObject + } + }); + } + } + CrawlTree(currentElement, AddEnumObjectUsings); + } private void MergeElementsToFile(CodeElement currentElement) { // create all request builders as a code file with the functions @@ -185,9 +216,17 @@ private void MergeElementsToFile(CodeElement currentElement) { GenerateRequestBuilderCodeFile(currentClass, namespaceOfRequestBuilder); } + if (currentElement is CodeEnum codeEnum && codeEnum.Parent is CodeNamespace codeEnumNameSpace) + { + GenerateCodeEnumFile(codeEnum, codeEnumNameSpace); + } CrawlTree(currentElement, MergeElementsToFile); } + private static void GenerateCodeEnumFile(CodeEnum codeEnum, CodeNamespace codeEnumNameSpace) + { + codeEnumNameSpace.TryAddCodeFile(codeEnum.Name, codeEnum, codeEnum.CodeEnumObject!); + } private static void GenerateModelCodeFile(CodeInterface codeInterface, CodeNamespace codeNamespace) { var functions = new List(); diff --git a/src/Kiota.Builder/Writers/LanguageWriter.cs b/src/Kiota.Builder/Writers/LanguageWriter.cs index 98635fa6bc..5b7c9fb68c 100644 --- a/src/Kiota.Builder/Writers/LanguageWriter.cs +++ b/src/Kiota.Builder/Writers/LanguageWriter.cs @@ -158,6 +158,9 @@ public void Write(T code) where T : CodeElement case CodeConstant codeConstant: ((ICodeElementWriter)elementWriter).WriteCodeElement(codeConstant, this); break; + case CodeEnumObject codeEnumObject: + ((ICodeElementWriter)elementWriter).WriteCodeElement(codeEnumObject, this); + break; } else if (code is not CodeClass && code is not BlockDeclaration && diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs new file mode 100644 index 0000000000..66e7a735aa --- /dev/null +++ b/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs @@ -0,0 +1,15 @@ +using System; +using Kiota.Builder.CodeDOM; + +namespace Kiota.Builder.Writers.TypeScript; + +public class CodeEnumObjectWriter: BaseElementWriter +{ + public CodeEnumObjectWriter(TypeScriptConventionService conventionService) : base(conventionService) { } + public override void WriteCodeElement(CodeEnumObject codeElement, LanguageWriter writer) + { + ArgumentNullException.ThrowIfNull(codeElement); + ArgumentNullException.ThrowIfNull(writer); + + } +} diff --git a/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs b/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs index 611196461e..f770831183 100644 --- a/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs @@ -20,5 +20,6 @@ public TypeScriptWriter(string rootPath, string clientNamespaceName, bool usesBa AddOrReplaceCodeElementWriter(new CodeFileBlockEndWriter(conventionService)); AddOrReplaceCodeElementWriter(new CodeFileDeclarationWriter(conventionService, clientNamespaceName)); AddOrReplaceCodeElementWriter(new CodeConstantWriter(conventionService)); + AddOrReplaceCodeElementWriter(new CodeEnumObjectWriter(conventionService)); } } From b19e63019705f84bd2a42a3fbcbe78ee18f6549b Mon Sep 17 00:00:00 2001 From: koros Date: Fri, 1 Dec 2023 16:58:53 +0300 Subject: [PATCH 02/11] optimize enum object generation in Typescript/JS --- src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs | 2 +- .../Writers/TypeScript/CodeEnumObjectWriter.cs | 4 +++- src/Kiota.Builder/Writers/TypeScript/CodeEnumWriter.cs | 7 ++++--- src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs | 2 +- src/Kiota.Builder/Writers/TypeScript/CodePropertyWriter.cs | 2 +- .../Writers/TypeScript/CodeEnumWriterTests.cs | 6 +++--- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs index ad1e6d07e9..fa9a2b31a4 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs @@ -11,7 +11,7 @@ public override void WriteCodeElement(BlockEnd codeElement, LanguageWriter write { ArgumentNullException.ThrowIfNull(codeElement); ArgumentNullException.ThrowIfNull(writer); - if (codeElement.Parent is CodeNamespace) return; + if (codeElement.Parent is CodeNamespace or CodeEnum) return; writer.CloseBlock(); if (codeElement.Parent?.Parent is CodeNamespace) conventions.WriteAutoGeneratedEnd(writer); diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs index 66e7a735aa..c5c331aa2f 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs @@ -1,5 +1,6 @@ using System; using Kiota.Builder.CodeDOM; +using Kiota.Builder.Extensions; namespace Kiota.Builder.Writers.TypeScript; @@ -10,6 +11,7 @@ public override void WriteCodeElement(CodeEnumObject codeElement, LanguageWriter { ArgumentNullException.ThrowIfNull(codeElement); ArgumentNullException.ThrowIfNull(writer); - + var objectName = codeElement.Name.ToFirstCharacterUpperCase(); + writer.WriteLine($"export type {codeElement.Parent?.Name.ToFirstCharacterUpperCase()} = (typeof {objectName})[keyof typeof {objectName}];"); } } diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeEnumWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeEnumWriter.cs index 80eec59131..c88b5e6c0a 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeEnumWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeEnumWriter.cs @@ -14,14 +14,15 @@ public override void WriteCodeElement(CodeEnum codeElement, LanguageWriter write ArgumentNullException.ThrowIfNull(writer); if (!codeElement.Options.Any()) return; - conventions.WriteLongDescription(codeElement, writer); - writer.WriteLine($"export enum {codeElement.Name.ToFirstCharacterUpperCase()} {{"); + writer.WriteLine($"export const {codeElement.CodeEnumObject?.Name.ToFirstCharacterUpperCase()} = {{"); writer.IncreaseIndent(); codeElement.Options.ToList().ForEach(x => { conventions.WriteShortDescription(x.Documentation.Description, writer); - writer.WriteLine($"{x.Name.ToFirstCharacterUpperCase()} = \"{x.WireName}\","); + writer.WriteLine($"{x.Name.ToFirstCharacterUpperCase()}: \"{x.WireName}\","); }); + writer.DecreaseIndent(); + writer.WriteLine("} as const;"); } } diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs index e0dd329e71..53498ec591 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs @@ -223,7 +223,7 @@ private string GetDeserializationMethodName(CodeTypeBase propType, CodeFunction if (!string.IsNullOrEmpty(propertyType) && propType is CodeType currentType) { if (currentType.TypeDefinition is CodeEnum currentEnum) - return $"{(currentEnum.Flags || isCollection ? "getCollectionOfEnumValues" : "getEnumValue")}<{currentEnum.Name.ToFirstCharacterUpperCase()}>({propertyType.ToFirstCharacterUpperCase()})"; + return $"{(currentEnum.Flags || isCollection ? "getCollectionOfEnumValues" : "getEnumValue")}<{currentEnum.Name.ToFirstCharacterUpperCase()}>({currentEnum.CodeEnumObject?.Name.ToFirstCharacterUpperCase()})"; else if (conventions.StreamTypeName.Equals(propertyType, StringComparison.OrdinalIgnoreCase)) return "getByteArrayValue"; else if (isCollection) diff --git a/src/Kiota.Builder/Writers/TypeScript/CodePropertyWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodePropertyWriter.cs index f12b8815df..7896f17f53 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodePropertyWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodePropertyWriter.cs @@ -15,7 +15,7 @@ public override void WriteCodeElement(CodeProperty codeElement, LanguageWriter w var returnType = conventions.GetTypeString(codeElement.Type, codeElement); var isFlagEnum = codeElement.Type is CodeType { TypeDefinition: CodeEnum { Flags: true } } && !codeElement.Type.IsCollection;//collection of flagged enums are not supported/don't make sense - + conventions.WriteLongDescription(codeElement, writer); switch (codeElement.Parent) { diff --git a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs index 3eadd15bc6..1e50f2b646 100644 --- a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs @@ -39,9 +39,9 @@ public void WritesEnum() currentEnum.AddOption(new CodeEnumOption { Name = optionName }); writer.Write(currentEnum); var result = tw.ToString(); - Assert.Contains("export enum", result); + Assert.Contains("export const", result); Assert.Contains(optionName, result); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result, 0); } [Fact] public void DoesntWriteAnythingOnNoOption() @@ -65,6 +65,6 @@ public void WritesEnumOptionDescription() writer.Write(currentEnum); var result = tw.ToString(); Assert.Contains($"/** {option.Documentation.Description} */", result); - AssertExtensions.CurlyBracesAreClosed(result, 1); + AssertExtensions.CurlyBracesAreClosed(result, 0); } } From f7bd936c17d722cb44219f5aea3a06d6046955b9 Mon Sep 17 00:00:00 2001 From: koros Date: Tue, 5 Dec 2023 18:35:39 +0300 Subject: [PATCH 03/11] format code --- src/Kiota.Builder/Refiners/TypeScriptRefiner.cs | 7 +++---- .../Writers/TypeScript/CodeEnumObjectWriter.cs | 2 +- src/Kiota.Builder/Writers/TypeScript/CodePropertyWriter.cs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs index 4f23229b5f..9af5fa8f35 100644 --- a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs +++ b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs @@ -1008,13 +1008,12 @@ protected static void AddEnumObject(CodeElement currentElement) } CrawlTree(currentElement, AddEnumObject); } - + protected static void AddEnumObjectUsings(CodeElement currentElement) { - if (currentElement is CodeFunction codeFunction && - codeFunction.OriginalLocalMethod.IsOfKind(CodeMethodKind.Deserializer, CodeMethodKind.Serializer)) + if (currentElement is CodeFunction codeFunction && codeFunction.OriginalLocalMethod.IsOfKind(CodeMethodKind.Deserializer, CodeMethodKind.Serializer)) { - foreach (var propertyEnum in codeFunction.OriginalMethodParentClass.Properties.Select(static x => x.Type).OfType().Select( static x => x.TypeDefinition).OfType()) + foreach (var propertyEnum in codeFunction.OriginalMethodParentClass.Properties.Select(static x => x.Type).OfType().Select(static x => x.TypeDefinition).OfType()) { codeFunction.AddUsing(new CodeUsing { diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs index c5c331aa2f..02c65897a5 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs @@ -4,7 +4,7 @@ namespace Kiota.Builder.Writers.TypeScript; -public class CodeEnumObjectWriter: BaseElementWriter +public class CodeEnumObjectWriter : BaseElementWriter { public CodeEnumObjectWriter(TypeScriptConventionService conventionService) : base(conventionService) { } public override void WriteCodeElement(CodeEnumObject codeElement, LanguageWriter writer) diff --git a/src/Kiota.Builder/Writers/TypeScript/CodePropertyWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodePropertyWriter.cs index 7896f17f53..f12b8815df 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodePropertyWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodePropertyWriter.cs @@ -15,7 +15,7 @@ public override void WriteCodeElement(CodeProperty codeElement, LanguageWriter w var returnType = conventions.GetTypeString(codeElement.Type, codeElement); var isFlagEnum = codeElement.Type is CodeType { TypeDefinition: CodeEnum { Flags: true } } && !codeElement.Type.IsCollection;//collection of flagged enums are not supported/don't make sense - + conventions.WriteLongDescription(codeElement, writer); switch (codeElement.Parent) { From 2457bbe09ad5dc9477d18d13a9cd769fca062a73 Mon Sep 17 00:00:00 2001 From: koros Date: Wed, 6 Dec 2023 12:52:42 +0300 Subject: [PATCH 04/11] improve test coverage for CodeEnumWriter and CodeEnumObjectWriter --- .../TypeScript/CodeEnumObjectWriterTests.cs | 57 +++++++++++++++++++ .../Writers/TypeScript/CodeEnumWriterTests.cs | 20 ++++++- 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs diff --git a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs new file mode 100644 index 0000000000..b17c1f377a --- /dev/null +++ b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; +using System.Linq; +using Kiota.Builder; +using Kiota.Builder.CodeDOM; +using Kiota.Builder.Writers; +using Kiota.Builder.Writers.TypeScript; + +using Xunit; + +namespace Kiota.Builder.Tests.Writers.TypeScript; +public class CodeEnumObjectWriterTests +{ + private const string DefaultPath = "./"; + private const string DefaultName = "name"; + private readonly StringWriter tw; + private readonly LanguageWriter writer; + private readonly CodeEnumObjectWriter codeEnumObjectWriter; + public CodeEnumObjectWriterTests() + { + writer = LanguageWriter.GetLanguageWriter(GenerationLanguage.TypeScript, DefaultPath, DefaultName); + codeEnumObjectWriter = new CodeEnumObjectWriter(new()); + tw = new StringWriter(); + writer.SetTextWriter(tw); + } + public void Dispose() + { + tw?.Dispose(); + GC.SuppressFinalize(this); + } + + [Fact] + public void WriteCodeElement_ThrowsException_WhenCodeElementIsNull() + { + Assert.Throws(() => codeEnumObjectWriter.WriteCodeElement(null, writer)); + } + + [Fact] + public void WriteCodeElement_ThrowsException_WhenWriterIsNull() + { + var codeElement = new CodeEnumObject(); + Assert.Throws(() => codeEnumObjectWriter.WriteCodeElement(codeElement, null)); + } + + [Fact] + public void WriteCodeElement_WritesExpectedOutput_WhenCodeElementAndWriterAreNotNull() + { + var codeElement = new CodeEnumObject { Name = "TestEnum", Parent = new CodeClass { Name = "TestClass" } }; + + codeEnumObjectWriter.WriteCodeElement(codeElement, writer); + + var expectedOutput = "export type TestClass = (typeof TestEnum)[keyof typeof TestEnum];"; + + var result = tw.ToString(); + Assert.Contains(expectedOutput, result); + } +} diff --git a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs index b40c1a5e8a..a709adc6be 100644 --- a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs @@ -4,7 +4,7 @@ using Kiota.Builder.CodeDOM; using Kiota.Builder.Writers; - +using Kiota.Builder.Writers.TypeScript; using Xunit; namespace Kiota.Builder.Tests.Writers.TypeScript; @@ -16,9 +16,11 @@ public sealed class CodeEnumWriterTests : IDisposable private readonly LanguageWriter writer; private readonly CodeEnum currentEnum; private const string EnumName = "someEnum"; + private readonly CodeEnumWriter codeEnumWriter; public CodeEnumWriterTests() { writer = LanguageWriter.GetLanguageWriter(GenerationLanguage.TypeScript, DefaultPath, DefaultName); + codeEnumWriter = new CodeEnumWriter(new()); tw = new StringWriter(); writer.SetTextWriter(tw); var root = CodeNamespace.InitRootNamespace(); @@ -26,6 +28,7 @@ public CodeEnumWriterTests() { Name = EnumName, }).First(); + currentEnum.CodeEnumObject = new CodeEnumObject { Name = currentEnum.Name + "Object", Parent = currentEnum }; } public void Dispose() { @@ -33,13 +36,26 @@ public void Dispose() GC.SuppressFinalize(this); } [Fact] + public void WriteCodeElement_ThrowsException_WhenCodeElementIsNull() + { + Assert.Throws(() => codeEnumWriter.WriteCodeElement(null, writer)); + } + [Fact] + public void WriteCodeElement_ThrowsException_WhenWriterIsNull() + { + var codeElement = new CodeEnum(); + Assert.Throws(() => codeEnumWriter.WriteCodeElement(codeElement, null)); + } + [Fact] public void WritesEnum() { const string optionName = "option1"; currentEnum.AddOption(new CodeEnumOption { Name = optionName }); writer.Write(currentEnum); var result = tw.ToString(); - Assert.Contains("export const", result); + Assert.Contains("export const SomeEnumObject = {", result); + Assert.Contains("Option1: \"option1\"", result); + Assert.Contains("as const;", result); Assert.Contains(optionName, result); AssertExtensions.CurlyBracesAreClosed(result, 0); } From 3dcdae1547a8106c50cd608cf77d6cc81151103c Mon Sep 17 00:00:00 2001 From: koros Date: Wed, 6 Dec 2023 13:02:57 +0300 Subject: [PATCH 05/11] format code --- .../Writers/TypeScript/CodeEnumObjectWriterTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs index b17c1f377a..8f1192f95e 100644 --- a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs @@ -23,6 +23,8 @@ public CodeEnumObjectWriterTests() tw = new StringWriter(); writer.SetTextWriter(tw); } + + [Fact] public void Dispose() { tw?.Dispose(); From 40e445696e516ce831eb55384dcdfd012bdbdcb9 Mon Sep 17 00:00:00 2001 From: koros Date: Fri, 8 Dec 2023 09:18:26 +0300 Subject: [PATCH 06/11] refactor enums to use constant objects in typescript --- src/Kiota.Builder/CodeDOM/CodeConstant.cs | 15 ++- src/Kiota.Builder/CodeDOM/CodeEnum.cs | 20 +--- .../Refiners/TypeScriptRefiner.cs | 18 ++- src/Kiota.Builder/Writers/LanguageWriter.cs | 3 - .../Writers/TypeScript/CodeConstantWriter.cs | 39 +++++-- .../TypeScript/CodeEnumObjectWriter.cs | 17 --- .../Writers/TypeScript/CodeEnumWriter.cs | 13 +-- .../Writers/TypeScript/TypeScriptWriter.cs | 1 - .../TypeScript/CodeConstantWriterTests.cs | 104 ++++++++++++++++++ .../TypeScript/CodeEnumObjectWriterTests.cs | 59 ---------- .../Writers/TypeScript/CodeEnumWriterTests.cs | 38 ------- 11 files changed, 159 insertions(+), 168 deletions(-) delete mode 100644 src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs create mode 100644 tests/Kiota.Builder.Tests/Writers/TypeScript/CodeConstantWriterTests.cs delete mode 100644 tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs diff --git a/src/Kiota.Builder/CodeDOM/CodeConstant.cs b/src/Kiota.Builder/CodeDOM/CodeConstant.cs index 57a8f0e12e..e834b074e3 100644 --- a/src/Kiota.Builder/CodeDOM/CodeConstant.cs +++ b/src/Kiota.Builder/CodeDOM/CodeConstant.cs @@ -6,7 +6,7 @@ namespace Kiota.Builder.CodeDOM; public class CodeConstant : CodeTerminalWithKind { - public CodeInterface? OriginalInterface + public CodeElement? OriginalCodeElement { get; set; @@ -20,11 +20,22 @@ public CodeInterface? OriginalInterface { Name = $"{source.Name.ToFirstCharacterLowerCase()}Mapper", Kind = CodeConstantKind.QueryParametersMapper, - OriginalInterface = source, + OriginalCodeElement = source, + }; + } + public static CodeConstant? FromCodeEnum(CodeEnum source) + { + ArgumentNullException.ThrowIfNull(source); + return new CodeConstant + { + Name = $"{source.Name.ToFirstCharacterLowerCase()}Object", + Kind = CodeConstantKind.EnumObject, + OriginalCodeElement = source, }; } } public enum CodeConstantKind { QueryParametersMapper, + EnumObject, } diff --git a/src/Kiota.Builder/CodeDOM/CodeEnum.cs b/src/Kiota.Builder/CodeDOM/CodeEnum.cs index 917ead5d4c..3a46dd0a40 100644 --- a/src/Kiota.Builder/CodeDOM/CodeEnum.cs +++ b/src/Kiota.Builder/CodeDOM/CodeEnum.cs @@ -37,24 +37,8 @@ public DeprecationInformation? Deprecation { get; set; } - - public CodeEnumObject? CodeEnumObject - { - get; - set; - } - -} - - -public class CodeEnumObject : CodeElement, ICloneable -{ - public object Clone() + public CodeConstant? CodeEnumObject { - return new CodeUsing - { - Name = Name, - Parent = Parent, - }; + get; set; } } diff --git a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs index 3763e29723..0831d217a6 100644 --- a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs +++ b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs @@ -174,15 +174,6 @@ private static void GenerateEnumObjects(CodeElement currentElement) { AddEnumObject(currentElement); AddEnumObjectUsings(currentElement); - GenerateEnumCodeFiles(currentElement); - } - private static void GenerateEnumCodeFiles(CodeElement currentElement) - { - if (currentElement is CodeEnum codeEnum && codeEnum.CodeEnumObject is not null && codeEnum.Parent is CodeNamespace codeEnumNameSpace) - { - codeEnumNameSpace.TryAddCodeFile(codeEnum.Name, codeEnum, codeEnum.CodeEnumObject); - } - CrawlTree(currentElement, GenerateEnumCodeFiles); } private const string FileNameForModels = "index"; private static void GroupReusableModelsInSingleFile(CodeElement currentElement) @@ -198,8 +189,11 @@ private static void GroupReusableModelsInSingleFile(CodeElement currentElement) } if (codeNamespace.Enums.ToArray() is { Length: > 0 } enums) { + var enumObjects = enums.Select(static x => x.CodeEnumObject).OfType().ToArray(); + targetFile.AddElements(enumObjects); targetFile.AddElements(enums); codeNamespace.RemoveChildElement(enums); + codeNamespace.RemoveChildElement(enumObjects); } RemoveSelfReferencingUsingForFile(targetFile, codeNamespace); var childElements = targetFile.GetChildElements(true).ToArray(); @@ -1031,9 +1025,11 @@ private static void SetUsingsOfPropertyInSerializationFunctions(string propertyS protected static void AddEnumObject(CodeElement currentElement) { - if (currentElement is CodeEnum codeEnum) + if (currentElement is CodeEnum codeEnum && CodeConstant.FromCodeEnum(codeEnum) is CodeConstant constant) { - codeEnum.CodeEnumObject = new CodeEnumObject { Name = codeEnum.Name + "Object", Parent = codeEnum }; + codeEnum.CodeEnumObject = constant; + var nameSpace = codeEnum.GetImmediateParentOfType(); + nameSpace.AddConstant(constant); } CrawlTree(currentElement, AddEnumObject); } diff --git a/src/Kiota.Builder/Writers/LanguageWriter.cs b/src/Kiota.Builder/Writers/LanguageWriter.cs index c9be40221a..ea8ab2066a 100644 --- a/src/Kiota.Builder/Writers/LanguageWriter.cs +++ b/src/Kiota.Builder/Writers/LanguageWriter.cs @@ -158,9 +158,6 @@ public void Write(T code) where T : CodeElement case CodeConstant codeConstant: ((ICodeElementWriter)elementWriter).WriteCodeElement(codeConstant, this); break; - case CodeEnumObject codeEnumObject: - ((ICodeElementWriter)elementWriter).WriteCodeElement(codeEnumObject, this); - break; } else if (code is not CodeClass && code is not BlockDeclaration && diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs index 082ed5b53c..4f53821be4 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs @@ -11,16 +11,37 @@ public override void WriteCodeElement(CodeConstant codeElement, LanguageWriter w { ArgumentNullException.ThrowIfNull(codeElement); ArgumentNullException.ThrowIfNull(writer); - if (codeElement.OriginalInterface is null) throw new InvalidOperationException("Original interface cannot be null"); - writer.StartBlock($"const {codeElement.Name.ToFirstCharacterLowerCase()}: Record = {{"); - foreach (var property in codeElement.OriginalInterface - .Properties - .OfKind(CodePropertyKind.QueryParameter) - .Where(static x => !string.IsNullOrEmpty(x.SerializationName)) - .OrderBy(static x => x.SerializationName, StringComparer.OrdinalIgnoreCase)) + if (codeElement.OriginalCodeElement is null) throw new InvalidOperationException("Original CodeElement cannot be null"); + switch (codeElement.Kind) { - writer.WriteLine($"\"{property.Name.ToFirstCharacterLowerCase()}\": \"{property.SerializationName}\","); + case CodeConstantKind.QueryParametersMapper: + if (codeElement.OriginalCodeElement is not CodeInterface codeInterface) throw new InvalidOperationException("Original CodeElement cannot be null"); + writer.StartBlock($"const {codeElement.Name.ToFirstCharacterLowerCase()}: Record = {{"); + foreach (var property in codeInterface + .Properties + .OfKind(CodePropertyKind.QueryParameter) + .Where(static x => !string.IsNullOrEmpty(x.SerializationName)) + .OrderBy(static x => x.SerializationName, StringComparer.OrdinalIgnoreCase)) + { + writer.WriteLine($"\"{property.Name.ToFirstCharacterLowerCase()}\": \"{property.SerializationName}\","); + } + writer.CloseBlock("};"); + break; + case CodeConstantKind.EnumObject: + if (codeElement.OriginalCodeElement is not CodeEnum codeEnum) throw new InvalidOperationException("Original CodeElement cannot be null"); + if (!codeEnum.Options.Any()) + return; + conventions.WriteLongDescription(codeEnum, writer); + writer.WriteLine($"export const {codeElement.Name.ToFirstCharacterUpperCase()} = {{"); + writer.IncreaseIndent(); + codeEnum.Options.ToList().ForEach(x => + { + conventions.WriteShortDescription(x.Documentation.Description, writer); + writer.WriteLine($"{x.Name.ToFirstCharacterUpperCase()}: \"{x.WireName}\","); + }); + writer.DecreaseIndent(); + writer.WriteLine("} as const;"); + break; } - writer.CloseBlock("};"); } } diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs deleted file mode 100644 index 02c65897a5..0000000000 --- a/src/Kiota.Builder/Writers/TypeScript/CodeEnumObjectWriter.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using Kiota.Builder.CodeDOM; -using Kiota.Builder.Extensions; - -namespace Kiota.Builder.Writers.TypeScript; - -public class CodeEnumObjectWriter : BaseElementWriter -{ - public CodeEnumObjectWriter(TypeScriptConventionService conventionService) : base(conventionService) { } - public override void WriteCodeElement(CodeEnumObject codeElement, LanguageWriter writer) - { - ArgumentNullException.ThrowIfNull(codeElement); - ArgumentNullException.ThrowIfNull(writer); - var objectName = codeElement.Name.ToFirstCharacterUpperCase(); - writer.WriteLine($"export type {codeElement.Parent?.Name.ToFirstCharacterUpperCase()} = (typeof {objectName})[keyof typeof {objectName}];"); - } -} diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeEnumWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeEnumWriter.cs index c88b5e6c0a..6810b9be44 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeEnumWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeEnumWriter.cs @@ -12,17 +12,10 @@ public override void WriteCodeElement(CodeEnum codeElement, LanguageWriter write { ArgumentNullException.ThrowIfNull(codeElement); ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(codeElement.CodeEnumObject); if (!codeElement.Options.Any()) return; - conventions.WriteLongDescription(codeElement, writer); - writer.WriteLine($"export const {codeElement.CodeEnumObject?.Name.ToFirstCharacterUpperCase()} = {{"); - writer.IncreaseIndent(); - codeElement.Options.ToList().ForEach(x => - { - conventions.WriteShortDescription(x.Documentation.Description, writer); - writer.WriteLine($"{x.Name.ToFirstCharacterUpperCase()}: \"{x.WireName}\","); - }); - writer.DecreaseIndent(); - writer.WriteLine("} as const;"); + var enumObjectName = codeElement.CodeEnumObject.Name.ToFirstCharacterUpperCase(); + writer.WriteLine($"export type {codeElement.Name.ToFirstCharacterUpperCase()} = (typeof {enumObjectName})[keyof typeof {enumObjectName}];"); } } diff --git a/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs b/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs index b585a691ac..ea6306f2f0 100644 --- a/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/TypeScriptWriter.cs @@ -19,6 +19,5 @@ public TypeScriptWriter(string rootPath, string clientNamespaceName, bool usesBa AddOrReplaceCodeElementWriter(new CodeFileBlockEndWriter(conventionService)); AddOrReplaceCodeElementWriter(new CodeFileDeclarationWriter(conventionService, clientNamespaceName)); AddOrReplaceCodeElementWriter(new CodeConstantWriter(conventionService)); - AddOrReplaceCodeElementWriter(new CodeEnumObjectWriter(conventionService)); } } diff --git a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeConstantWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeConstantWriterTests.cs new file mode 100644 index 0000000000..c61bcc422f --- /dev/null +++ b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeConstantWriterTests.cs @@ -0,0 +1,104 @@ +using System; +using System.IO; +using System.Linq; +using Kiota.Builder.CodeDOM; +using Kiota.Builder.Writers; +using Kiota.Builder.Writers.Go; +using Kiota.Builder.Writers.TypeScript; + +using Xunit; + +namespace Kiota.Builder.Tests.Writers.TypeScript; +public sealed class CodeConstantWriterTests : IDisposable +{ + private const string DefaultPath = "./"; + private const string DefaultName = "name"; + private readonly StringWriter tw; + private readonly LanguageWriter writer; + private readonly CodeEnum currentEnum; + private const string EnumName = "someEnum"; + private readonly CodeConstantWriter codeConstantWriter; + + public CodeConstantWriterTests() + { + writer = LanguageWriter.GetLanguageWriter(GenerationLanguage.TypeScript, DefaultPath, DefaultName); + codeConstantWriter = new CodeConstantWriter(new()); + tw = new StringWriter(); + writer.SetTextWriter(tw); + var root = CodeNamespace.InitRootNamespace(); + currentEnum = root.AddEnum(new CodeEnum + { + Name = EnumName, + }).First(); + if (CodeConstant.FromCodeEnum(currentEnum) is CodeConstant constant) + { + currentEnum.CodeEnumObject = constant; + root.AddConstant(constant); + } + } + public void Dispose() + { + tw?.Dispose(); + GC.SuppressFinalize(this); + } + + [Fact] + public void WriteCodeElement_ThrowsException_WhenCodeElementIsNull() + { + Assert.Throws(() => codeConstantWriter.WriteCodeElement(null, writer)); + } + + [Fact] + public void WriteCodeElement_ThrowsException_WhenWriterIsNull() + { + var codeElement = new CodeConstant(); + Assert.Throws(() => codeConstantWriter.WriteCodeElement(codeElement, null)); + } + + [Fact] + public void WriteCodeElement_ThrowsException_WhenOriginalCodeElementIsNull() + { + var codeElement = new CodeConstant(); + Assert.Throws(() => codeConstantWriter.WriteCodeElement(codeElement, writer)); + } + + [Fact] + public void WritesEnumOptionDescription() + { + var option = new CodeEnumOption + { + Documentation = new() + { + Description = "Some option description", + }, + Name = "option1", + }; + currentEnum.AddOption(option); + codeConstantWriter.WriteCodeElement(currentEnum.CodeEnumObject, writer); + var result = tw.ToString(); + Assert.Contains($"/** {option.Documentation.Description} */", result); + AssertExtensions.CurlyBracesAreClosed(result, 0); + } + + [Fact] + public void WritesEnum() + { + const string optionName = "option1"; + currentEnum.AddOption(new CodeEnumOption { Name = optionName }); + codeConstantWriter.WriteCodeElement(currentEnum.CodeEnumObject, writer); + var result = tw.ToString(); + Assert.Contains("export const SomeEnumObject = {", result); + Assert.Contains("Option1: \"option1\"", result); + Assert.Contains("as const;", result); + Assert.Contains(optionName, result); + AssertExtensions.CurlyBracesAreClosed(result, 0); + } + + [Fact] + public void DoesntWriteAnythingOnNoOption() + { + codeConstantWriter.WriteCodeElement(currentEnum.CodeEnumObject, writer); + var result = tw.ToString(); + Assert.Empty(result); + } +} diff --git a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs deleted file mode 100644 index 8f1192f95e..0000000000 --- a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumObjectWriterTests.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using Kiota.Builder; -using Kiota.Builder.CodeDOM; -using Kiota.Builder.Writers; -using Kiota.Builder.Writers.TypeScript; - -using Xunit; - -namespace Kiota.Builder.Tests.Writers.TypeScript; -public class CodeEnumObjectWriterTests -{ - private const string DefaultPath = "./"; - private const string DefaultName = "name"; - private readonly StringWriter tw; - private readonly LanguageWriter writer; - private readonly CodeEnumObjectWriter codeEnumObjectWriter; - public CodeEnumObjectWriterTests() - { - writer = LanguageWriter.GetLanguageWriter(GenerationLanguage.TypeScript, DefaultPath, DefaultName); - codeEnumObjectWriter = new CodeEnumObjectWriter(new()); - tw = new StringWriter(); - writer.SetTextWriter(tw); - } - - [Fact] - public void Dispose() - { - tw?.Dispose(); - GC.SuppressFinalize(this); - } - - [Fact] - public void WriteCodeElement_ThrowsException_WhenCodeElementIsNull() - { - Assert.Throws(() => codeEnumObjectWriter.WriteCodeElement(null, writer)); - } - - [Fact] - public void WriteCodeElement_ThrowsException_WhenWriterIsNull() - { - var codeElement = new CodeEnumObject(); - Assert.Throws(() => codeEnumObjectWriter.WriteCodeElement(codeElement, null)); - } - - [Fact] - public void WriteCodeElement_WritesExpectedOutput_WhenCodeElementAndWriterAreNotNull() - { - var codeElement = new CodeEnumObject { Name = "TestEnum", Parent = new CodeClass { Name = "TestClass" } }; - - codeEnumObjectWriter.WriteCodeElement(codeElement, writer); - - var expectedOutput = "export type TestClass = (typeof TestEnum)[keyof typeof TestEnum];"; - - var result = tw.ToString(); - Assert.Contains(expectedOutput, result); - } -} diff --git a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs index a709adc6be..d17710c451 100644 --- a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs @@ -28,7 +28,6 @@ public CodeEnumWriterTests() { Name = EnumName, }).First(); - currentEnum.CodeEnumObject = new CodeEnumObject { Name = currentEnum.Name + "Object", Parent = currentEnum }; } public void Dispose() { @@ -46,41 +45,4 @@ public void WriteCodeElement_ThrowsException_WhenWriterIsNull() var codeElement = new CodeEnum(); Assert.Throws(() => codeEnumWriter.WriteCodeElement(codeElement, null)); } - [Fact] - public void WritesEnum() - { - const string optionName = "option1"; - currentEnum.AddOption(new CodeEnumOption { Name = optionName }); - writer.Write(currentEnum); - var result = tw.ToString(); - Assert.Contains("export const SomeEnumObject = {", result); - Assert.Contains("Option1: \"option1\"", result); - Assert.Contains("as const;", result); - Assert.Contains(optionName, result); - AssertExtensions.CurlyBracesAreClosed(result, 0); - } - [Fact] - public void DoesntWriteAnythingOnNoOption() - { - writer.Write(currentEnum); - var result = tw.ToString(); - Assert.Empty(result); - } - [Fact] - public void WritesEnumOptionDescription() - { - var option = new CodeEnumOption - { - Documentation = new() - { - Description = "Some option description", - }, - Name = "option1", - }; - currentEnum.AddOption(option); - writer.Write(currentEnum); - var result = tw.ToString(); - Assert.Contains($"/** {option.Documentation.Description} */", result); - AssertExtensions.CurlyBracesAreClosed(result, 0); - } } From b7297e3cee77bf4f3645145ae57f5e8e2733a5ba Mon Sep 17 00:00:00 2001 From: koros Date: Fri, 8 Dec 2023 14:38:03 +0300 Subject: [PATCH 07/11] include enum constant objects in the inline enums --- src/Kiota.Builder/Refiners/TypeScriptRefiner.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs index 0831d217a6..0646e6f161 100644 --- a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs +++ b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs @@ -192,8 +192,8 @@ private static void GroupReusableModelsInSingleFile(CodeElement currentElement) var enumObjects = enums.Select(static x => x.CodeEnumObject).OfType().ToArray(); targetFile.AddElements(enumObjects); targetFile.AddElements(enums); - codeNamespace.RemoveChildElement(enums); codeNamespace.RemoveChildElement(enumObjects); + codeNamespace.RemoveChildElement(enums); } RemoveSelfReferencingUsingForFile(targetFile, codeNamespace); var childElements = targetFile.GetChildElements(true).ToArray(); @@ -252,6 +252,11 @@ private static void GenerateRequestBuilderCodeFile(CodeClass codeClass, CodeName .Enums .ToArray(); + var enumObjects = inlineEnums + .Select(static x => x.CodeEnumObject) + .OfType() + .ToArray(); + var queryParameterInterfaces = executorMethods .SelectMany(static x => x.Parameters) .Where(static x => x.IsOfKind(CodeParameterKind.RequestConfiguration)) @@ -281,6 +286,7 @@ private static void GenerateRequestBuilderCodeFile(CodeClass codeClass, CodeName .Union(queryParametersMapperConstants) .Union(inlineRequestAndResponseBodyFiles.SelectMany(static x => x.GetChildElements(true))) .Union(inlineEnums) + .Union(enumObjects) .Distinct() .ToArray(); From 5d7059f24a6e2a1f912fd9bc1398cd47061f32be Mon Sep 17 00:00:00 2001 From: koros Date: Fri, 8 Dec 2023 15:10:25 +0300 Subject: [PATCH 08/11] improve code coverage --- CHANGELOG.md | 1 + .../Writers/TypeScript/CodeEnumWriterTests.cs | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df261baf48..bae412d5d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Fixed a bug in the vscode extension where the "Paste API Manifest" button would not be able to parse the manifest. +- Enhance the way Enums are expressed in Typescript. [#2105](https://github.com/microsoft/kiota/issues/2105) ## [1.9.0] - 2023-12-07 diff --git a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs index d17710c451..dae79cf062 100644 --- a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeEnumWriterTests.cs @@ -3,6 +3,7 @@ using System.Linq; using Kiota.Builder.CodeDOM; +using Kiota.Builder.Extensions; using Kiota.Builder.Writers; using Kiota.Builder.Writers.TypeScript; using Xunit; @@ -28,6 +29,11 @@ public CodeEnumWriterTests() { Name = EnumName, }).First(); + if (CodeConstant.FromCodeEnum(currentEnum) is CodeConstant constant) + { + currentEnum.CodeEnumObject = constant; + root.AddConstant(constant); + } } public void Dispose() { @@ -45,4 +51,23 @@ public void WriteCodeElement_ThrowsException_WhenWriterIsNull() var codeElement = new CodeEnum(); Assert.Throws(() => codeEnumWriter.WriteCodeElement(codeElement, null)); } + [Fact] + public void WritesEnum() + { + const string optionName = "option1"; + currentEnum.AddOption(new CodeEnumOption { Name = optionName }); + codeEnumWriter.WriteCodeElement(currentEnum, writer); + var result = tw.ToString(); + Assert.Contains("export type SomeEnum = (", result); + Assert.Contains("typeof", result); + Assert.Contains(currentEnum.Name.ToFirstCharacterUpperCase(), result); + AssertExtensions.CurlyBracesAreClosed(result, 0); + } + [Fact] + public void DoesntWriteEnumWithEmptyOptions() + { + codeEnumWriter.WriteCodeElement(currentEnum, writer); + var result = tw.ToString(); + Assert.Equal("", result); + } } From 95d86967498b103e0ab4a12534b829f683557bc4 Mon Sep 17 00:00:00 2001 From: koros Date: Fri, 8 Dec 2023 19:37:03 +0300 Subject: [PATCH 09/11] address pr coments --- .../Writers/TypeScript/CodeBlockEndWriter.cs | 1 + .../Writers/TypeScript/CodeConstantWriter.cs | 57 +++++++++++-------- .../Writers/TypeScript/CodeFunctionWriter.cs | 4 +- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs index fa9a2b31a4..a310aeb364 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs @@ -12,6 +12,7 @@ public override void WriteCodeElement(BlockEnd codeElement, LanguageWriter write ArgumentNullException.ThrowIfNull(codeElement); ArgumentNullException.ThrowIfNull(writer); if (codeElement.Parent is CodeNamespace or CodeEnum) return; + if (codeElement.Parent is CodeNamespace or CodeEnum) return; writer.CloseBlock(); if (codeElement.Parent?.Parent is CodeNamespace) conventions.WriteAutoGeneratedEnd(writer); diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs index 4f53821be4..d224a023b0 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs @@ -2,6 +2,7 @@ using System.Linq; using Kiota.Builder.CodeDOM; using Kiota.Builder.Extensions; +using Kiota.Builder.Writers.Go; namespace Kiota.Builder.Writers.TypeScript; public class CodeConstantWriter : BaseElementWriter @@ -15,33 +16,41 @@ public override void WriteCodeElement(CodeConstant codeElement, LanguageWriter w switch (codeElement.Kind) { case CodeConstantKind.QueryParametersMapper: - if (codeElement.OriginalCodeElement is not CodeInterface codeInterface) throw new InvalidOperationException("Original CodeElement cannot be null"); - writer.StartBlock($"const {codeElement.Name.ToFirstCharacterLowerCase()}: Record = {{"); - foreach (var property in codeInterface - .Properties - .OfKind(CodePropertyKind.QueryParameter) - .Where(static x => !string.IsNullOrEmpty(x.SerializationName)) - .OrderBy(static x => x.SerializationName, StringComparer.OrdinalIgnoreCase)) - { - writer.WriteLine($"\"{property.Name.ToFirstCharacterLowerCase()}\": \"{property.SerializationName}\","); - } - writer.CloseBlock("};"); + WriteQueryParametersMapperConstant(codeElement, writer); break; case CodeConstantKind.EnumObject: - if (codeElement.OriginalCodeElement is not CodeEnum codeEnum) throw new InvalidOperationException("Original CodeElement cannot be null"); - if (!codeEnum.Options.Any()) - return; - conventions.WriteLongDescription(codeEnum, writer); - writer.WriteLine($"export const {codeElement.Name.ToFirstCharacterUpperCase()} = {{"); - writer.IncreaseIndent(); - codeEnum.Options.ToList().ForEach(x => - { - conventions.WriteShortDescription(x.Documentation.Description, writer); - writer.WriteLine($"{x.Name.ToFirstCharacterUpperCase()}: \"{x.WireName}\","); - }); - writer.DecreaseIndent(); - writer.WriteLine("} as const;"); + WriteEnumObjectConstant(codeElement, writer); break; } } + private static void WriteQueryParametersMapperConstant(CodeConstant codeElement, LanguageWriter writer) + { + if (codeElement.OriginalCodeElement is not CodeInterface codeInterface) throw new InvalidOperationException("Original CodeElement cannot be null"); + writer.StartBlock($"const {codeElement.Name.ToFirstCharacterLowerCase()}: Record = {{"); + foreach (var property in codeInterface + .Properties + .OfKind(CodePropertyKind.QueryParameter) + .Where(static x => !string.IsNullOrEmpty(x.SerializationName)) + .OrderBy(static x => x.SerializationName, StringComparer.OrdinalIgnoreCase)) + { + writer.WriteLine($"\"{property.Name.ToFirstCharacterLowerCase()}\": \"{property.SerializationName}\","); + } + writer.CloseBlock("};"); + } + private void WriteEnumObjectConstant(CodeConstant codeElement, LanguageWriter writer) + { + if (codeElement.OriginalCodeElement is not CodeEnum codeEnum) throw new InvalidOperationException("Original CodeElement cannot be null"); + if (!codeEnum.Options.Any()) + return; + conventions.WriteLongDescription(codeEnum, writer); + writer.WriteLine($"export const {codeElement.Name.ToFirstCharacterUpperCase()} = {{"); + writer.IncreaseIndent(); + codeEnum.Options.ToList().ForEach(x => + { + conventions.WriteShortDescription(x.Documentation.Description, writer); + writer.WriteLine($"{x.Name.ToFirstCharacterUpperCase()}: \"{x.WireName}\","); + }); + writer.DecreaseIndent(); + writer.WriteLine("} as const;"); + } } diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs index 108915c110..333df72eb2 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs @@ -223,8 +223,8 @@ private string GetDeserializationMethodName(CodeTypeBase propType, CodeFunction var propertyType = conventions.GetTypeString(propType, codeFunction, false); if (!string.IsNullOrEmpty(propertyType) && propType is CodeType currentType) { - if (currentType.TypeDefinition is CodeEnum currentEnum) - return $"{(currentEnum.Flags || isCollection ? "getCollectionOfEnumValues" : "getEnumValue")}<{currentEnum.Name.ToFirstCharacterUpperCase()}>({currentEnum.CodeEnumObject?.Name.ToFirstCharacterUpperCase()})"; + if (currentType.TypeDefinition is CodeEnum currentEnum && currentEnum.CodeEnumObject is not null) + return $"{(currentEnum.Flags || isCollection ? "getCollectionOfEnumValues" : "getEnumValue")}<{currentEnum.Name.ToFirstCharacterUpperCase()}>({currentEnum.CodeEnumObject.Name.ToFirstCharacterUpperCase()})"; else if (conventions.StreamTypeName.Equals(propertyType, StringComparison.OrdinalIgnoreCase)) return "getByteArrayValue"; else if (isCollection) From 4485c847faa95d89820e2899d18dddaaa576c21f Mon Sep 17 00:00:00 2001 From: koros Date: Fri, 8 Dec 2023 19:38:10 +0300 Subject: [PATCH 10/11] delete extra line --- src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs index a310aeb364..fa9a2b31a4 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeBlockEndWriter.cs @@ -12,7 +12,6 @@ public override void WriteCodeElement(BlockEnd codeElement, LanguageWriter write ArgumentNullException.ThrowIfNull(codeElement); ArgumentNullException.ThrowIfNull(writer); if (codeElement.Parent is CodeNamespace or CodeEnum) return; - if (codeElement.Parent is CodeNamespace or CodeEnum) return; writer.CloseBlock(); if (codeElement.Parent?.Parent is CodeNamespace) conventions.WriteAutoGeneratedEnd(writer); From 38894b6ab790ff84ebd9b9e97370d1edb617b7ad Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 8 Dec 2023 12:01:11 -0500 Subject: [PATCH 11/11] Apply suggestions from code review --- src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs index d224a023b0..70e9b5fee2 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs @@ -43,14 +43,12 @@ private void WriteEnumObjectConstant(CodeConstant codeElement, LanguageWriter wr if (!codeEnum.Options.Any()) return; conventions.WriteLongDescription(codeEnum, writer); - writer.WriteLine($"export const {codeElement.Name.ToFirstCharacterUpperCase()} = {{"); - writer.IncreaseIndent(); + writer.StartBlock($"export const {codeElement.Name.ToFirstCharacterUpperCase()} = {{"); codeEnum.Options.ToList().ForEach(x => { conventions.WriteShortDescription(x.Documentation.Description, writer); writer.WriteLine($"{x.Name.ToFirstCharacterUpperCase()}: \"{x.WireName}\","); }); - writer.DecreaseIndent(); - writer.WriteLine("} as const;"); + writer.CloseBlock("} as const;"); } }