Skip to content

[Repo Assist] feat: add CancellationToken support to OpenApiClientProvider generated methods (closes #212)#336

Merged
sergey-tihon merged 13 commits intomasterfrom
repo-assist/feat-cancellationtoken-issue-212-2026-03-23-448c1cde90ec7d95
Mar 25, 2026
Merged

[Repo Assist] feat: add CancellationToken support to OpenApiClientProvider generated methods (closes #212)#336
sergey-tihon merged 13 commits intomasterfrom
repo-assist/feat-cancellationtoken-issue-212-2026-03-23-448c1cde90ec7d95

Conversation

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented Mar 23, 2026

Summary

Adds optional CancellationToken support to all methods generated by OpenApiClientProvider (v3), closing #212.

Before: Generated methods had no way to cancel in-flight HTTP requests.
After: Every generated method has an optional cancellationToken parameter (defaulting to CancellationToken.None). Existing code continues to work unchanged.

// Existing code — still works, uses CancellationToken.None
let! result = client.GetPet(1L)

// New — pass your own token
use cts = new CancellationTokenSource(TimeSpan.FromSeconds(5.0))
let! result = client.GetPet(1L, cts.Token)

Design Decisions

  1. Single method per operation, no overloads — The ProvidedTypes SDK does not support method overloads (GetMethodImpl throws at ProvidedTypes.fs:1632). Each operation compiles to exactly one method with an optional CT parameter appended last.

  2. null default for struct parameterProvidedParameter(name, typeof<CancellationToken>, false, null) emits valid IL. The runtime interprets null as default(CancellationToken) = CancellationToken.None. SetConstant only supports primitives/strings/null, so a real struct default is not possible.

  3. CT positioned after all required + optional params — Preserves backward compatibility with existing positional argument calls in user code and tests.

  4. UniqueNameGenerator with occupiedNames — Avoids name collisions if an API already has a parameter named cancellationToken.

  5. PreferAsync=true works automatically — The implementation always uses task { } internally and passes CT to HttpClient.SendAsync. When asAsync=true, results are wrapped via Async.AwaitTask, which cooperatively propagates cancellation.

Changes

Core (4 files)

File Change
src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs Single method per operation with optional CT param; CT extracted by index in quotation; passed to CallAsync
src/SwaggerProvider.Runtime/ProvidedApiClientBase.fs Single CallAsync with 4 args (request, errorCodes, errorDescriptions, cancellationToken); passes CT to HttpClient.SendAsync
src/SwaggerProvider.DesignTime/Utils.fs UniqueNameGenerator accepts optional occupiedNames parameter
build.fsx Explicit dotnet restore before dotnet build --no-restore to fix TFM asset file race

Tests (4 files)

File Change
tests/SwaggerProvider.ProviderTests/v3/Swashbuckle.CancellationToken.Tests.fs New. 6 integration tests: without CT, with CT None, with CTS token, cancelled token, POST with CT, async without CT (PreferAsync=true)
tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs 2 new unit tests for CallAsync with CT; existing tests updated to 4-arg signature
tests/SwaggerProvider.Tests/UtilsTests.fs 4 new unit tests for UniqueNameGenerator occupied names feature
tests/SwaggerProvider.ProviderTests/SwaggerProvider.ProviderTests.fsproj Added CT test file to project

Infrastructure (2 files)

File Change
global.json SDK version 10.0.200 -> 10.0.102
.github/ Repo Assist workflow lock file updates (auto-generated, not feature-related)

Test Results

Full ./build.sh pipeline passes (Status: Ok):

  • CheckFormat (Fantomas)
  • Build (Release)
  • RunUnitTests (225 passed, 2 skipped - pre-existing)
  • StartServer + BuildTests + RunIntegrationTests (all v3 tests pass)
  • NuGet + BuildPackage

Scope

Only v3 OpenApiClientProvider is modified. The v2 SwaggerClientProvider is left unchanged and could be updated in a follow-up.

…d methods (closes #212)

- Add CallAsync overload with CancellationToken to ProvidedApiClientBase
- Thread CancellationToken from generated methods through to HttpClient.SendAsync
- Each generated method gains an optional cancellationToken parameter (defaults to CancellationToken.None)
- Backward-compatible: existing call sites without CT continue to work unchanged
- Add unit tests: success with CancellationToken.None, cancellation propagation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergey-tihon
Copy link
Member

@copilot finish or and fix the build

Copy link
Contributor

Copilot AI commented Mar 23, 2026

@sergey-tihon I've opened a new pull request, #337, to work on those changes. Once the pull request is ready, I'll request review from you.

…overloads (#337)

* Initial plan

* Fix: revert global.json and address CancellationToken build failures

Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/1861c3cb-6a0a-438a-aa31-f65b8c809f88

* fix: use method overloading for CancellationToken support instead of optional struct parameter

Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/1861c3cb-6a0a-438a-aa31-f65b8c809f88

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds CancellationToken plumbing to the OpenAPI v3 generated client methods by extending the runtime ProvidedApiClientBase.CallAsync API and updating the v3 operation compiler, with tests added around the new runtime overload.

Changes:

  • Add CallAsync overload that accepts a CancellationToken and forwards it to HttpClient.SendAsync.
  • Generate v3 client method overloads that accept a CancellationToken and pass it through to CallAsync.
  • Extend runtime tests to validate success and already-cancelled token behavior; update stub handler to honor cancellation.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs Adds coverage for CallAsync cancellation forwarding and updates stub handler to throw on cancellation.
src/SwaggerProvider.Runtime/ProvidedApiClientBase.fs Introduces CallAsync overload with CancellationToken and delegates existing overload to CancellationToken.None.
src/SwaggerProvider.DesignTime/v3/OperationCompiler.fs Generates additional operation method overloads and wires CT through to runtime CallAsync.
global.json Changes pinned .NET SDK version used by the repo.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@sergey-tihon
Copy link
Member

@copilot add typeprovider tests that actually call TP generated methods with cancellation token

Copy link
Contributor

Copilot AI commented Mar 23, 2026

@sergey-tihon I've opened a new pull request, #338, to work on those changes. Once the pull request is ready, I'll request review from you.

@sergey-tihon sergey-tihon marked this pull request as ready for review March 23, 2026 22:09
Copy link
Contributor

Copilot AI commented Mar 23, 2026

@sergey-tihon I've opened a new pull request, #339, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Contributor

Copilot AI commented Mar 23, 2026

@sergey-tihon I've opened a new pull request, #340, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Contributor

Copilot AI commented Mar 23, 2026

@sergey-tihon I've opened a new pull request, #341, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 2 commits March 25, 2026 08:06
…ation (#339)

* Initial plan

* fix: generate unique CT parameter name to avoid collision with OpenAPI params named 'cancellationToken'

Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/7d588ec7-c4df-4a6c-89f8-9c13c2472d29

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
…rationCompiler (#341)

* Initial plan

* fix: insert CT between required and optional params; generate unique CT name

Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/b0c519de-0186-40ca-8174-42ed67a5316a

* fix: add explicit restore + --no-restore to BuildTests to fix NETSDK1005

Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
Agent-Logs-Url: https://github.com/fsprojects/SwaggerProvider/sessions/565d6633-576d-4587-b924-a29b0ea53c2c

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com>
@github-actions
Copy link
Contributor Author

github-actions bot commented Mar 25, 2026

Repo Assist completed successfully!

Replace hand-coded recursive findUniqueName function with the existing
UniqueNameGenerator utility (already used in DefinitionCompiler and
for method name deduplication in OperationCompiler).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Contributor Author

Commit pushed: 37822d7

Generated by Repo Assist

@github-actions
Copy link
Contributor Author

github-actions bot commented Mar 25, 2026

Repo Assist completed successfully!

…structor

Allows callers to pre-seed the generator with names that are already taken,
so MakeUnique will never return any of those names without a numeric suffix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Contributor Author

Commit pushed: d8769f8

Generated by Repo Assist

@github-actions
Copy link
Contributor Author

github-actions bot commented Mar 25, 2026

Repo Assist completed successfully!

…tional CancellationToken

- Remove no-CT CallAsync overload from ProvidedApiClientBase; keep only the
  version with explicit CancellationToken (quotation code always supplies it)
- Remove double-compilation in OperationCompiler: one method per operation with
  optional cancellationToken (null default = default(CancellationToken).None)
- Update RuntimeHelpersTests to pass CancellationToken.None explicitly

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Contributor Author

Commit pushed: 2a976ee

Generated by Repo Assist

- Use List.map instead of List.collect since compileOperation returns a single method
- Clean up comments in OperationCompiler
- Add test for calling generated method without CancellationToken (default token)
- Add test for async (PreferAsync=true) generated method without CancellationToken
…eamAsync via RuntimeHelpers

Add readContentAsString and readContentAsStream wrappers to RuntimeHelpers
with #if NET5_0_OR_GREATER guards, enabling CancellationToken propagation
in generated quotation code that must compile against netstandard2.0.
Also add explicit CancellationToken integration tests and conditional
CT support in ProvidedApiClientBase error path.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@sergey-tihon sergey-tihon merged commit 168908d into master Mar 25, 2026
2 checks passed
@sergey-tihon sergey-tihon deleted the repo-assist/feat-cancellationtoken-issue-212-2026-03-23-448c1cde90ec7d95 branch March 25, 2026 19:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants