From 6a7e01f2952c228dc43fff35bb465f5014362d57 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 22:12:00 +0000 Subject: [PATCH 1/3] improve: enrich XML documentation for generated methods with param descriptions and remarks - Build structured XML doc (, , ) instead of plain text - Add entries for OpenAPI parameters that have descriptions - Add for operation.Description when it differs from Summary - Apply same improvement to both v2 (SwaggerClientProvider) and v3 (OpenApiClientProvider) - XML-escape description text to prevent malformed doc comments - Resolves the TODO comment that has been in the code since the initial implementation 241 unit tests pass; format check passes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../v2/OperationCompiler.fs | 28 ++++++++++++- .../v3/OperationCompiler.fs | 40 +++++++++++++++++-- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/SwaggerProvider.DesignTime/v2/OperationCompiler.fs b/src/SwaggerProvider.DesignTime/v2/OperationCompiler.fs index 5e266c9..af18f28 100644 --- a/src/SwaggerProvider.DesignTime/v2/OperationCompiler.fs +++ b/src/SwaggerProvider.DesignTime/v2/OperationCompiler.fs @@ -252,8 +252,32 @@ type OperationCompiler(schema: SwaggerObject, defCompiler: DefinitionCompiler, i | true, None -> (awaitTask responseUnit).Raw ) - if not <| String.IsNullOrEmpty(op.Summary) then - m.AddXmlDoc(op.Summary) // TODO: Use description of parameters in docs + let xmlDoc = + let esc(s: string) = + s.Replace("&", "&").Replace("<", "<").Replace(">", ">") + + let summaryPart = + if String.IsNullOrEmpty op.Summary then + "" + else + $"{esc op.Summary}" + + let remarksPart = + if String.IsNullOrEmpty op.Description || op.Description = op.Summary then + "" + else + $"{esc op.Description}" + + let paramParts = + [ for p in op.Parameters do + if not(String.IsNullOrWhiteSpace p.Description) then + yield $"{esc p.Description}" ] + |> String.concat "" + + summaryPart + remarksPart + paramParts + + if not(String.IsNullOrEmpty xmlDoc) then + m.AddXmlDoc xmlDoc if op.Deprecated then m.AddObsoleteAttribute("Operation is deprecated", false) diff --git a/src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs b/src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs index 1782213..ab6fe09 100644 --- a/src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs +++ b/src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs @@ -96,7 +96,7 @@ type OperationCompiler(schema: OpenApiDocument, defCompiler: DefinitionCompiler, let (|NoMediaType|_|)(content: IDictionary) = if isNull content || content.Count = 0 then Some() else None - let payloadMime, parameters, ctArgIndex = + let payloadTy, payloadMime, parameters, ctArgIndex = /// handles de-duplicating Swagger parameter names if the same parameter name /// appears in multiple locations in a given operation definition. let uniqueParamName usedNames (param: IOpenApiParameter) = @@ -195,7 +195,7 @@ type OperationCompiler(schema: OpenApiDocument, defCompiler: DefinitionCompiler, ctArgIndex, requiredProvidedParams @ optionalProvidedParams @ [ ctParam ] - payloadTy.ToMediaType(), parameters, ctArgIndex + payloadTy, payloadTy.ToMediaType(), parameters, ctArgIndex // find the inner type value let retMimeAndTy = @@ -494,8 +494,40 @@ type OperationCompiler(schema: OpenApiDocument, defCompiler: DefinitionCompiler, | _ -> Expr.Coerce(<@ RuntimeHelpers.asyncCast t %(awaitTask responseObj) @>, overallReturnType) ) - if not <| String.IsNullOrEmpty(operation.Summary) then - m.AddXmlDoc(operation.Summary) // TODO: Use description of parameters in docs + let xmlDoc = + let esc(s: string) = + s.Replace("&", "&").Replace("<", "<").Replace(">", ">") + + let summaryPart = + if String.IsNullOrEmpty operation.Summary then + "" + else + $"{esc operation.Summary}" + + let remarksPart = + if + String.IsNullOrEmpty operation.Description + || operation.Description = operation.Summary + then + "" + else + $"{esc operation.Description}" + + let paramParts = + [ for p in openApiParameters do + if not(String.IsNullOrWhiteSpace p.Description) then + yield $"{esc p.Description}" + if + not(isNull operation.RequestBody) + && not(String.IsNullOrWhiteSpace operation.RequestBody.Description) + then + yield $"{esc operation.RequestBody.Description}" ] + |> String.concat "" + + summaryPart + remarksPart + paramParts + + if not(String.IsNullOrEmpty xmlDoc) then + m.AddXmlDoc xmlDoc if operation.Deprecated then m.AddObsoleteAttribute("Operation is deprecated", false) From 2a1ccfdbf9f0e9c45abb184ad0a7661710329aa9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 30 Mar 2026 22:12:03 +0000 Subject: [PATCH 2/3] ci: trigger checks From f7feb4cf09a782c1ac9b42e0228dd7f86b099f2c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 06:42:26 +0000 Subject: [PATCH 3/3] refactor: extract shared XML doc generation to Utils.fs Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/1ebc143c-89b5-4035-8920-2271a97cde2b Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com> --- src/SwaggerProvider.DesignTime/Utils.fs | 29 ++++++++++++++++ .../v2/OperationCompiler.fs | 24 ++----------- .../v3/OperationCompiler.fs | 34 +++---------------- 3 files changed, 37 insertions(+), 50 deletions(-) diff --git a/src/SwaggerProvider.DesignTime/Utils.fs b/src/SwaggerProvider.DesignTime/Utils.fs index a5978bd..ff3890c 100644 --- a/src/SwaggerProvider.DesignTime/Utils.fs +++ b/src/SwaggerProvider.DesignTime/Utils.fs @@ -332,6 +332,35 @@ module SchemaReader = resolvedPath } +module XmlDoc = + open System + + let private escapeXml(s: string) = + s.Replace("&", "&").Replace("<", "<").Replace(">", ">") + + /// Builds a structured XML doc string from summary, description, and parameter descriptions. + /// paramDescriptions is a sequence of (camelCaseName, description) pairs. + let buildXmlDoc (summary: string) (description: string) (paramDescriptions: (string * string) seq) = + let summaryPart = + if String.IsNullOrEmpty summary then + "" + else + $"{escapeXml summary}" + + let remarksPart = + if String.IsNullOrEmpty description || description = summary then + "" + else + $"{escapeXml description}" + + let paramParts = + [ for name, desc in paramDescriptions do + if not(String.IsNullOrWhiteSpace desc) then + yield $"{escapeXml desc}" ] + |> String.concat "" + + summaryPart + remarksPart + paramParts + type UniqueNameGenerator(?occupiedNames: string seq) = let hash = System.Collections.Generic.HashSet<_>() diff --git a/src/SwaggerProvider.DesignTime/v2/OperationCompiler.fs b/src/SwaggerProvider.DesignTime/v2/OperationCompiler.fs index af18f28..6d13c1f 100644 --- a/src/SwaggerProvider.DesignTime/v2/OperationCompiler.fs +++ b/src/SwaggerProvider.DesignTime/v2/OperationCompiler.fs @@ -253,28 +253,10 @@ type OperationCompiler(schema: SwaggerObject, defCompiler: DefinitionCompiler, i ) let xmlDoc = - let esc(s: string) = - s.Replace("&", "&").Replace("<", "<").Replace(">", ">") + let paramDescriptions = + [ for p in op.Parameters -> niceCamelName p.Name, p.Description ] - let summaryPart = - if String.IsNullOrEmpty op.Summary then - "" - else - $"{esc op.Summary}" - - let remarksPart = - if String.IsNullOrEmpty op.Description || op.Description = op.Summary then - "" - else - $"{esc op.Description}" - - let paramParts = - [ for p in op.Parameters do - if not(String.IsNullOrWhiteSpace p.Description) then - yield $"{esc p.Description}" ] - |> String.concat "" - - summaryPart + remarksPart + paramParts + XmlDoc.buildXmlDoc op.Summary op.Description paramDescriptions if not(String.IsNullOrEmpty xmlDoc) then m.AddXmlDoc xmlDoc diff --git a/src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs b/src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs index ab6fe09..e075687 100644 --- a/src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs +++ b/src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs @@ -495,36 +495,12 @@ type OperationCompiler(schema: OpenApiDocument, defCompiler: DefinitionCompiler, ) let xmlDoc = - let esc(s: string) = - s.Replace("&", "&").Replace("<", "<").Replace(">", ">") + let paramDescriptions = + [ for p in openApiParameters -> niceCamelName p.Name, p.Description + if not(isNull operation.RequestBody) then + yield niceCamelName(payloadTy.ToString()), operation.RequestBody.Description ] - let summaryPart = - if String.IsNullOrEmpty operation.Summary then - "" - else - $"{esc operation.Summary}" - - let remarksPart = - if - String.IsNullOrEmpty operation.Description - || operation.Description = operation.Summary - then - "" - else - $"{esc operation.Description}" - - let paramParts = - [ for p in openApiParameters do - if not(String.IsNullOrWhiteSpace p.Description) then - yield $"{esc p.Description}" - if - not(isNull operation.RequestBody) - && not(String.IsNullOrWhiteSpace operation.RequestBody.Description) - then - yield $"{esc operation.RequestBody.Description}" ] - |> String.concat "" - - summaryPart + remarksPart + paramParts + XmlDoc.buildXmlDoc operation.Summary operation.Description paramDescriptions if not(String.IsNullOrEmpty xmlDoc) then m.AddXmlDoc xmlDoc