From d9717b1448d244681191c9b82b5fb69c344a00af Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 22 Feb 2024 08:15:15 -0700 Subject: [PATCH 01/10] sanitize string headers when writing request generator --- .../Writers/CSharp/CodeMethodWriter.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs index bdab613416..f33ed1341c 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs @@ -418,22 +418,23 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req writer.WriteLine($"{RequestInfoVarName}.Configure({requestParams.requestConfiguration.Name});"); if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue}\");"); + writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{SanitizeString(codeElement.AcceptHeaderValue)}\");"); if (requestParams.requestBody != null) { var suffix = requestParams.requestBody.Type.IsCollection ? "Collection" : string.Empty; + var sanitizedRequestBodyContentType = SanitizeString(codeElement.RequestBodyContentType); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"{RequestInfoVarName}.SetStreamContent({requestParams.requestBody.Name}, {requestParams.requestContentType.Name});"); - else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"{RequestInfoVarName}.SetStreamContent({requestParams.requestBody.Name}, \"{codeElement.RequestBodyContentType}\");"); + else if (!string.IsNullOrEmpty(sanitizedRequestBodyContentType)) + writer.WriteLine($"{RequestInfoVarName}.SetStreamContent({requestParams.requestBody.Name}, \"{sanitizedRequestBodyContentType}\");"); } else if (currentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) if (requestParams.requestBody.Type is CodeType bodyType && (bodyType.TypeDefinition is CodeClass || bodyType.Name.Equals("MultipartBody", StringComparison.OrdinalIgnoreCase))) - writer.WriteLine($"{RequestInfoVarName}.SetContentFromParsable({requestAdapterProperty.Name.ToFirstCharacterUpperCase()}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name});"); + writer.WriteLine($"{RequestInfoVarName}.SetContentFromParsable({requestAdapterProperty.Name.ToFirstCharacterUpperCase()}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name});"); else - writer.WriteLine($"{RequestInfoVarName}.SetContentFromScalar{suffix}({requestAdapterProperty.Name.ToFirstCharacterUpperCase()}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name});"); + writer.WriteLine($"{RequestInfoVarName}.SetContentFromScalar{suffix}({requestAdapterProperty.Name.ToFirstCharacterUpperCase()}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name});"); } writer.WriteLine($"return {RequestInfoVarName};"); @@ -669,4 +670,12 @@ _ when conventions.IsPrimitiveType(propertyType) => $"Write{propertyType.TrimEnd _ => $"WriteObjectValue<{propertyType.ToFirstCharacterUpperCase()}{(includeNullableRef ? "?" : string.Empty)}>", }; } + + /// + /// Sanitize a string for direct writing. + /// + /// The string to sanitize. + /// The sanitized string. + private static string SanitizeString(string input) + => input.Replace("\"", "\\\"", StringComparison.Ordinal); } From cff4cef89946f4eac8cd00e6c31d8477ff3fc7cc Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 22 Feb 2024 09:15:31 -0700 Subject: [PATCH 02/10] update other generators --- .../Writers/CLI/CliCodeMethodWriter.cs | 7 +++-- .../Writers/CSharp/CodeMethodWriter.cs | 12 ++------ .../Writers/Go/CodeMethodWriter.cs | 11 ++++---- .../Writers/Java/CodeMethodWriter.cs | 9 +++--- .../Writers/Php/CodeMethodWriter.cs | 9 +++--- .../Writers/Python/CodeMethodWriter.cs | 9 +++--- .../Writers/Ruby/CodeMethodWriter.cs | 9 +++--- src/Kiota.Builder/Writers/StringExtensions.cs | 28 +++++++++++++++++-- .../Writers/TypeScript/CodeConstantWriter.cs | 7 +++-- 9 files changed, 62 insertions(+), 39 deletions(-) diff --git a/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs b/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs index 9d7c94edca..b651471c26 100644 --- a/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs @@ -639,7 +639,7 @@ protected virtual void WriteCommandHandlerBody(CodeMethod codeElement, CodeClass if (requestBodyParamType?.TypeDefinition is CodeClass) { writer.WriteLine($"using var stream = new MemoryStream(Encoding.UTF8.GetBytes({requestBodyParam.Name}));"); - writer.WriteLine($"var parseNode = ParseNodeFactoryRegistry.DefaultInstance.GetRootParseNode(\"{generatorMethod.RequestBodyContentType}\", stream);"); + writer.WriteLine($"var parseNode = ParseNodeFactoryRegistry.DefaultInstance.GetRootParseNode(\"{generatorMethod.RequestBodyContentType.SanitizeDoubleQuote()}\", stream);"); var typeString = conventions.GetTypeString(requestBodyParamType, requestBodyParam, false); @@ -757,9 +757,10 @@ private static void WriteRequestInformation(LanguageWriter writer, CodeMethod ge // Set the content type header. Will not add the code if the method is a stream, has no RequestBodyContentType or if there's no body parameter. if (!isStream && generatorMethod.Parameters.Any(p => p.IsOfKind(CodeParameterKind.RequestBody))) { - if (!string.IsNullOrWhiteSpace(generatorMethod.RequestBodyContentType)) + var sanitizedRequestBodyContentType = generatorMethod.RequestBodyContentType.SanitizeDoubleQuote(); + if (!string.IsNullOrWhiteSpace(sanitizedRequestBodyContentType)) { - writer.WriteLine($"requestInfo.SetContentFromParsable({RequestAdapterParamName}, \"{generatorMethod.RequestBodyContentType}\", model);"); + writer.WriteLine($"requestInfo.SetContentFromParsable({RequestAdapterParamName}, \"{sanitizedRequestBodyContentType}\", model);"); } else { diff --git a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs index f33ed1341c..08a2e53183 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs @@ -418,11 +418,11 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req writer.WriteLine($"{RequestInfoVarName}.Configure({requestParams.requestConfiguration.Name});"); if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{SanitizeString(codeElement.AcceptHeaderValue)}\");"); + writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue.SanitizeDoubleQuote()}\");"); if (requestParams.requestBody != null) { var suffix = requestParams.requestBody.Type.IsCollection ? "Collection" : string.Empty; - var sanitizedRequestBodyContentType = SanitizeString(codeElement.RequestBodyContentType); + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) @@ -670,12 +670,4 @@ _ when conventions.IsPrimitiveType(propertyType) => $"Write{propertyType.TrimEnd _ => $"WriteObjectValue<{propertyType.ToFirstCharacterUpperCase()}{(includeNullableRef ? "?" : string.Empty)}>", }; } - - /// - /// Sanitize a string for direct writing. - /// - /// The string to sanitize. - /// The sanitized string. - private static string SanitizeString(string input) - => input.Replace("\"", "\\\"", StringComparison.Ordinal); } diff --git a/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs index cb7889c03f..41b1456864 100644 --- a/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs @@ -882,17 +882,18 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req WriteLegacyRequestConfiguration(requestParams, writer); if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue}\")"); + writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue.SanitizeDoubleQuote()}\")"); if (requestParams.requestBody != null) { var bodyParamReference = $"{requestParams.requestBody.Name.ToFirstCharacterLowerCase()}"; var collectionSuffix = requestParams.requestBody.Type.IsCollection ? "Collection" : string.Empty; + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals("binary", StringComparison.OrdinalIgnoreCase) || requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"{RequestInfoVarName}.SetStreamContentAndContentType({bodyParamReference}, *{requestParams.requestContentType.Name.ToFirstCharacterLowerCase()})"); - else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"{RequestInfoVarName}.SetStreamContentAndContentType({bodyParamReference}, \"{codeElement.RequestBodyContentType}\")"); + else if (!string.IsNullOrEmpty(sanitizedRequestBodyContentType)) + writer.WriteLine($"{RequestInfoVarName}.SetStreamContentAndContentType({bodyParamReference}, \"{sanitizedRequestBodyContentType}\")"); } else if (requestParams.requestBody.Type is CodeType bodyType && (bodyType.TypeDefinition is CodeClass || bodyType.TypeDefinition is CodeInterface || bodyType.Name.Equals("MultipartBody", StringComparison.OrdinalIgnoreCase))) { @@ -902,11 +903,11 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req WriteCollectionCast(parsableSymbol, bodyParamReference, "cast", writer, string.Empty, false); bodyParamReference = "cast"; } - writer.WriteLine($"err := {RequestInfoVarName}.SetContentFromParsable{collectionSuffix}({contextParameterName}, m.{requestAdapterPropertyName}, \"{codeElement.RequestBodyContentType}\", {bodyParamReference})"); + writer.WriteLine($"err := {RequestInfoVarName}.SetContentFromParsable{collectionSuffix}({contextParameterName}, m.{requestAdapterPropertyName}, \"{sanitizedRequestBodyContentType}\", {bodyParamReference})"); writer.WriteBlock("if err != nil {", "}", "return nil, err"); } else - writer.WriteLine($"{RequestInfoVarName}.SetContentFromScalar{collectionSuffix}({contextParameterName}, m.{requestAdapterPropertyName}, \"{codeElement.RequestBodyContentType}\", {bodyParamReference})"); + writer.WriteLine($"{RequestInfoVarName}.SetContentFromScalar{collectionSuffix}({contextParameterName}, m.{requestAdapterPropertyName}, \"{sanitizedRequestBodyContentType}\", {bodyParamReference})"); } writer.WriteLine($"return {RequestInfoVarName}, nil"); diff --git a/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs index ab24292b6d..8bf41ca027 100644 --- a/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs @@ -585,24 +585,25 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req } if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}.headers.tryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue}\");"); + writer.WriteLine($"{RequestInfoVarName}.headers.tryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue.SanitizeDoubleQuote()}\");"); if (requestParams.requestBody != null && currentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) { var toArrayPostfix = requestParams.requestBody.Type.IsCollection ? $".toArray(new {requestParams.requestBody.Type.Name}[0])" : string.Empty; var collectionPostfix = requestParams.requestBody.Type.IsCollection ? "Collection" : string.Empty; + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"{RequestInfoVarName}.setStreamContent({requestParams.requestBody.Name}, {requestParams.requestContentType.Name});"); else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"{RequestInfoVarName}.setStreamContent({requestParams.requestBody.Name}, \"{codeElement.RequestBodyContentType}\");"); + writer.WriteLine($"{RequestInfoVarName}.setStreamContent({requestParams.requestBody.Name}, \"{sanitizedRequestBodyContentType}\");"); } else if (requestParams.requestBody.Type is CodeType bodyType && (bodyType.TypeDefinition is CodeClass || bodyType.Name.Equals("MultipartBody", StringComparison.OrdinalIgnoreCase))) - writer.WriteLine($"{RequestInfoVarName}.setContentFromParsable({requestAdapterProperty.Name}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name}{toArrayPostfix});"); + writer.WriteLine($"{RequestInfoVarName}.setContentFromParsable({requestAdapterProperty.Name}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name}{toArrayPostfix});"); else - writer.WriteLine($"{RequestInfoVarName}.setContentFromScalar{collectionPostfix}({requestAdapterProperty.Name}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name}{toArrayPostfix});"); + writer.WriteLine($"{RequestInfoVarName}.setContentFromScalar{collectionPostfix}({requestAdapterProperty.Name}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name}{toArrayPostfix});"); } writer.WriteLine($"return {RequestInfoVarName};"); diff --git a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs index 3fc6397717..375f986024 100644 --- a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs @@ -568,18 +568,19 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req if (requestParams.requestBody != null) { var suffix = requestParams.requestBody.Type.IsCollection ? "Collection" : string.Empty; + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"{RequestInfoVarName}->setStreamContent({conventions.GetParameterName(requestParams.requestBody)}, {conventions.GetParameterName(requestParams.requestContentType)});"); else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"{RequestInfoVarName}->setStreamContent({conventions.GetParameterName(requestParams.requestBody)}, \"{codeElement.RequestBodyContentType}\");"); + writer.WriteLine($"{RequestInfoVarName}->setStreamContent({conventions.GetParameterName(requestParams.requestBody)}, \"{sanitizedRequestBodyContentType}\");"); } else if (currentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) if (requestParams.requestBody.Type is CodeType bodyType && bodyType.TypeDefinition is CodeClass) - writer.WriteLine($"{RequestInfoVarName}->setContentFromParsable{suffix}($this->{requestAdapterProperty.Name.ToFirstCharacterLowerCase()}, \"{codeElement.RequestBodyContentType}\", {conventions.GetParameterName(requestParams.requestBody)});"); + writer.WriteLine($"{RequestInfoVarName}->setContentFromParsable{suffix}($this->{requestAdapterProperty.Name.ToFirstCharacterLowerCase()}, \"{sanitizedRequestBodyContentType}\", {conventions.GetParameterName(requestParams.requestBody)});"); else - writer.WriteLine($"{RequestInfoVarName}->setContentFromScalar{suffix}($this->{requestAdapterProperty.Name.ToFirstCharacterLowerCase()}, \"{codeElement.RequestBodyContentType}\", {conventions.GetParameterName(requestParams.requestBody)});"); + writer.WriteLine($"{RequestInfoVarName}->setContentFromScalar{suffix}($this->{requestAdapterProperty.Name.ToFirstCharacterLowerCase()}, \"{sanitizedRequestBodyContentType}\", {conventions.GetParameterName(requestParams.requestBody)});"); } writer.WriteLine($"return {RequestInfoVarName};"); @@ -612,7 +613,7 @@ private void WriteRequestConfiguration(RequestParams requestParams, LanguageWrit private void WriteAcceptHeaderDef(CodeMethod codeMethod, LanguageWriter writer) { if (codeMethod.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}->tryAddHeader('Accept', \"{codeMethod.AcceptHeaderValue}\");"); + writer.WriteLine($"{RequestInfoVarName}->tryAddHeader('Accept', \"{codeMethod.AcceptHeaderValue.SanitizeDoubleQuote()}\");"); } private void WriteDeserializerBody(CodeClass parentClass, LanguageWriter writer, CodeMethod method, bool extendsModelClass = false) { diff --git a/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs index 42a222e36b..f156bf9262 100644 --- a/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs @@ -619,7 +619,7 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req if (requestParams.requestConfiguration != null) writer.WriteLine($"{RequestInfoVarName}.configure({requestParams.requestConfiguration.Name})"); if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}.headers.try_add(\"Accept\", \"{codeElement.AcceptHeaderValue}\")"); + writer.WriteLine($"{RequestInfoVarName}.headers.try_add(\"Accept\", \"{codeElement.AcceptHeaderValue.SanitizeDoubleQuote()}\")"); if (currentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) UpdateRequestInformationFromRequestBody(codeElement, requestParams, requestAdapterProperty, writer); writer.WriteLine($"return {RequestInfoVarName}"); @@ -823,17 +823,18 @@ private void UpdateRequestInformationFromRequestBody(CodeMethod codeElement, Req { if (requestParams.requestBody != null) { + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"{RequestInfoVarName}.set_stream_content({requestParams.requestBody.Name}, {requestParams.requestContentType.Name})"); - else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"{RequestInfoVarName}.set_stream_content({requestParams.requestBody.Name}, \"{codeElement.RequestBodyContentType}\")"); + else if (!string.IsNullOrEmpty(sanitizedRequestBodyContentType)) + writer.WriteLine($"{RequestInfoVarName}.set_stream_content({requestParams.requestBody.Name}, \"{sanitizedRequestBodyContentType}\")"); } else { var setMethodName = requestParams.requestBody.Type is CodeType bodyType && bodyType.TypeDefinition is CodeClass ? "set_content_from_parsable" : "set_content_from_scalar"; - writer.WriteLine($"{RequestInfoVarName}.{setMethodName}(self.{requestAdapterProperty.Name}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name})"); + writer.WriteLine($"{RequestInfoVarName}.{setMethodName}(self.{requestAdapterProperty.Name}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name})"); } } } diff --git a/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs index a20eeb3823..0bad38caf3 100644 --- a/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs @@ -301,15 +301,16 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req } if (requestParams.requestBody != null) { + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"request_info.set_stream_content({requestParams.requestBody.Name}, {requestParams.requestContentType.Name})"); - else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"request_info.set_stream_content({requestParams.requestBody.Name}, \"{codeElement.RequestBodyContentType}\")"); + else if (!string.IsNullOrEmpty(sanitizedRequestBodyContentType)) + writer.WriteLine($"request_info.set_stream_content({requestParams.requestBody.Name}, \"{sanitizedRequestBodyContentType}\")"); } else if (parentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) - writer.WriteLine($"request_info.set_content_from_parsable(@{requestAdapterProperty.Name.ToSnakeCase()}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name})"); + writer.WriteLine($"request_info.set_content_from_parsable(@{requestAdapterProperty.Name.ToSnakeCase()}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name})"); } } if (parentClass.GetPropertyOfKind(CodePropertyKind.PathParameters) is CodeProperty urlTemplateParamsProperty && @@ -321,7 +322,7 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req } writer.WriteLine($"request_info.http_method = :{codeElement.HttpMethod.Value.ToString().ToUpperInvariant()}"); if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"request_info.headers.try_add('Accept', '{codeElement.AcceptHeaderValue}')"); + writer.WriteLine($"request_info.headers.try_add('Accept', '{codeElement.AcceptHeaderValue.SanitizeSingleQuote()}')"); writer.WriteLine("return request_info"); } private static string GetPropertyCall(CodeProperty property, string defaultValue) => property == null ? defaultValue : $"@{property.NamePrefix}{property.Name.ToSnakeCase()}"; diff --git a/src/Kiota.Builder/Writers/StringExtensions.cs b/src/Kiota.Builder/Writers/StringExtensions.cs index c3e9c21043..ace7e7a465 100644 --- a/src/Kiota.Builder/Writers/StringExtensions.cs +++ b/src/Kiota.Builder/Writers/StringExtensions.cs @@ -1,5 +1,29 @@ -namespace Kiota.Builder.Writers; +using System; + +namespace Kiota.Builder.Writers; + public static class StringExtensions { - public static string StripArraySuffix(this string original) => string.IsNullOrEmpty(original) ? original : original.TrimEnd(']').TrimEnd('['); + public static string StripArraySuffix(this string original) => + string.IsNullOrEmpty(original) ? original : original.TrimEnd(']').TrimEnd('['); + + /// + /// Sanitize a string for direct writing. + /// + /// The string to sanitize. + /// The sanitized string. + public static string SanitizeDoubleQuote(this string original) + => string.IsNullOrEmpty(original) + ? original + : original.Replace("\"", "\\\"", StringComparison.Ordinal); + + /// + /// Sanitize a string for direct writing. + /// + /// The string to sanitize. + /// The sanitized string. + public static string SanitizeSingleQuote(this string original) + => string.IsNullOrEmpty(original) + ? original + : original.Replace("'", "\\'", StringComparison.Ordinal); } diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs index c1d37e9233..a3e0f9401b 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs @@ -105,7 +105,7 @@ private void WriteRequestsMetadataConstant(CodeConstant codeElement, LanguageWri writer.WriteLine($"uriTemplate: {urlTemplateValue},"); if (codeClass.Methods.FirstOrDefault(x => x.Kind is CodeMethodKind.RequestGenerator && x.HttpMethod == executorMethod.HttpMethod) is { } generatorMethod && generatorMethod.AcceptHeaderValue is string acceptHeader && !string.IsNullOrEmpty(acceptHeader)) - writer.WriteLine($"responseBodyContentType: \"{acceptHeader}\","); + writer.WriteLine($"responseBodyContentType: \"{acceptHeader.SanitizeDoubleQuote()}\","); if (executorMethod.ErrorMappings.Any()) { writer.StartBlock("errorMappings: {"); @@ -118,8 +118,9 @@ private void WriteRequestsMetadataConstant(CodeConstant codeElement, LanguageWri writer.WriteLine($"adapterMethodName: \"{GetSendRequestMethodName(isVoid, isStream, executorMethod.ReturnType.IsCollection, returnTypeWithoutCollectionSymbol)}\","); if (!isVoid) writer.WriteLine($"responseBodyFactory: {GetTypeFactory(isVoid, isStream, executorMethod, writer)},"); - if (!string.IsNullOrEmpty(executorMethod.RequestBodyContentType)) - writer.WriteLine($"requestBodyContentType: \"{executorMethod.RequestBodyContentType}\","); + var sanitizedRequestBodyContentType = executorMethod.RequestBodyContentType.SanitizeDoubleQuote(); + if (!string.IsNullOrEmpty(sanitizedRequestBodyContentType)) + writer.WriteLine($"requestBodyContentType: \"{sanitizedRequestBodyContentType}\","); if (executorMethod.Parameters.FirstOrDefault(static x => x.Kind is CodeParameterKind.RequestBody) is CodeParameter requestBody) { if (GetBodySerializer(requestBody) is string bodySerializer) From f5905a47b9cc6a47b2248e6dd803fd59496afd81 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 22 Feb 2024 09:15:39 -0700 Subject: [PATCH 03/10] add tests --- .../Writers/CSharp/CodeMethodWriterTests.cs | 27 ++++++++++++++++++ .../Writers/Go/CodeMethodWriterTests.cs | 27 ++++++++++++++++++ .../Writers/Java/CodeMethodWriterTests.cs | 27 ++++++++++++++++++ .../Writers/Php/CodeMethodWriterTests.cs | 28 ++++++++++++++++++- .../Writers/Python/CodeMethodWriterTests.cs | 27 ++++++++++++++++++ .../Writers/Ruby/CodeMethodWriterTests.cs | 27 ++++++++++++++++++ 6 files changed, 162 insertions(+), 1 deletion(-) diff --git a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs index 2dc8a949f3..23a5f940c3 100644 --- a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs @@ -1850,4 +1850,31 @@ public void WritesDeprecationInformationFromBuilder() var result = tw.ToString(); Assert.Contains("This method is obsolete. Use NewAwesomeMethod instead.", result); } + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile=\"CamelCase\""); + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("requestInfo.Headers.TryAdd(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\")", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } diff --git a/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs index da4ee17d6b..9b099d21f0 100644 --- a/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs @@ -2266,4 +2266,31 @@ public void WritesMessageOverrideOnPrimary() Assert.Contains("Error()(string) {", result); Assert.Contains("return *(m.GetProp1()", result); } + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile=\"CamelCase\""); + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("requestInfo.Headers.TryAdd(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\")", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } diff --git a/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs index 1fa32f6de4..b472fa3aa6 100644 --- a/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs @@ -2132,4 +2132,31 @@ public void WritesMessageOverrideOnPrimary() Assert.Contains("String getErrorMessage() ", result); Assert.Contains("return this.getProp1()", result); } + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile=\"CamelCase\""); + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("requestInfo.headers.tryAdd(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\");", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } diff --git a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs index d7fecc9ab7..2682b6a9e6 100644 --- a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs @@ -2481,5 +2481,31 @@ public async Task WritesFullyQualifiedNameWhenSimilarTypeAlreadyExists() Assert.Contains("return $this->requestAdapter->sendAsync($requestInfo, [\\Microsoft\\Graph\\Models\\Security\\ModelA::class, 'createFromDiscriminatorValue'], null);", result); Assert.Contains("return $this->requestAdapter->sendCollectionAsync($requestInfo, [Component::class, 'createFromDiscriminatorValue'], null);", result); } - + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile=\"CamelCase\""); + languageWriter.Write(method); + var result = stringWriter.ToString(); + Assert.Contains("$requestInfo->tryAddHeader('Accept', \"application/json; profile=\\\"CamelCase\\\"\");", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + languageWriter.Write(method); + var result = stringWriter.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } diff --git a/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs index 6693c4ba59..6f93b7124b 100644 --- a/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs @@ -2202,4 +2202,31 @@ public void WritesDeprecationInformationFromBuilder() var result = tw.ToString(); Assert.Contains("This method is obsolete. Use NewAwesomeMethod instead.", result); } + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile=\"CamelCase\""); + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("request_info.headers.try_add(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\")", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } diff --git a/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs index 44cb9bdd9c..5d24252c52 100644 --- a/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs @@ -1074,4 +1074,31 @@ public void DoesntWriteReadOnlyPropertiesInSerializerBody() Assert.DoesNotContain("ReadOnlyProperty", result); AssertExtensions.CurlyBracesAreClosed(result); } + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile='CamelCase'"); + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("request_info.headers.try_add('Accept', 'application/json; profile=\\'CamelCase\\'')", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } From ac13233e6f0564043fc5ea035facea44c8782624 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 22 Feb 2024 08:15:15 -0700 Subject: [PATCH 04/10] sanitize string headers when writing request generator --- .../Writers/CSharp/CodeMethodWriter.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs index bdab613416..f33ed1341c 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs @@ -418,22 +418,23 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req writer.WriteLine($"{RequestInfoVarName}.Configure({requestParams.requestConfiguration.Name});"); if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue}\");"); + writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{SanitizeString(codeElement.AcceptHeaderValue)}\");"); if (requestParams.requestBody != null) { var suffix = requestParams.requestBody.Type.IsCollection ? "Collection" : string.Empty; + var sanitizedRequestBodyContentType = SanitizeString(codeElement.RequestBodyContentType); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"{RequestInfoVarName}.SetStreamContent({requestParams.requestBody.Name}, {requestParams.requestContentType.Name});"); - else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"{RequestInfoVarName}.SetStreamContent({requestParams.requestBody.Name}, \"{codeElement.RequestBodyContentType}\");"); + else if (!string.IsNullOrEmpty(sanitizedRequestBodyContentType)) + writer.WriteLine($"{RequestInfoVarName}.SetStreamContent({requestParams.requestBody.Name}, \"{sanitizedRequestBodyContentType}\");"); } else if (currentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) if (requestParams.requestBody.Type is CodeType bodyType && (bodyType.TypeDefinition is CodeClass || bodyType.Name.Equals("MultipartBody", StringComparison.OrdinalIgnoreCase))) - writer.WriteLine($"{RequestInfoVarName}.SetContentFromParsable({requestAdapterProperty.Name.ToFirstCharacterUpperCase()}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name});"); + writer.WriteLine($"{RequestInfoVarName}.SetContentFromParsable({requestAdapterProperty.Name.ToFirstCharacterUpperCase()}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name});"); else - writer.WriteLine($"{RequestInfoVarName}.SetContentFromScalar{suffix}({requestAdapterProperty.Name.ToFirstCharacterUpperCase()}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name});"); + writer.WriteLine($"{RequestInfoVarName}.SetContentFromScalar{suffix}({requestAdapterProperty.Name.ToFirstCharacterUpperCase()}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name});"); } writer.WriteLine($"return {RequestInfoVarName};"); @@ -669,4 +670,12 @@ _ when conventions.IsPrimitiveType(propertyType) => $"Write{propertyType.TrimEnd _ => $"WriteObjectValue<{propertyType.ToFirstCharacterUpperCase()}{(includeNullableRef ? "?" : string.Empty)}>", }; } + + /// + /// Sanitize a string for direct writing. + /// + /// The string to sanitize. + /// The sanitized string. + private static string SanitizeString(string input) + => input.Replace("\"", "\\\"", StringComparison.Ordinal); } From d9bb58bea1f8108483cac7830aee134fbed37d5c Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 22 Feb 2024 09:15:31 -0700 Subject: [PATCH 05/10] update other generators --- .../Writers/CLI/CliCodeMethodWriter.cs | 7 +++-- .../Writers/CSharp/CodeMethodWriter.cs | 12 ++------ .../Writers/Go/CodeMethodWriter.cs | 11 ++++---- .../Writers/Java/CodeMethodWriter.cs | 9 +++--- .../Writers/Php/CodeMethodWriter.cs | 9 +++--- .../Writers/Python/CodeMethodWriter.cs | 9 +++--- .../Writers/Ruby/CodeMethodWriter.cs | 9 +++--- src/Kiota.Builder/Writers/StringExtensions.cs | 28 +++++++++++++++++-- .../Writers/TypeScript/CodeConstantWriter.cs | 7 +++-- 9 files changed, 62 insertions(+), 39 deletions(-) diff --git a/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs b/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs index 9d7c94edca..b651471c26 100644 --- a/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/CLI/CliCodeMethodWriter.cs @@ -639,7 +639,7 @@ protected virtual void WriteCommandHandlerBody(CodeMethod codeElement, CodeClass if (requestBodyParamType?.TypeDefinition is CodeClass) { writer.WriteLine($"using var stream = new MemoryStream(Encoding.UTF8.GetBytes({requestBodyParam.Name}));"); - writer.WriteLine($"var parseNode = ParseNodeFactoryRegistry.DefaultInstance.GetRootParseNode(\"{generatorMethod.RequestBodyContentType}\", stream);"); + writer.WriteLine($"var parseNode = ParseNodeFactoryRegistry.DefaultInstance.GetRootParseNode(\"{generatorMethod.RequestBodyContentType.SanitizeDoubleQuote()}\", stream);"); var typeString = conventions.GetTypeString(requestBodyParamType, requestBodyParam, false); @@ -757,9 +757,10 @@ private static void WriteRequestInformation(LanguageWriter writer, CodeMethod ge // Set the content type header. Will not add the code if the method is a stream, has no RequestBodyContentType or if there's no body parameter. if (!isStream && generatorMethod.Parameters.Any(p => p.IsOfKind(CodeParameterKind.RequestBody))) { - if (!string.IsNullOrWhiteSpace(generatorMethod.RequestBodyContentType)) + var sanitizedRequestBodyContentType = generatorMethod.RequestBodyContentType.SanitizeDoubleQuote(); + if (!string.IsNullOrWhiteSpace(sanitizedRequestBodyContentType)) { - writer.WriteLine($"requestInfo.SetContentFromParsable({RequestAdapterParamName}, \"{generatorMethod.RequestBodyContentType}\", model);"); + writer.WriteLine($"requestInfo.SetContentFromParsable({RequestAdapterParamName}, \"{sanitizedRequestBodyContentType}\", model);"); } else { diff --git a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs index f33ed1341c..08a2e53183 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs @@ -418,11 +418,11 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req writer.WriteLine($"{RequestInfoVarName}.Configure({requestParams.requestConfiguration.Name});"); if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{SanitizeString(codeElement.AcceptHeaderValue)}\");"); + writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue.SanitizeDoubleQuote()}\");"); if (requestParams.requestBody != null) { var suffix = requestParams.requestBody.Type.IsCollection ? "Collection" : string.Empty; - var sanitizedRequestBodyContentType = SanitizeString(codeElement.RequestBodyContentType); + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) @@ -670,12 +670,4 @@ _ when conventions.IsPrimitiveType(propertyType) => $"Write{propertyType.TrimEnd _ => $"WriteObjectValue<{propertyType.ToFirstCharacterUpperCase()}{(includeNullableRef ? "?" : string.Empty)}>", }; } - - /// - /// Sanitize a string for direct writing. - /// - /// The string to sanitize. - /// The sanitized string. - private static string SanitizeString(string input) - => input.Replace("\"", "\\\"", StringComparison.Ordinal); } diff --git a/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs index cb7889c03f..41b1456864 100644 --- a/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs @@ -882,17 +882,18 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req WriteLegacyRequestConfiguration(requestParams, writer); if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue}\")"); + writer.WriteLine($"{RequestInfoVarName}.Headers.TryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue.SanitizeDoubleQuote()}\")"); if (requestParams.requestBody != null) { var bodyParamReference = $"{requestParams.requestBody.Name.ToFirstCharacterLowerCase()}"; var collectionSuffix = requestParams.requestBody.Type.IsCollection ? "Collection" : string.Empty; + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals("binary", StringComparison.OrdinalIgnoreCase) || requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"{RequestInfoVarName}.SetStreamContentAndContentType({bodyParamReference}, *{requestParams.requestContentType.Name.ToFirstCharacterLowerCase()})"); - else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"{RequestInfoVarName}.SetStreamContentAndContentType({bodyParamReference}, \"{codeElement.RequestBodyContentType}\")"); + else if (!string.IsNullOrEmpty(sanitizedRequestBodyContentType)) + writer.WriteLine($"{RequestInfoVarName}.SetStreamContentAndContentType({bodyParamReference}, \"{sanitizedRequestBodyContentType}\")"); } else if (requestParams.requestBody.Type is CodeType bodyType && (bodyType.TypeDefinition is CodeClass || bodyType.TypeDefinition is CodeInterface || bodyType.Name.Equals("MultipartBody", StringComparison.OrdinalIgnoreCase))) { @@ -902,11 +903,11 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req WriteCollectionCast(parsableSymbol, bodyParamReference, "cast", writer, string.Empty, false); bodyParamReference = "cast"; } - writer.WriteLine($"err := {RequestInfoVarName}.SetContentFromParsable{collectionSuffix}({contextParameterName}, m.{requestAdapterPropertyName}, \"{codeElement.RequestBodyContentType}\", {bodyParamReference})"); + writer.WriteLine($"err := {RequestInfoVarName}.SetContentFromParsable{collectionSuffix}({contextParameterName}, m.{requestAdapterPropertyName}, \"{sanitizedRequestBodyContentType}\", {bodyParamReference})"); writer.WriteBlock("if err != nil {", "}", "return nil, err"); } else - writer.WriteLine($"{RequestInfoVarName}.SetContentFromScalar{collectionSuffix}({contextParameterName}, m.{requestAdapterPropertyName}, \"{codeElement.RequestBodyContentType}\", {bodyParamReference})"); + writer.WriteLine($"{RequestInfoVarName}.SetContentFromScalar{collectionSuffix}({contextParameterName}, m.{requestAdapterPropertyName}, \"{sanitizedRequestBodyContentType}\", {bodyParamReference})"); } writer.WriteLine($"return {RequestInfoVarName}, nil"); diff --git a/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs index ab24292b6d..8bf41ca027 100644 --- a/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs @@ -585,24 +585,25 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req } if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}.headers.tryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue}\");"); + writer.WriteLine($"{RequestInfoVarName}.headers.tryAdd(\"Accept\", \"{codeElement.AcceptHeaderValue.SanitizeDoubleQuote()}\");"); if (requestParams.requestBody != null && currentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) { var toArrayPostfix = requestParams.requestBody.Type.IsCollection ? $".toArray(new {requestParams.requestBody.Type.Name}[0])" : string.Empty; var collectionPostfix = requestParams.requestBody.Type.IsCollection ? "Collection" : string.Empty; + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"{RequestInfoVarName}.setStreamContent({requestParams.requestBody.Name}, {requestParams.requestContentType.Name});"); else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"{RequestInfoVarName}.setStreamContent({requestParams.requestBody.Name}, \"{codeElement.RequestBodyContentType}\");"); + writer.WriteLine($"{RequestInfoVarName}.setStreamContent({requestParams.requestBody.Name}, \"{sanitizedRequestBodyContentType}\");"); } else if (requestParams.requestBody.Type is CodeType bodyType && (bodyType.TypeDefinition is CodeClass || bodyType.Name.Equals("MultipartBody", StringComparison.OrdinalIgnoreCase))) - writer.WriteLine($"{RequestInfoVarName}.setContentFromParsable({requestAdapterProperty.Name}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name}{toArrayPostfix});"); + writer.WriteLine($"{RequestInfoVarName}.setContentFromParsable({requestAdapterProperty.Name}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name}{toArrayPostfix});"); else - writer.WriteLine($"{RequestInfoVarName}.setContentFromScalar{collectionPostfix}({requestAdapterProperty.Name}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name}{toArrayPostfix});"); + writer.WriteLine($"{RequestInfoVarName}.setContentFromScalar{collectionPostfix}({requestAdapterProperty.Name}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name}{toArrayPostfix});"); } writer.WriteLine($"return {RequestInfoVarName};"); diff --git a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs index 3fc6397717..375f986024 100644 --- a/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs @@ -568,18 +568,19 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req if (requestParams.requestBody != null) { var suffix = requestParams.requestBody.Type.IsCollection ? "Collection" : string.Empty; + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"{RequestInfoVarName}->setStreamContent({conventions.GetParameterName(requestParams.requestBody)}, {conventions.GetParameterName(requestParams.requestContentType)});"); else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"{RequestInfoVarName}->setStreamContent({conventions.GetParameterName(requestParams.requestBody)}, \"{codeElement.RequestBodyContentType}\");"); + writer.WriteLine($"{RequestInfoVarName}->setStreamContent({conventions.GetParameterName(requestParams.requestBody)}, \"{sanitizedRequestBodyContentType}\");"); } else if (currentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) if (requestParams.requestBody.Type is CodeType bodyType && bodyType.TypeDefinition is CodeClass) - writer.WriteLine($"{RequestInfoVarName}->setContentFromParsable{suffix}($this->{requestAdapterProperty.Name.ToFirstCharacterLowerCase()}, \"{codeElement.RequestBodyContentType}\", {conventions.GetParameterName(requestParams.requestBody)});"); + writer.WriteLine($"{RequestInfoVarName}->setContentFromParsable{suffix}($this->{requestAdapterProperty.Name.ToFirstCharacterLowerCase()}, \"{sanitizedRequestBodyContentType}\", {conventions.GetParameterName(requestParams.requestBody)});"); else - writer.WriteLine($"{RequestInfoVarName}->setContentFromScalar{suffix}($this->{requestAdapterProperty.Name.ToFirstCharacterLowerCase()}, \"{codeElement.RequestBodyContentType}\", {conventions.GetParameterName(requestParams.requestBody)});"); + writer.WriteLine($"{RequestInfoVarName}->setContentFromScalar{suffix}($this->{requestAdapterProperty.Name.ToFirstCharacterLowerCase()}, \"{sanitizedRequestBodyContentType}\", {conventions.GetParameterName(requestParams.requestBody)});"); } writer.WriteLine($"return {RequestInfoVarName};"); @@ -612,7 +613,7 @@ private void WriteRequestConfiguration(RequestParams requestParams, LanguageWrit private void WriteAcceptHeaderDef(CodeMethod codeMethod, LanguageWriter writer) { if (codeMethod.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}->tryAddHeader('Accept', \"{codeMethod.AcceptHeaderValue}\");"); + writer.WriteLine($"{RequestInfoVarName}->tryAddHeader('Accept', \"{codeMethod.AcceptHeaderValue.SanitizeDoubleQuote()}\");"); } private void WriteDeserializerBody(CodeClass parentClass, LanguageWriter writer, CodeMethod method, bool extendsModelClass = false) { diff --git a/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs index 42a222e36b..f156bf9262 100644 --- a/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs @@ -619,7 +619,7 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req if (requestParams.requestConfiguration != null) writer.WriteLine($"{RequestInfoVarName}.configure({requestParams.requestConfiguration.Name})"); if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"{RequestInfoVarName}.headers.try_add(\"Accept\", \"{codeElement.AcceptHeaderValue}\")"); + writer.WriteLine($"{RequestInfoVarName}.headers.try_add(\"Accept\", \"{codeElement.AcceptHeaderValue.SanitizeDoubleQuote()}\")"); if (currentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) UpdateRequestInformationFromRequestBody(codeElement, requestParams, requestAdapterProperty, writer); writer.WriteLine($"return {RequestInfoVarName}"); @@ -823,17 +823,18 @@ private void UpdateRequestInformationFromRequestBody(CodeMethod codeElement, Req { if (requestParams.requestBody != null) { + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"{RequestInfoVarName}.set_stream_content({requestParams.requestBody.Name}, {requestParams.requestContentType.Name})"); - else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"{RequestInfoVarName}.set_stream_content({requestParams.requestBody.Name}, \"{codeElement.RequestBodyContentType}\")"); + else if (!string.IsNullOrEmpty(sanitizedRequestBodyContentType)) + writer.WriteLine($"{RequestInfoVarName}.set_stream_content({requestParams.requestBody.Name}, \"{sanitizedRequestBodyContentType}\")"); } else { var setMethodName = requestParams.requestBody.Type is CodeType bodyType && bodyType.TypeDefinition is CodeClass ? "set_content_from_parsable" : "set_content_from_scalar"; - writer.WriteLine($"{RequestInfoVarName}.{setMethodName}(self.{requestAdapterProperty.Name}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name})"); + writer.WriteLine($"{RequestInfoVarName}.{setMethodName}(self.{requestAdapterProperty.Name}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name})"); } } } diff --git a/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs index a20eeb3823..0bad38caf3 100644 --- a/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs @@ -301,15 +301,16 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req } if (requestParams.requestBody != null) { + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"request_info.set_stream_content({requestParams.requestBody.Name}, {requestParams.requestContentType.Name})"); - else if (!string.IsNullOrEmpty(codeElement.RequestBodyContentType)) - writer.WriteLine($"request_info.set_stream_content({requestParams.requestBody.Name}, \"{codeElement.RequestBodyContentType}\")"); + else if (!string.IsNullOrEmpty(sanitizedRequestBodyContentType)) + writer.WriteLine($"request_info.set_stream_content({requestParams.requestBody.Name}, \"{sanitizedRequestBodyContentType}\")"); } else if (parentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) - writer.WriteLine($"request_info.set_content_from_parsable(@{requestAdapterProperty.Name.ToSnakeCase()}, \"{codeElement.RequestBodyContentType}\", {requestParams.requestBody.Name})"); + writer.WriteLine($"request_info.set_content_from_parsable(@{requestAdapterProperty.Name.ToSnakeCase()}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name})"); } } if (parentClass.GetPropertyOfKind(CodePropertyKind.PathParameters) is CodeProperty urlTemplateParamsProperty && @@ -321,7 +322,7 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req } writer.WriteLine($"request_info.http_method = :{codeElement.HttpMethod.Value.ToString().ToUpperInvariant()}"); if (codeElement.ShouldAddAcceptHeader) - writer.WriteLine($"request_info.headers.try_add('Accept', '{codeElement.AcceptHeaderValue}')"); + writer.WriteLine($"request_info.headers.try_add('Accept', '{codeElement.AcceptHeaderValue.SanitizeSingleQuote()}')"); writer.WriteLine("return request_info"); } private static string GetPropertyCall(CodeProperty property, string defaultValue) => property == null ? defaultValue : $"@{property.NamePrefix}{property.Name.ToSnakeCase()}"; diff --git a/src/Kiota.Builder/Writers/StringExtensions.cs b/src/Kiota.Builder/Writers/StringExtensions.cs index c3e9c21043..ace7e7a465 100644 --- a/src/Kiota.Builder/Writers/StringExtensions.cs +++ b/src/Kiota.Builder/Writers/StringExtensions.cs @@ -1,5 +1,29 @@ -namespace Kiota.Builder.Writers; +using System; + +namespace Kiota.Builder.Writers; + public static class StringExtensions { - public static string StripArraySuffix(this string original) => string.IsNullOrEmpty(original) ? original : original.TrimEnd(']').TrimEnd('['); + public static string StripArraySuffix(this string original) => + string.IsNullOrEmpty(original) ? original : original.TrimEnd(']').TrimEnd('['); + + /// + /// Sanitize a string for direct writing. + /// + /// The string to sanitize. + /// The sanitized string. + public static string SanitizeDoubleQuote(this string original) + => string.IsNullOrEmpty(original) + ? original + : original.Replace("\"", "\\\"", StringComparison.Ordinal); + + /// + /// Sanitize a string for direct writing. + /// + /// The string to sanitize. + /// The sanitized string. + public static string SanitizeSingleQuote(this string original) + => string.IsNullOrEmpty(original) + ? original + : original.Replace("'", "\\'", StringComparison.Ordinal); } diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs index c1d37e9233..a3e0f9401b 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeConstantWriter.cs @@ -105,7 +105,7 @@ private void WriteRequestsMetadataConstant(CodeConstant codeElement, LanguageWri writer.WriteLine($"uriTemplate: {urlTemplateValue},"); if (codeClass.Methods.FirstOrDefault(x => x.Kind is CodeMethodKind.RequestGenerator && x.HttpMethod == executorMethod.HttpMethod) is { } generatorMethod && generatorMethod.AcceptHeaderValue is string acceptHeader && !string.IsNullOrEmpty(acceptHeader)) - writer.WriteLine($"responseBodyContentType: \"{acceptHeader}\","); + writer.WriteLine($"responseBodyContentType: \"{acceptHeader.SanitizeDoubleQuote()}\","); if (executorMethod.ErrorMappings.Any()) { writer.StartBlock("errorMappings: {"); @@ -118,8 +118,9 @@ private void WriteRequestsMetadataConstant(CodeConstant codeElement, LanguageWri writer.WriteLine($"adapterMethodName: \"{GetSendRequestMethodName(isVoid, isStream, executorMethod.ReturnType.IsCollection, returnTypeWithoutCollectionSymbol)}\","); if (!isVoid) writer.WriteLine($"responseBodyFactory: {GetTypeFactory(isVoid, isStream, executorMethod, writer)},"); - if (!string.IsNullOrEmpty(executorMethod.RequestBodyContentType)) - writer.WriteLine($"requestBodyContentType: \"{executorMethod.RequestBodyContentType}\","); + var sanitizedRequestBodyContentType = executorMethod.RequestBodyContentType.SanitizeDoubleQuote(); + if (!string.IsNullOrEmpty(sanitizedRequestBodyContentType)) + writer.WriteLine($"requestBodyContentType: \"{sanitizedRequestBodyContentType}\","); if (executorMethod.Parameters.FirstOrDefault(static x => x.Kind is CodeParameterKind.RequestBody) is CodeParameter requestBody) { if (GetBodySerializer(requestBody) is string bodySerializer) From 5710263c6ba7bc20cccf7227c7b862a55563747f Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 22 Feb 2024 09:15:39 -0700 Subject: [PATCH 06/10] add tests --- .../Writers/CSharp/CodeMethodWriterTests.cs | 27 ++++++++++++++++++ .../Writers/Go/CodeMethodWriterTests.cs | 27 ++++++++++++++++++ .../Writers/Java/CodeMethodWriterTests.cs | 27 ++++++++++++++++++ .../Writers/Php/CodeMethodWriterTests.cs | 28 ++++++++++++++++++- .../Writers/Python/CodeMethodWriterTests.cs | 27 ++++++++++++++++++ .../Writers/Ruby/CodeMethodWriterTests.cs | 27 ++++++++++++++++++ 6 files changed, 162 insertions(+), 1 deletion(-) diff --git a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs index 2dc8a949f3..23a5f940c3 100644 --- a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs @@ -1850,4 +1850,31 @@ public void WritesDeprecationInformationFromBuilder() var result = tw.ToString(); Assert.Contains("This method is obsolete. Use NewAwesomeMethod instead.", result); } + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile=\"CamelCase\""); + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("requestInfo.Headers.TryAdd(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\")", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } diff --git a/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs index da4ee17d6b..9b099d21f0 100644 --- a/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs @@ -2266,4 +2266,31 @@ public void WritesMessageOverrideOnPrimary() Assert.Contains("Error()(string) {", result); Assert.Contains("return *(m.GetProp1()", result); } + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile=\"CamelCase\""); + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("requestInfo.Headers.TryAdd(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\")", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } diff --git a/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs index 1fa32f6de4..b472fa3aa6 100644 --- a/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs @@ -2132,4 +2132,31 @@ public void WritesMessageOverrideOnPrimary() Assert.Contains("String getErrorMessage() ", result); Assert.Contains("return this.getProp1()", result); } + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile=\"CamelCase\""); + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("requestInfo.headers.tryAdd(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\");", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } diff --git a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs index d7fecc9ab7..2682b6a9e6 100644 --- a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs @@ -2481,5 +2481,31 @@ public async Task WritesFullyQualifiedNameWhenSimilarTypeAlreadyExists() Assert.Contains("return $this->requestAdapter->sendAsync($requestInfo, [\\Microsoft\\Graph\\Models\\Security\\ModelA::class, 'createFromDiscriminatorValue'], null);", result); Assert.Contains("return $this->requestAdapter->sendCollectionAsync($requestInfo, [Component::class, 'createFromDiscriminatorValue'], null);", result); } - + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile=\"CamelCase\""); + languageWriter.Write(method); + var result = stringWriter.ToString(); + Assert.Contains("$requestInfo->tryAddHeader('Accept', \"application/json; profile=\\\"CamelCase\\\"\");", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + languageWriter.Write(method); + var result = stringWriter.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } diff --git a/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs index 6693c4ba59..6f93b7124b 100644 --- a/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs @@ -2202,4 +2202,31 @@ public void WritesDeprecationInformationFromBuilder() var result = tw.ToString(); Assert.Contains("This method is obsolete. Use NewAwesomeMethod instead.", result); } + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile=\"CamelCase\""); + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("request_info.headers.try_add(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\")", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } diff --git a/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs index 44cb9bdd9c..5d24252c52 100644 --- a/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs @@ -1074,4 +1074,31 @@ public void DoesntWriteReadOnlyPropertiesInSerializerBody() Assert.DoesNotContain("ReadOnlyProperty", result); AssertExtensions.CurlyBracesAreClosed(result); } + + [Fact] + public void WritesRequestGeneratorAcceptHeaderQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Get; + AddRequestProperties(); + method.AcceptedResponseTypes.Add("application/json; profile='CamelCase'"); + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("request_info.headers.try_add('Accept', 'application/json; profile=\\'CamelCase\\'')", result); + } + + [Fact] + public void WritesRequestGeneratorContentTypeQuotes() + { + setup(); + method.Kind = CodeMethodKind.RequestGenerator; + method.HttpMethod = HttpMethod.Post; + AddRequestProperties(); + AddRequestBodyParameters(); + method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + writer.Write(method); + var result = tw.ToString(); + Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + } } From 2bb8492823aa2dec6d131b213ed574b075cb5cff Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Thu, 22 Feb 2024 09:19:19 -0700 Subject: [PATCH 07/10] update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82f797bd6f..5b6ee54c0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Deduplicates 4XX and 5XX error mappings when they map to the same type to reduce emitted code. [#4025](https://github.com/microsoft/kiota/issues/4025) - 📢📢📢 Java generation is now stable! 🚀🚀🚀 special thanks to @andreaTP (Red Hat) for all the help. - Fixed bug where stream responses would generate incorrect partial paging code. [#4207](https://github.com/microsoft/kiota/issues/4207) +- Sanitize Accept and Content-Type headers during generation. [#4211](https://github.com/microsoft/kiota/issues/4211) ## [1.11.1] - 2024-02-05 From dc3c44363ea8713638ea0b9f64a5d9859a8caaaf Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 22 Feb 2024 14:14:24 -0500 Subject: [PATCH 08/10] Apply suggestions from code review --- src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs | 6 +++--- .../Writers/Ruby/CodeMethodWriterTests.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs index 0bad38caf3..8b51bc0e29 100644 --- a/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Ruby/CodeMethodWriter.cs @@ -301,16 +301,16 @@ private void WriteRequestGeneratorBody(CodeMethod codeElement, RequestParams req } if (requestParams.requestBody != null) { - var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeDoubleQuote(); + var sanitizedRequestBodyContentType = codeElement.RequestBodyContentType.SanitizeSingleQuote(); if (requestParams.requestBody.Type.Name.Equals(conventions.StreamTypeName, StringComparison.OrdinalIgnoreCase)) { if (requestParams.requestContentType is not null) writer.WriteLine($"request_info.set_stream_content({requestParams.requestBody.Name}, {requestParams.requestContentType.Name})"); else if (!string.IsNullOrEmpty(sanitizedRequestBodyContentType)) - writer.WriteLine($"request_info.set_stream_content({requestParams.requestBody.Name}, \"{sanitizedRequestBodyContentType}\")"); + writer.WriteLine($"request_info.set_stream_content({requestParams.requestBody.Name}, '{sanitizedRequestBodyContentType}')"); } else if (parentClass.GetPropertyOfKind(CodePropertyKind.RequestAdapter) is CodeProperty requestAdapterProperty) - writer.WriteLine($"request_info.set_content_from_parsable(@{requestAdapterProperty.Name.ToSnakeCase()}, \"{sanitizedRequestBodyContentType}\", {requestParams.requestBody.Name})"); + writer.WriteLine($"request_info.set_content_from_parsable(@{requestAdapterProperty.Name.ToSnakeCase()}, '{sanitizedRequestBodyContentType}', {requestParams.requestBody.Name})"); } } if (parentClass.GetPropertyOfKind(CodePropertyKind.PathParameters) is CodeProperty urlTemplateParamsProperty && diff --git a/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs index 5d24252c52..42801436e5 100644 --- a/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs @@ -1099,6 +1099,6 @@ public void WritesRequestGeneratorContentTypeQuotes() method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; writer.Write(method); var result = tw.ToString(); - Assert.Contains("\"application/json; profile=\\\"CamelCase\\\"\"", result); + Assert.Contains("'application/json; profile=\\'CamelCase\\''", result); } } From 6e6735056d2e568635767fa36ab8287533c8253f Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 22 Feb 2024 14:56:59 -0500 Subject: [PATCH 09/10] - fixes formatting --- src/Kiota.Builder/Writers/StringExtensions.cs | 2 +- .../Writers/CSharp/CodeMethodWriterTests.cs | 4 ++-- tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs | 4 ++-- .../Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs | 4 ++-- .../Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs | 4 ++-- .../Writers/Python/CodeMethodWriterTests.cs | 4 ++-- .../Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Kiota.Builder/Writers/StringExtensions.cs b/src/Kiota.Builder/Writers/StringExtensions.cs index ace7e7a465..46f9461283 100644 --- a/src/Kiota.Builder/Writers/StringExtensions.cs +++ b/src/Kiota.Builder/Writers/StringExtensions.cs @@ -16,7 +16,7 @@ public static string SanitizeDoubleQuote(this string original) => string.IsNullOrEmpty(original) ? original : original.Replace("\"", "\\\"", StringComparison.Ordinal); - + /// /// Sanitize a string for direct writing. /// diff --git a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs index 23a5f940c3..820e7d2a5d 100644 --- a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs @@ -1850,7 +1850,7 @@ public void WritesDeprecationInformationFromBuilder() var result = tw.ToString(); Assert.Contains("This method is obsolete. Use NewAwesomeMethod instead.", result); } - + [Fact] public void WritesRequestGeneratorAcceptHeaderQuotes() { @@ -1863,7 +1863,7 @@ public void WritesRequestGeneratorAcceptHeaderQuotes() var result = tw.ToString(); Assert.Contains("requestInfo.Headers.TryAdd(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\")", result); } - + [Fact] public void WritesRequestGeneratorContentTypeQuotes() { diff --git a/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs index 9b099d21f0..54ded91a6e 100644 --- a/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Go/CodeMethodWriterTests.cs @@ -2266,7 +2266,7 @@ public void WritesMessageOverrideOnPrimary() Assert.Contains("Error()(string) {", result); Assert.Contains("return *(m.GetProp1()", result); } - + [Fact] public void WritesRequestGeneratorAcceptHeaderQuotes() { @@ -2279,7 +2279,7 @@ public void WritesRequestGeneratorAcceptHeaderQuotes() var result = tw.ToString(); Assert.Contains("requestInfo.Headers.TryAdd(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\")", result); } - + [Fact] public void WritesRequestGeneratorContentTypeQuotes() { diff --git a/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs index b472fa3aa6..dca1b6850f 100644 --- a/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Java/CodeMethodWriterTests.cs @@ -2132,7 +2132,7 @@ public void WritesMessageOverrideOnPrimary() Assert.Contains("String getErrorMessage() ", result); Assert.Contains("return this.getProp1()", result); } - + [Fact] public void WritesRequestGeneratorAcceptHeaderQuotes() { @@ -2145,7 +2145,7 @@ public void WritesRequestGeneratorAcceptHeaderQuotes() var result = tw.ToString(); Assert.Contains("requestInfo.headers.tryAdd(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\");", result); } - + [Fact] public void WritesRequestGeneratorContentTypeQuotes() { diff --git a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs index 2682b6a9e6..f5dd725799 100644 --- a/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Php/CodeMethodWriterTests.cs @@ -2481,7 +2481,7 @@ public async Task WritesFullyQualifiedNameWhenSimilarTypeAlreadyExists() Assert.Contains("return $this->requestAdapter->sendAsync($requestInfo, [\\Microsoft\\Graph\\Models\\Security\\ModelA::class, 'createFromDiscriminatorValue'], null);", result); Assert.Contains("return $this->requestAdapter->sendCollectionAsync($requestInfo, [Component::class, 'createFromDiscriminatorValue'], null);", result); } - + [Fact] public void WritesRequestGeneratorAcceptHeaderQuotes() { @@ -2494,7 +2494,7 @@ public void WritesRequestGeneratorAcceptHeaderQuotes() var result = stringWriter.ToString(); Assert.Contains("$requestInfo->tryAddHeader('Accept', \"application/json; profile=\\\"CamelCase\\\"\");", result); } - + [Fact] public void WritesRequestGeneratorContentTypeQuotes() { diff --git a/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs index 6f93b7124b..b4d73a1481 100644 --- a/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs @@ -2202,7 +2202,7 @@ public void WritesDeprecationInformationFromBuilder() var result = tw.ToString(); Assert.Contains("This method is obsolete. Use NewAwesomeMethod instead.", result); } - + [Fact] public void WritesRequestGeneratorAcceptHeaderQuotes() { @@ -2215,7 +2215,7 @@ public void WritesRequestGeneratorAcceptHeaderQuotes() var result = tw.ToString(); Assert.Contains("request_info.headers.try_add(\"Accept\", \"application/json; profile=\\\"CamelCase\\\"\")", result); } - + [Fact] public void WritesRequestGeneratorContentTypeQuotes() { diff --git a/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs index 42801436e5..2933fa05fe 100644 --- a/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs @@ -1074,7 +1074,7 @@ public void DoesntWriteReadOnlyPropertiesInSerializerBody() Assert.DoesNotContain("ReadOnlyProperty", result); AssertExtensions.CurlyBracesAreClosed(result); } - + [Fact] public void WritesRequestGeneratorAcceptHeaderQuotes() { @@ -1087,7 +1087,7 @@ public void WritesRequestGeneratorAcceptHeaderQuotes() var result = tw.ToString(); Assert.Contains("request_info.headers.try_add('Accept', 'application/json; profile=\\'CamelCase\\'')", result); } - + [Fact] public void WritesRequestGeneratorContentTypeQuotes() { From a0e3d6c041cce71bfbae2c03b58dfca72baba552 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 22 Feb 2024 15:12:17 -0500 Subject: [PATCH 10/10] - fixes failing unit test after quotes normalziation Signed-off-by: Vincent Biret --- tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs index 2933fa05fe..1f2a128c5c 100644 --- a/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Ruby/CodeMethodWriterTests.cs @@ -1096,7 +1096,7 @@ public void WritesRequestGeneratorContentTypeQuotes() method.HttpMethod = HttpMethod.Post; AddRequestProperties(); AddRequestBodyParameters(); - method.RequestBodyContentType = "application/json; profile=\"CamelCase\""; + method.RequestBodyContentType = "application/json; profile='CamelCase'"; writer.Write(method); var result = tw.ToString(); Assert.Contains("'application/json; profile=\\'CamelCase\\''", result);