Skip to content
Draft
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
33 changes: 13 additions & 20 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) (includeCancellationToken: bool) =
let compileOperation (providedMethodName: string) (apiCall: ApiCall) =
let path, pathItem, opTy = apiCall
let operation = pathItem.Operations[opTy]

Expand Down Expand Up @@ -179,13 +179,14 @@ type OperationCompiler(schema: OpenApiDocument, defCompiler: DefinitionCompiler,
|> List.rev

let parameters =
if includeCancellationToken then
let ctParam =
ProvidedParameter("cancellationToken", typeof<Threading.CancellationToken>)
// Passing null as optionalValue encodes default(CancellationToken) in CLI metadata,
// which is the standard way to represent `ct = default` for struct parameters
// (equivalent to CancellationToken.None). A boxed struct value cannot be used
// here because CancellationToken is not a valid custom attribute argument type.
let ctParam =
ProvidedParameter("cancellationToken", typeof<Threading.CancellationToken>, optionalValue = (null: obj))

providedParameters @ [ ctParam ]
else
providedParameters
providedParameters @ [ ctParam ]

payloadTy.ToMediaType(), parameters

Expand Down Expand Up @@ -273,18 +274,15 @@ type OperationCompiler(schema: OpenApiDocument, defCompiler: DefinitionCompiler,
// Locates parameters matching the arguments
let mutable payloadExp = None

// When the CancellationToken overload is generated, CancellationToken is always appended last.
// CancellationToken is always appended last as an optional parameter.
// Extract it by position to avoid name-collision issues and invalid Expr.Coerce
// on a struct type (which generates an invalid castclass IL instruction).
let apiArgs, ct =
let allArgs = List.tail args // skip `this`

if includeCancellationToken then
match List.rev allArgs with
| ctArg :: revApiArgs -> List.rev revApiArgs, Expr.Cast<Threading.CancellationToken>(ctArg)
| [] -> failwith "Expected CancellationToken argument but argument list was empty"
else
allArgs, <@ Threading.CancellationToken.None @>
match List.rev allArgs with
| ctArg :: revApiArgs -> List.rev revApiArgs, Expr.Cast<Threading.CancellationToken>(ctArg)
| [] -> failwith "Expected CancellationToken argument but argument list was empty"

let parameters =
apiArgs
Expand Down Expand Up @@ -622,10 +620,5 @@ type OperationCompiler(schema: OpenApiDocument, defCompiler: DefinitionCompiler,

let name = OperationCompiler.GetMethodNameCandidate op skipLength ignoreOperationId
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 ])
[ compileOperation uniqueName op ])
|> ty.AddMembers)
Loading