From 84d2c071c1a05db932f38006ac4425ca3eaddc80 Mon Sep 17 00:00:00 2001 From: Ronald K <43806892+rkodev@users.noreply.github.com> Date: Fri, 22 Nov 2024 18:03:28 +0300 Subject: [PATCH] Fix function writer (#5809) * Fix function writer * enable tests * fix documentation * ling re-qrite * Fix default value for escaped enum options * Fix enum default value * fix * fix signature for composed types of collections * fix missing data types * fix test * Fix * Adds readme * Format code * Update src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs --------- Co-authored-by: Vincent Biret --- CHANGELOG.md | 1 + it/config.json | 4 --- .../Refiners/TypeScriptRefiner.cs | 28 +++++++++++-------- .../Writers/TypeScript/CodeConstantWriter.cs | 4 +-- .../Writers/TypeScript/CodeFunctionWriter.cs | 16 ++++++++--- .../TypeScript/TypeScriptConventionService.cs | 27 ++++++++++++++++-- .../TypeScript/CodeFunctionWriterTests.cs | 17 +++++++++++ 7 files changed, 73 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7649b49a9c..8524fead23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed Python error when a class inherits from a base class and implements an interface. [5637](https://github.com/microsoft/kiota/issues/5637) - Fix anyOf/oneOf generation in TypeScript. [5353](https://github.com/microsoft/kiota/issues/5353) - Fixed invalid code in Php caused by "*/*/" in property description. [5635](https://github.com/microsoft/kiota/issues/5635) +- Fixed TypeScript generation error when generating usings from shaken serializers. [#5634](https://github.com/microsoft/kiota/issues/5634) ## [1.20.0] - 2024-11-07 diff --git a/it/config.json b/it/config.json index dab857bc52..777f0a36fd 100644 --- a/it/config.json +++ b/it/config.json @@ -29,10 +29,6 @@ "apisguru::github.com:api.github.com": { "MockServerITFolder": "gh", "Suppressions": [ - { - "Language": "typescript", - "Rationale": "https://github.com/microsoft/kiota/issues/5634" - }, { "Language": "ruby", "Rationale": "https://github.com/microsoft/kiota/issues/1816" diff --git a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs index 4b634c7d08..ca06132982 100644 --- a/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs +++ b/src/Kiota.Builder/Refiners/TypeScriptRefiner.cs @@ -1493,22 +1493,26 @@ private static void AddDeserializerUsingToDiscriminatorFactory(CodeElement codeE foreach (var mappedType in parsableFactoryFunction.OriginalMethodParentClass.DiscriminatorInformation.DiscriminatorMappings) { - if (mappedType.Value is CodeType type && type.TypeDefinition is CodeClass mappedClass) + if (mappedType.Value is not + { TypeDefinition: CodeClass { Parent: CodeNamespace codeNamespace } mappedClass } + || codeNamespace.FindChildByName( + $"{ModelDeserializerPrefix}{mappedClass.Name.ToFirstCharacterUpperCase()}") is not + { } deserializer) { - var deserializer = GetSerializationFunctionsForNamespace(mappedClass).Item2; + continue; + } - if (deserializer.Parent is not null) + if (deserializer.Parent is not null) + { + parsableFactoryFunction.AddUsing(new CodeUsing { - parsableFactoryFunction.AddUsing(new CodeUsing + Name = deserializer.Parent.Name, + Declaration = new CodeType { - Name = deserializer.Parent.Name, - Declaration = new CodeType - { - Name = deserializer.Name, - TypeDefinition = deserializer - }, - }); - } + Name = deserializer.Name, + TypeDefinition = deserializer + }, + }); } } } diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs index b75590008a..f9f41d41a8 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs @@ -102,7 +102,7 @@ private void WriteRequestsMetadataConstant(CodeConstant codeElement, LanguageWri var isStream = conventions.StreamTypeName.Equals(returnType, StringComparison.OrdinalIgnoreCase); var isEnum = executorMethod.ReturnType is CodeType codeType && codeType.TypeDefinition is CodeEnum; var returnTypeWithoutCollectionSymbol = GetReturnTypeWithoutCollectionSymbol(executorMethod, returnType); - var isPrimitive = IsPrimitiveType(returnTypeWithoutCollectionSymbol); + var isPrimitive = IsPrimitiveType(returnTypeWithoutCollectionSymbol) || IsKiotaPrimitive(returnTypeWithoutCollectionSymbol); var isPrimitiveAlias = GetPrimitiveAlias(returnTypeWithoutCollectionSymbol) is not null; writer.StartBlock($"{executorMethod.Name.ToFirstCharacterLowerCase()}: {{"); var urlTemplateValue = executorMethod.HasUrlTemplateOverride ? $"\"{executorMethod.UrlTemplateOverride}\"" : uriTemplateConstant.Name.ToFirstCharacterUpperCase(); @@ -169,7 +169,7 @@ private string GetTypeFactory(bool isVoid, bool isStream, CodeMethod codeElement { if (isVoid) return string.Empty; var typeName = conventions.TranslateType(codeElement.ReturnType); - if (isStream || IsPrimitiveType(typeName)) return $" \"{typeName}\""; + if (isStream || IsPrimitiveType(typeName) || IsKiotaPrimitive(typeName)) return $" \"{typeName}\""; if (GetPrimitiveAlias(typeName) is { } alias && !string.IsNullOrEmpty(alias)) return $" \"{alias}\""; return $" {GetFactoryMethodName(codeElement.ReturnType, codeElement, writer)}"; diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs index 1710944648..c21197ae14 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs @@ -117,7 +117,7 @@ private void WriteComposedTypeSerializer(CodeFunction codeElement, LanguageWrite private void WriteSerializationFunctionForCodeIntersectionType(CodeComposedTypeBase composedType, CodeParameter composedParam, CodeFunction method, LanguageWriter writer) { - foreach (var mappedType in composedType.Types.Where(x => !IsPrimitiveType(x, composedType))) + foreach (var mappedType in composedType.Types.Where(x => !IsPrimitiveType(x, composedType) && x.TypeDefinition != null)) { var functionName = GetSerializerFunctionName(method, mappedType); var variableName = composedParam.Name.ToFirstCharacterLowerCase(); @@ -611,9 +611,17 @@ private static string GetDefaultValueSuffix(CodeProperty otherProp) private static string GetDefaultValueLiteralForProperty(CodeProperty codeProperty) { if (string.IsNullOrEmpty(codeProperty.DefaultValue)) return string.Empty; - if (codeProperty.Type is CodeType propertyType && propertyType.TypeDefinition is CodeEnum enumDefinition && enumDefinition.CodeEnumObject is not null) - return $"{enumDefinition.CodeEnumObject.Name.ToFirstCharacterUpperCase()}.{codeProperty.DefaultValue.Trim('"').CleanupSymbolName().ToFirstCharacterUpperCase()}"; - return codeProperty.DefaultValue; + if (codeProperty.Type is CodeType propertyType && propertyType.TypeDefinition is CodeEnum enumDefinition && + enumDefinition.CodeEnumObject is not null) + { + var codeEnumOption = enumDefinition.Options.First(x => + x.SymbolName.Equals(codeProperty.DefaultValue.Trim('"').CleanupSymbolName(), + StringComparison.OrdinalIgnoreCase)); + return $"{enumDefinition.CodeEnumObject.Name.ToFirstCharacterUpperCase()}.{codeEnumOption.Name.Trim('"').CleanupSymbolName().ToFirstCharacterUpperCase()}"; + } + + // only string primitive should keep quotes + return codeProperty.Type.Name.Equals("string", StringComparison.Ordinal) ? codeProperty.DefaultValue : codeProperty.DefaultValue.Trim('"'); } private void WriteDefensiveStatements(CodeMethod codeElement, LanguageWriter writer) { diff --git a/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs b/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs index 90a6ac83e3..a963796ba6 100644 --- a/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs +++ b/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs @@ -99,7 +99,7 @@ public override string GetParameterSignature(CodeParameter parameter, CodeElemen // add a 'Parsable' type to the parameter if it is composed of non-Parsable types var parsableTypes = ( - composedType != null && !composedType.IsComposedOfObjects(IsPrimitiveType), + composedType != null && (!composedType.IsComposedOfObjects(IsPrimitiveType) || composedType.Types.Any(x => x.IsCollection)), parameter.Parent is CodeMethod method && (method.IsOfKind(CodeMethodKind.Deserializer, CodeMethodKind.Serializer)) ) switch { @@ -237,6 +237,18 @@ TYPE_LOWERCASE_BOOLEAN or }; } + // Types that are imported from kiota-abstractions and considered as primitive types + public static bool IsKiotaPrimitive(string typeName) + { + return typeName switch + { + TYPE_DATE_ONLY or + TYPE_TIME_ONLY or + TYPE_DURATION => true, + _ => false, + }; + } + public static string? GetPrimitiveAlias(string typeName) { return typeName switch @@ -252,7 +264,18 @@ TYPE_LOWERCASE_BOOLEAN or private static bool IsPrimitiveTypeOrPrimitiveCollection(CodeType codeType, CodeComposedTypeBase codeComposedTypeBase) => IsPrimitiveType(codeType, codeComposedTypeBase, false); - internal static string RemoveInvalidDescriptionCharacters(string originalDescription) => originalDescription?.Replace("\\", "/", StringComparison.OrdinalIgnoreCase) ?? string.Empty; + private static Dictionary InvalidCharactersReplacements = new(StringComparer.OrdinalIgnoreCase) { + { "\\", "/"}, + { "/*", "//*"} + }; + + internal static string RemoveInvalidDescriptionCharacters(string originalDescription) + { + if (string.IsNullOrEmpty(originalDescription)) return string.Empty; + originalDescription = InvalidCharactersReplacements + .Aggregate(originalDescription, (current, replacement) => current.Replace(replacement.Key, replacement.Value, StringComparison.OrdinalIgnoreCase)); + return originalDescription; + } public override bool WriteShortDescription(IDocumentedElement element, LanguageWriter writer, string prefix = "", string suffix = "") { ArgumentNullException.ThrowIfNull(writer); diff --git a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs index 6eee533f97..79025ef0fa 100644 --- a/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/TypeScript/CodeFunctionWriterTests.cs @@ -1116,6 +1116,23 @@ public async Task WritesConstructorWithEnumValueAsync() { Name = "pictureSize" }).First(); + + codeEnum.AddOption( + new CodeEnumOption + { + Name = "256x256", + SerializationName = "256x256" + }, + new CodeEnumOption + { + Name = "512x512", + SerializationName = "512x512" + }, + new CodeEnumOption + { + Name = "1024x1024", + SerializationName = "1024x1024" + }); parentClass.AddProperty(new CodeProperty { Name = propName,