From 3852185fd0e919a5418431a53a5d3c4c7c3f2d36 Mon Sep 17 00:00:00 2001 From: koros Date: Tue, 19 Nov 2024 18:37:47 +0300 Subject: [PATCH] Use URL Template to write actual urls --- .../HTTP/CodeClassDeclarationWriter.cs | 199 +++++++++++++----- 1 file changed, 143 insertions(+), 56 deletions(-) diff --git a/src/Kiota.Builder/Writers/HTTP/CodeClassDeclarationWriter.cs b/src/Kiota.Builder/Writers/HTTP/CodeClassDeclarationWriter.cs index 09b1bca791..08b84be62a 100644 --- a/src/Kiota.Builder/Writers/HTTP/CodeClassDeclarationWriter.cs +++ b/src/Kiota.Builder/Writers/HTTP/CodeClassDeclarationWriter.cs @@ -1,8 +1,9 @@ using System; +using System.Collections.Generic; using System.Linq; -using System.Web; using Kiota.Builder.CodeDOM; using Kiota.Builder.Extensions; +using Microsoft.Kiota.Abstractions; namespace Kiota.Builder.Writers.http; public class CodeClassDeclarationWriter(HttpConventionService conventionService) : CodeProprietableBlockDeclarationWriter(conventionService) @@ -12,107 +13,160 @@ protected override void WriteTypeDeclaration(ClassDeclaration codeElement, Langu ArgumentNullException.ThrowIfNull(codeElement); ArgumentNullException.ThrowIfNull(writer); - if (codeElement.Parent is CodeClass requestBuilderClass && requestBuilderClass.IsOfKind(CodeClassKind.RequestBuilder)) + if (codeElement.Parent is CodeClass requestBuilderClass && requestBuilderClass.IsOfKind(CodeClassKind.RequestBuilder) && GetUrlTemplateProperty(requestBuilderClass) is CodeProperty urlTemplateProperty) { // Write short description conventions.WriteShortDescription(requestBuilderClass, writer); writer.WriteLine(); + // Retrieve all query parameters + var queryParameters = GetAllQueryParameters(requestBuilderClass); + + // Retrieve all path parameters + var pathParameters = GetPathParameters(requestBuilderClass); + + var baseUrl = GetBaseUrl(requestBuilderClass); + // Write the baseUrl variable - WriteBaseUrl(requestBuilderClass, writer); + WriteBaseUrl(baseUrl, writer); // Extract and write the URL template - WriteUrlTemplate(requestBuilderClass, writer); + WriteUrlTemplate(urlTemplateProperty, writer); // Write path parameters - WritePathParameters(requestBuilderClass, writer); + WritePathParameters(pathParameters, writer); // Write all query parameter variables - WriteQueryParameters(requestBuilderClass, writer); + WriteQueryParameters(queryParameters, writer); // Write all HTTP methods GET, POST, PUT, DELETE e.t.c - WriteHttpMethods(requestBuilderClass, writer); + WriteHttpMethods(requestBuilderClass, writer, queryParameters, pathParameters, urlTemplateProperty, baseUrl); } } + /// + /// Retrieves all the query parameters for the given request builder class. + /// + /// The request builder class containing the query parameters. + /// A list of all query parameters. + private static List GetAllQueryParameters(CodeClass requestBuilderClass) + { + var queryParameters = new List(); + + // Retrieve all the query parameter classes + var queryParameterClasses = requestBuilderClass + .GetChildElements(true) + .OfType() + .Where(element => element.IsOfKind(CodeClassKind.QueryParameters)) + .ToList(); + + // Collect all query parameter properties into the aggregated list + queryParameterClasses?.ForEach(paramCodeClass => + { + var queryParams = paramCodeClass + .Properties + .Where(property => property.IsOfKind(CodePropertyKind.QueryParameter)) + .ToList(); + + queryParameters.AddRange(queryParams); + }); + + return queryParameters; + } + + /// + /// Retrieves all the path parameters for the given request builder class. + /// + /// The request builder class containing the path parameters. + /// A list of all path parameters, or an empty list if none are found. + private static List GetPathParameters(CodeClass requestBuilderClass) + { + // Retrieve all the path variables except the generic path parameter named "pathParameters" + var pathParameters = requestBuilderClass + .GetChildElements(true) + .OfType() + .Where(property => property.IsOfKind(CodePropertyKind.PathParameters) && !property.Name.Equals("pathParameters", StringComparison.OrdinalIgnoreCase)) + .ToList(); + + return pathParameters ?? []; + } + /// /// Writes the base URL for the given request builder class to the writer. /// /// The request builder class containing the base URL property. /// The language writer to write the base URL to. - private static void WriteBaseUrl(CodeClass requestBuilderClass, LanguageWriter writer) + private static void WriteBaseUrl(string? baseUrl, LanguageWriter writer) { - // Retrieve the base URL property from the request builder class - var baseUrl = requestBuilderClass.Properties - .FirstOrDefault(property => property.Name.Equals("BaseUrl", StringComparison.OrdinalIgnoreCase))?.DefaultValue; - // Write the base URL variable to the writer writer.WriteLine($"# baseUrl"); writer.WriteLine($"@baseUrl = {baseUrl}"); writer.WriteLine(); } - private static void WriteUrlTemplate(CodeClass requestBuilderClass, LanguageWriter writer) + /// + /// Retrieves the base URL for the given request builder class. + /// + /// The request builder class containing the base URL property. + /// The base URL as a string, or null if not found. + private static string? GetBaseUrl(CodeClass requestBuilderClass) { - var urlTemplateProperty = requestBuilderClass + // Retrieve the base URL property from the request builder class + return requestBuilderClass.Properties + .FirstOrDefault(property => property.Name.Equals("BaseUrl", StringComparison.OrdinalIgnoreCase))?.DefaultValue; + } + + /// + /// Retrieves the URL template property for the given request builder class. + /// + /// The request builder class containing the URL template property. + /// The URL template property, or null if not found. + private static CodeProperty? GetUrlTemplateProperty(CodeClass requestBuilderClass) + { + // Retrieve the URL template property from the request builder class + return requestBuilderClass .GetChildElements(true) .OfType() .FirstOrDefault(property => property.IsOfKind(CodePropertyKind.UrlTemplate)); + } + + /// + /// Writes the URL template for the given URL template property to the writer. + /// + /// The URL template property containing the URL template. + /// The language writer to write the URL template to. + private static void WriteUrlTemplate(CodeProperty urlTemplateProperty, LanguageWriter writer) + { + // Write the URL template documentation as a comment + writer.WriteLine($"# {urlTemplateProperty.Documentation?.DescriptionTemplate}"); - var urlTemplate = urlTemplateProperty?.DefaultValue; - writer.WriteLine($"# {urlTemplateProperty?.Documentation?.DescriptionTemplate}"); - writer.WriteLine($"# {urlTemplate}"); + // Write the URL template value + writer.WriteLine($"# {urlTemplateProperty.DefaultValue}"); + + // Write an empty line for separation writer.WriteLine(); } /// /// Writes the path parameters for the given request builder class to the writer. /// - /// The request builder class containing the path parameters. + /// The list of path parameters to write. /// The language writer to write the path parameters to. - private static void WritePathParameters(CodeClass requestBuilderClass, LanguageWriter writer) + private static void WritePathParameters(List pathParameters, LanguageWriter writer) { - // Retrieve all the path variables except the generic path parameter named "pathParameters" - var pathParameters = requestBuilderClass - .GetChildElements(true) - .OfType() - .Where(property => property.IsOfKind(CodePropertyKind.PathParameters) && !property.Name.Equals("pathParameters", StringComparison.OrdinalIgnoreCase)) - .ToList(); - // Write each path parameter property - pathParameters?.ForEach(prop => - { - WriteHttpParameterProperty(prop, writer); - }); + pathParameters?.ForEach(prop => WriteHttpParameterProperty(prop, writer)); } /// /// Writes the query parameters for the given request builder class to the writer. /// - /// The request builder class containing the query parameters. + /// The list of query parameters to write. /// The language writer to write the query parameters to. - private static void WriteQueryParameters(CodeClass requestBuilderClass, LanguageWriter writer) + private static void WriteQueryParameters(List queryParameters, LanguageWriter writer) { - // Retrieve all the query parameter classes - var queryParameterClasses = requestBuilderClass - .GetChildElements(true) - .OfType() - .Where(element => element.IsOfKind(CodeClassKind.QueryParameters)) - .ToList(); - // Write each query parameter property - queryParameterClasses?.ForEach(paramCodeClass => - { - var queryParams = paramCodeClass - .Properties - .Where(property => property.IsOfKind(CodePropertyKind.QueryParameter)) - .ToList(); - - queryParams.ForEach(prop => - { - WriteHttpParameterProperty(prop, writer); - }); - }); + queryParameters.ForEach(prop => WriteHttpParameterProperty(prop, writer)); } /// @@ -140,7 +194,7 @@ private static void WriteHttpParameterProperty(CodeProperty property, LanguageWr /// /// The request builder class containing the HTTP methods. /// The language writer to write the HTTP methods to. - private static void WriteHttpMethods(CodeClass requestBuilderClass, LanguageWriter writer) + private static void WriteHttpMethods(CodeClass requestBuilderClass, LanguageWriter writer, List queryParameters, List pathParameters, CodeProperty urlTemplateProperty, string? baseUrl) { // Retrieve all the HTTP methods of kind RequestExecutor var httpMethods = requestBuilderClass @@ -155,8 +209,11 @@ private static void WriteHttpMethods(CodeClass requestBuilderClass, LanguageWrit // Write the method documentation as a comment writer.WriteLine($"# {method.Documentation.DescriptionTemplate}"); - // Write the method name and URL template - writer.WriteLine($"{method.Name.ToUpperInvariant()} {GetUrlTemplate(requestBuilderClass)}"); + // Build the actual URL string and replace all required fields(path and query) with placeholder variables + var url = BuildUrlStringFromTemplate(urlTemplateProperty.DefaultValue, queryParameters, pathParameters, baseUrl); + + // Write the http operation e.g GET, POST, PATCH, e.t.c + writer.WriteLine($"{method.Name.ToUpperInvariant()} {url} HTTP/1.1"); // Write the request body if present WriteRequestBody(method, writer); @@ -196,10 +253,11 @@ private static void WriteRequestBody(CodeMethod method, LanguageWriter writer) var requestBody = method.Parameters.FirstOrDefault(param => param.IsOfKind(CodeParameterKind.RequestBody)); if (requestBody is null) return; - // Empty line before content type - writer.WriteLine(); writer.WriteLine($"Content-Type: {method.RequestBodyContentType}"); + // Empty line before body content + writer.WriteLine(); + // Loop through the properties of the request body and write a JSON object if (requestBody.Type is CodeType ct && ct.TypeDefinition is CodeClass requestBodyClass) { @@ -273,4 +331,33 @@ private static string GetDefaultValueForProperty(CodeProperty codeProperty) _ => "null" }; } + + private static string BuildUrlStringFromTemplate(string urlTemplateString, List queryParameters, List pathParameters, string? baseUrl) + { + // Use the provided baseUrl or default to "http://localhost/" + baseUrl ??= "http://localhost/"; + + // unquote the urlTemplate string and replace the {+baseurl} with the actual base url string + urlTemplateString = urlTemplateString.Trim('"').Replace("{+baseurl}", baseUrl, StringComparison.InvariantCultureIgnoreCase); + + // Build RequestInformation using the URL + var requestInformation = new RequestInformation() + { + UrlTemplate = urlTemplateString + }; + + queryParameters?.ForEach(param => + { + // Check if its a required parameter then add it + requestInformation.QueryParameters.Add(param.WireName, $"{{{param.Name.ToFirstCharacterLowerCase()}}}"); + }); + + pathParameters?.ForEach(param => + { + requestInformation.PathParameters.Add(param.WireName, $"{{{param.Name.ToFirstCharacterLowerCase()}}}"); + }); + + // Erase baseUrl and use the placeholder variable {baseUrl} already defined in the snippet + return requestInformation.URI.ToString().Replace(baseUrl, "{baseUrl}", StringComparison.InvariantCultureIgnoreCase); + } }