Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "10.0.200",
"version": "10.0.102",
"rollForward": "latestMinor"
}
}
24 changes: 18 additions & 6 deletions src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ type PayloadType =

/// Object for compiling operations.
type OperationCompiler(schema: OpenApiDocument, defCompiler: DefinitionCompiler, ignoreControllerPrefix, ignoreOperationId, asAsync: bool) =
let compileOperation (providedMethodName: string) (apiCall: ApiCall) =
let compileOperation (providedMethodName: string) (apiCall: ApiCall) (includeCancellationToken: bool) =
let path, pathItem, opTy = apiCall
let operation = pathItem.Operations[opTy]

Expand Down Expand Up @@ -178,10 +178,16 @@ type OperationCompiler(schema: OpenApiDocument, defCompiler: DefinitionCompiler,
// reverse it again so that all required properties come first
|> List.rev

let ctParam =
ProvidedParameter("cancellationToken", typeof<Threading.CancellationToken>, optionalValue = box Threading.CancellationToken.None)
let parameters =
if includeCancellationToken then
let ctParam =
ProvidedParameter("cancellationToken", typeof<Threading.CancellationToken>)
Comment on lines +183 to +184
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added CancellationToken parameter is always named "cancellationToken" without checking whether an OpenAPI parameter/body field already normalizes to the same name. That can (1) create duplicate parameter names when includeCancellationToken=true, and (2) when includeCancellationToken=false, cause a real OpenAPI parameter named "cancellationToken" to be misinterpreted as the special token in invokeCode and be omitted from the request. Make the CancellationToken parameter name unique against the existing providedParameters (or adjust the invokeCode matching so it only treats the argument as a CT when it doesn’t correspond to an OpenAPI parameter).

Suggested change
let ctParam =
ProvidedParameter("cancellationToken", typeof<Threading.CancellationToken>)
let existingNames =
providedParameters
|> List.map (fun (p: ProvidedParameter) -> p.Name)
|> Set.ofList
let rec uniqueName baseName idx =
let candidate =
if idx = 0 then baseName
else baseName + string idx
if existingNames.Contains candidate then
uniqueName baseName (idx + 1)
else
candidate
let ctName = uniqueName "cancellationToken" 0
let ctParam =
ProvidedParameter(ctName, typeof<Threading.CancellationToken>)

Copilot uses AI. Check for mistakes.

payloadTy.ToMediaType(), providedParameters @ [ ctParam ]
providedParameters @ [ ctParam ]
else
providedParameters

payloadTy.ToMediaType(), parameters

// find the inner type value
let retMimeAndTy =
Expand Down Expand Up @@ -608,13 +614,19 @@ type OperationCompiler(schema: OpenApiDocument, defCompiler: DefinitionCompiler,
let methodNameScope = UniqueNameGenerator()

operations
|> List.map(fun op ->
|> List.collect(fun op ->
let skipLength =
if String.IsNullOrEmpty clientName then
0
else
clientName.Length + 1

let name = OperationCompiler.GetMethodNameCandidate op skipLength ignoreOperationId
compileOperation (methodNameScope.MakeUnique name) op)
let uniqueName = methodNameScope.MakeUnique name
// Generate two overloads: one without CancellationToken (backward compatible)
// and one with an explicit CancellationToken parameter.
// We cannot use an optional struct parameter with a default value because
// struct values (e.g., CancellationToken.None) cannot be stored in DefaultParameterValue
// custom attributes.
[ compileOperation uniqueName op false; compileOperation uniqueName op true ])
Comment on lines 616 to +631
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generating overloads by calling compileOperation twice re-runs schema/type compilation (defCompiler.CompileTy and ReserveUniqueName) for the same operation. For inline/request/response schemas this can produce different provided types between the two overloads (e.g., Response vs Response2), making the overloads incompatible and potentially breaking callers. Refactor so the operation’s parameter/return type compilation happens once and then create the 2 ProvidedMethod overloads from the shared compiled metadata (only appending the CancellationToken parameter for the second overload).

Copilot uses AI. Check for mistakes.
|> ty.AddMembers)
Loading