Skip to content

feat: add write conflict resolution options#136

Merged
evansims merged 10 commits intomainfrom
feat/write-conflict-options
Oct 21, 2025
Merged

feat: add write conflict resolution options#136
evansims merged 10 commits intomainfrom
feat/write-conflict-options

Conversation

@evansims
Copy link
Copy Markdown
Contributor

@evansims evansims commented Oct 16, 2025

Description

Implements support for write conflict resolution options introduced in OpenFGA v1.10.0, allowing users to control behavior for duplicate writes and missing deletes.

  • Added OnDuplicateWrites enum: Error (default) | Ignore - controls behavior when writing a tuple that already exists
  • Added OnMissingDeletes enum: Error (default) | Ignore - controls behavior when deleting a non-existent tuple
  • Added ConflictOptions class with properties for both enum types
  • Exposed via ClientWriteOptions.Conflict property
  • Default values (Error) are explicitly sent to the API when not specified by the user

Usage Example:

var options = new ClientWriteOptions {
  Conflict = new ConflictOptions {
      OnDuplicateWrites = OnDuplicateWrites.Ignore,
      OnMissingDeletes = OnMissingDeletes.Ignore
  }
};

await fgaClient.Write(body, options);

References

Generated from → openfga/sdk-generator#644
Issue → #121

Review Checklist

  • I have clicked on "allow edits by maintainers".
  • I have added documentation for new/changed functionality in this PR or in a PR to openfga.dev [Provide a link to any relevant PRs in the references section above]
  • The correct base branch is being used, if not main
  • I have added tests to validate that the change in functionality is working as expected

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced write conflict resolution options allowing control over duplicate writes and missing deletes with Error or Ignore settings.
    • API methods now accept optional request options parameter (breaking change).
  • Documentation

    • Added guidance for configuring conflict options in write operations.
  • Bug Fixes

    • Fixed header validation error message wording.

@evansims evansims requested review from a team as code owners October 16, 2025 23:15
@evansims evansims added the enhancement New feature or request label Oct 16, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 16, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR introduces write conflict resolution options to the SDK, adding OnDuplicateWrites and OnMissingDeletes enums within ConflictOptions to control write operation behavior. The client implementation maps these options into request bodies. A breaking change adds optional IRequestOptions parameters to all OpenFgaApi methods. Extensive test coverage validates conflict option propagation and header handling across operations.

Changes

Cohort / File(s) Summary
Conflict Options & Model
src/OpenFga.Sdk/Client/Model/ClientWriteOptions.cs
Added OnDuplicateWrites and OnMissingDeletes enums; introduced IConflictOptions interface and ConflictOptions class with properties for duplicate writes and missing deletes behavior; extended IClientWriteOptions and ClientWriteOptions with Conflict and Headers properties.
Client Implementation
src/OpenFga.Sdk/Client/Client.cs
Added private mapping methods MapOnDuplicateWrites() and MapOnMissingDeletes() to convert enums to request body enums; updated Write path to pass conflict mappings into request bodies for writes and deletes; expanded ClientWriteOptions construction to propagate Conflict from options.
Test Coverage
src/OpenFga.Sdk.Test/Client/OpenFgaClientTests.cs
Added comprehensive test cases for conflict resolution (OnDuplicateWrites/OnMissingDeletes with Error/Ignore values, combined scenarios, null behavior); expanded header propagation tests validating per-request header overrides, reserved header protection, and concurrent request isolation across Check, Read, Expand, List, and store operations.
Documentation
README.md
Added documentation section explaining Conflict Options for Write Operations with OnDuplicateWrites and OnMissingDeletes values and configuration example via ClientWriteOptions.
Build Metadata
CHANGELOG.md
Documented addition of ConflictOptions, breaking API changes to OpenFgaApi (optional IRequestOptions parameter on all methods), and interface renaming (ClientRequestOptionsIClientRequestOptions).

Sequence Diagram

sequenceDiagram
    participant User as User Code
    participant Client as Client
    participant Mapper as Mapper Logic
    participant API as WriteRequest Body

    User->>Client: Write with ClientWriteOptions<br/>(Conflict: OnDuplicateWrites.Ignore)
    Client->>Mapper: MapOnDuplicateWrites(Ignore)
    Mapper-->>Client: WriteRequestWrites.OnDuplicateEnum.Ignore
    Client->>Mapper: MapOnMissingDeletes(null)
    Mapper-->>Client: WriteRequestDeletes.OnMissingEnum.Error<br/>(default)
    Client->>API: WriteRequest with<br/>Writes.OnDuplicate=Ignore<br/>Deletes.OnMissing=Error
    API-->>Client: Response
    Client-->>User: Result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

The changes introduce straightforward enum mappings and conflict option propagation with consistent patterns across test cases. While the test file is large with extensive scenarios, the logic density is moderate—primarily validation of option threading through request paths and header precedence. The breaking API surface change (optional IRequestOptions on all methods) is well-scoped but requires verification across method signatures.

Possibly related issues

Possibly related PRs

  • chore: sync generator at api@0ac19aa #120: Introduces identical write conflict controls with OnDuplicate/OnMissing enums and updates Write request models, sharing the same API-level conflict field modifications.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "feat: add write conflict resolution options" directly and clearly describes the primary objective of the changeset. The main feature being introduced is the addition of conflict resolution options for write operations, specifically the OnDuplicateWrites and OnMissingDeletes enums, the ConflictOptions class, and their exposure via ClientWriteOptions.Conflict. The title is concise, specific, and avoids generic terms—it accurately captures the core functionality being added. While the PR also includes other changes such as breaking API modifications and test additions, these are supporting changes to implement the stated feature.
Docstring Coverage ✅ Passed Docstring coverage is 84.21% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a040c45 and 0731319.

📒 Files selected for processing (26)
  • .openapi-generator/FILES (1 hunks)
  • CHANGELOG.md (8 hunks)
  • README.md (2 hunks)
  • src/OpenFga.Sdk.Test/Client/OpenFgaClientTests.cs (5 hunks)
  • src/OpenFga.Sdk/Api/OpenFgaApi.cs (31 hunks)
  • src/OpenFga.Sdk/ApiClient/ApiClient.cs (3 hunks)
  • src/OpenFga.Sdk/Client/Client.cs (19 hunks)
  • src/OpenFga.Sdk/Client/ClientConfiguration.cs (4 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientBatchCheckOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientCheckOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientCreateStoreOptions.cs (1 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientExpandOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientListObjectsOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientListRelationsOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientListStoresOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientListUsersOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientReadAssertionsOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientReadAuthorizaionModelOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientReadAuthorizaionModelsOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientReadChangesOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientReadOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientRequestOptions.cs (1 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientRequestOptsWithStoreId.cs (1 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientWriteAssertionsOptions.cs (2 hunks)
  • src/OpenFga.Sdk/Client/Model/ClientWriteOptions.cs (3 hunks)
  • src/OpenFga.Sdk/Model/RequestOptions.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (22)
src/OpenFga.Sdk/Client/Model/ClientReadOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientExpandOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientListRelationsOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientReadAuthorizaionModelOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientCheckOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Model/RequestOptions.cs (1)
src/OpenFga.Sdk/Client/Client.cs (1)
  • IRequestOptions (74-78)
src/OpenFga.Sdk/Client/Model/ClientListObjectsOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientCreateStoreOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientReadAuthorizaionModelsOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientReadAssertionsOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientWriteAssertionsOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk.Test/Client/OpenFgaClientTests.cs (4)
src/OpenFga.Sdk/Client/Client.cs (2)
  • WriteRequestWrites (239-244)
  • WriteRequestDeletes (246-251)
src/OpenFga.Sdk/ApiClient/Utils.cs (1)
  • Utils (27-63)
src/OpenFga.Sdk/Client/ClientConfiguration.cs (3)
  • ClientConfiguration (31-157)
  • ClientConfiguration (36-45)
  • ClientConfiguration (50-50)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientReadChangesOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientWriteOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/ClientConfiguration.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientListUsersOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientRequestOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Client.cs (7)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (2)
  • ApiClient (30-231)
  • ApiClient (41-67)
src/OpenFga.Sdk/Client/ClientConfiguration.cs (4)
  • ClientConfiguration (31-157)
  • ClientConfiguration (36-45)
  • ClientConfiguration (50-50)
  • ValidateHeaders (126-156)
src/OpenFga.Sdk/Model/RequestOptions.cs (3)
  • RequestOptions (18-35)
  • RequestOptions (22-23)
  • RequestOptions (29-31)
src/OpenFga.Sdk/Model/WriteRequestWrites.cs (2)
  • WriteRequestWrites (70-78)
  • WriteRequestWrites (107-109)
src/OpenFga.Sdk/Model/WriteRequestDeletes.cs (2)
  • WriteRequestDeletes (70-78)
  • WriteRequestDeletes (107-109)
src/OpenFga.Sdk/Api/OpenFgaApi.cs (16)
  • Task (52-74)
  • Task (85-107)
  • Task (117-133)
  • Task (143-164)
  • Task (175-197)
  • Task (207-228)
  • Task (239-261)
  • Task (273-297)
  • Task (308-330)
  • Task (341-363)
  • Task (374-398)
  • Task (409-433)
  • Task (445-472)
  • Task (486-520)
  • Task (531-553)
  • Task (565-590)
src/OpenFga.Sdk/Client/Model/ClientWriteOptions.cs (1)
  • ClientWriteOptions (118-135)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (2)
src/OpenFga.Sdk/Exceptions/ApiException.cs (5)
  • ApiException (22-84)
  • ApiException (26-28)
  • ApiException (34-36)
  • ApiException (45-47)
  • ApiException (50-52)
src/OpenFga.Sdk/Exceptions/ApiAuthenticationError.cs (3)
  • FgaApiAuthenticationError (25-48)
  • FgaApiAuthenticationError (34-38)
  • FgaApiAuthenticationError (40-42)
src/OpenFga.Sdk/Client/Model/ClientListStoresOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Client/Model/ClientBatchCheckOptions.cs (1)
src/OpenFga.Sdk/ApiClient/ApiClient.cs (1)
  • IDictionary (189-228)
src/OpenFga.Sdk/Api/OpenFgaApi.cs (1)
src/OpenFga.Sdk/Client/Client.cs (17)
  • Task (81-114)
  • Task (116-139)
  • Task (141-182)
  • Task (184-215)
  • Task (260-262)
  • Task (267-270)
  • Task (275-276)
  • Task (281-282)
  • Task (291-294)
  • Task (299-302)
  • Task (307-316)
  • Task (321-332)
  • Task (341-344)
  • Task (349-363)
  • Task (368-437)
  • Task (442-444)
  • IRequestOptions (74-78)
🪛 LanguageTool
README.md

[grammar] ~210-~210: There might be a mistake here.
Context: ...### Custom Headers #### Default Headers You can set default headers that will be...

(QB_NEW_EN)

CHANGELOG.md

[grammar] ~6-~6: There might be a mistake here.
Context: ...ionfor headers sent with every request - per-request headers support viaHeaders...

(QB_NEW_EN)


[grammar] ~9-~9: There might be a mistake here.
Context: ...s` class for client-level header support - add header validation to prevent overidi...

(QB_NEW_EN)


[grammar] ~10-~10: Ensure spelling is correct
Context: ...rt - add header validation to prevent overiding of reserved headers - feat: add write c...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~10-~10: There might be a mistake here.
Context: ...to prevent overiding of reserved headers - feat: add write conflict resolution opti...

(QB_NEW_EN)


[grammar] ~14-~14: There might be a mistake here.
Context: ...norefor handling missing tuple deletes - Available inClientWriteOptions.Conflic...

(QB_NEW_EN)


[grammar] ~17-~17: There might be a mistake here.
Context: ...teOptions.Conflict` property [!WARNING] BREAKING CHANGES: - **OpenFgaApi method...

(QB_NEW_EN)


[grammar] ~59-~59: There might be a mistake here.
Context: ...d 2.0, .NET 8.0 and .NET 9.0 [!WARNING] BREAKING CHANGES: - While we have dropp...

(QB_NEW_EN)


[grammar] ~62-~62: There might be a mistake here.
Context: ... can still be used in .NET 6.0 projects. - We have updated the underlying OpenAPI g...

(QB_NEW_EN)


[grammar] ~74-~74: There might be a mistake here.
Context: ... New Enum Value | Affected Enum | | -------------- | -------------------- ...

(QB_NEW_EN)


[grammar] ~75-~75: There might be a mistake here.
Context: ... -------------------- | -------------- | | WRITE | TUPLEOPERATIONWRITE ...

(QB_NEW_EN)


[grammar] ~76-~76: There might be a mistake here.
Context: ... TUPLEOPERATIONWRITE | TupleOperation | | DELETE | TUPLEOPERATIONDELETE ...

(QB_NEW_EN)


[grammar] ~109-~109: There might be a mistake here.
Context: ...lag enable-consistency-params enabled. See the [v1.5.7 release notes](https:/...

(QB_NEW_EN)


[grammar] ~176-~176: There might be a mistake here.
Context: ... - OpenFgaApi now requires storeId as first param when needed - `Configuratio...

(QB_NEW_EN)


[grammar] ~178-~178: There might be a mistake here.
Context: ...he following request interfaces changed: - CheckRequest: the TupleKey field is now of interfa...

(QB_NEW_EN)


[grammar] ~200-~200: There might be a mistake here.
Context: ...): read with no filter (read all tuples) - chore(deps): update dependencies ## v0....

(QB_NEW_EN)

🪛 markdownlint-cli2 (0.18.1)
CHANGELOG.md

194-194: Bare URL used

(MD034, no-bare-urls)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test netcoreapp3.1 on windows-latest
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Test net8.0 on windows-latest
  • GitHub Check: Test net48 on windows-latest
🔇 Additional comments (28)
src/OpenFga.Sdk/Client/Model/ClientReadChangesOptions.cs (1)

14-14: LGTM! Headers property correctly implements per-request header support.

The addition of the Headers property follows the established pattern across client option models. The nullable IDictionary<string, string>? type is appropriate for optional headers, and the /// <inheritdoc /> comment correctly indicates interface implementation. Header validation is properly handled at the API client boundary as shown in the relevant code snippets.

Also applies to: 35-36

src/OpenFga.Sdk/Client/Model/ClientListUsersOptions.cs (1)

15-15: LGTM!

The addition of the Headers property and corresponding using directive correctly implements per-request header support. The nullable dictionary type and inheritdoc comment indicate proper alignment with the interface contract.

Also applies to: 31-33

src/OpenFga.Sdk/Client/Model/ClientWriteAssertionsOptions.cs (1)

14-15: LGTM!

The per-request header support is correctly implemented with the appropriate nullable dictionary type and interface documentation inheritance.

Also applies to: 32-34

src/OpenFga.Sdk/Client/Model/ClientListRelationsOptions.cs (1)

15-15: LGTM!

The addition of per-request header support follows the established pattern and correctly uses the nullable dictionary type with interface documentation.

Also applies to: 36-38

CHANGELOG.md (1)

5-51: LGTM!

The changelog clearly documents the new per-request header support and breaking changes. The inclusion of before/after code examples helps users understand the migration path.

src/OpenFga.Sdk/Client/Model/ClientReadAssertionsOptions.cs (1)

14-15: LGTM!

The per-request header support is correctly implemented following the established pattern across client option classes.

Also applies to: 31-33

src/OpenFga.Sdk/Client/Model/ClientReadOptions.cs (1)

15-15: LGTM!

The addition of per-request header support is correctly implemented with the appropriate type and documentation.

Also applies to: 38-40

.openapi-generator/FILES (1)

240-240: LGTM!

The addition of RequestOptions.cs to the generated files manifest correctly tracks the new model introduced for per-request header support.

src/OpenFga.Sdk/Client/Model/ClientCheckOptions.cs (1)

15-15: LGTM!

The per-request header support is correctly implemented, maintaining consistency with the other client option classes in this PR.

Also applies to: 31-33

src/OpenFga.Sdk/Client/Model/ClientExpandOptions.cs (1)

15-15: LGTM! Clean addition of per-request headers support.

The Headers property addition follows the consistent pattern established across client option classes and aligns with the per-request headers mechanism described in the PR objectives.

Also applies to: 32-33

src/OpenFga.Sdk/Client/Model/ClientBatchCheckOptions.cs (1)

15-15: LGTM! Consistent implementation.

The addition of the Headers property is consistent with other client option classes and properly supports per-request header customization.

Also applies to: 41-42

src/OpenFga.Sdk/Client/Model/ClientListObjectsOptions.cs (1)

15-15: LGTM! Pattern correctly applied.

The Headers property addition follows the established pattern and enables per-request header support for list objects operations.

Also applies to: 32-33

src/OpenFga.Sdk/Client/Model/ClientRequestOptsWithStoreId.cs (1)

17-17: LGTM! Interface-based inheritance improves design.

The change from ClientRequestOptions to IClientRequestOptions follows better design principles by depending on abstractions rather than concrete implementations. This aligns with the broader refactoring to introduce IClientRequestOptions across the SDK.

src/OpenFga.Sdk/Client/Model/ClientReadAuthorizaionModelsOptions.cs (1)

14-15: LGTM with filename typo note.

The Headers property addition is correct and consistent. Note: This file also has the same filename typo as ClientReadAuthorizaionModelOptions.cs (missing 't' in "Authorization").

Also applies to: 35-36

README.md (2)

208-246: Excellent documentation for Custom Headers.

The Custom Headers section is well-structured with clear examples showing both default headers and per-request headers. The code examples are complete and follow C# conventions, making it easy for users to understand the feature.


577-614: Clear documentation for Conflict Options.

The Conflict Options section effectively explains the new feature with:

  • Clear descriptions of both OnDuplicateWrites and OnMissingDeletes enums
  • Explicit listing of possible values and their defaults
  • Complete code example demonstrating usage

This will help users understand how to handle write conflicts appropriately.

src/OpenFga.Sdk/Client/Model/ClientCreateStoreOptions.cs (1)

14-15: LGTM! Clean refactoring and Headers support.

The changes effectively:

  1. Update the interface to inherit from IClientRequestOptions (consistent with the broader refactoring)
  2. Add the Headers property for per-request header support
  3. Simplify the implementation while maintaining proper inheritance

Also applies to: 18-18, 20-22

src/OpenFga.Sdk/Client/Model/ClientReadAuthorizaionModelOptions.cs (1)

14-15: The code changes are correct; the filename typo is pre-existing and out of scope for this PR.

The Headers property addition and using System.Collections.Generic; statement are correct. The filename typo "Authorizaion" is pre-existing in the codebase (also appears in ClientReadAuthorizaionModelsOptions.cs), indicating it originates from the generator template. Fixing it would require updating the generator source, not individual generated files—this should be addressed separately.

src/OpenFga.Sdk/Client/Model/ClientRequestOptions.cs (1)

14-50: LGTM! Well-designed interface and implementation.

The interface-based design provides a clean contract for per-request options, and the implementation correctly handles nullable headers for optional customization. The documentation clearly explains the header merging behavior with DefaultHeaders.

src/OpenFga.Sdk/Client/Client.cs (5)

68-78: LGTM! Centralized header extraction with validation.

The ExtractHeaders method correctly extracts headers from options implementing IClientRequestOptions, validates them using ClientConfiguration.ValidateHeaders, and returns IRequestOptions or null. This centralized approach ensures consistent header handling across all API calls.


239-251: LGTM! Correct enum mapping with explicit null handling.

Both mapping methods correctly convert client-facing enums to API enums, with explicit null handling that defaults to Error behavior. This aligns with the PR objective of explicitly sending default values to the API.


267-270: Breaking change: CreateStore parameter type updated.

The parameter type changed from ClientRequestOptions? to IClientRequestOptions?. While this enables the new header functionality, it's a breaking change that requires callers to update their code if they were explicitly using ClientRequestOptions as the parameter type.

Verify that this breaking change is properly documented in the changelog or release notes, as it may affect existing consumers of the SDK.


382-394: LGTM! Conflict options correctly applied to write requests.

The write request construction now properly incorporates conflict options via the mapping methods. When writes or deletes are present, the conflict behavior is explicitly passed to the constructors, ensuring the API receives the intended conflict resolution strategy.


407-412: LGTM! Proper initialization of clientWriteOpts with headers and conflict.

When transactions are disabled, the clientWriteOpts are correctly initialized with extracted headers and conflict options from the input options. This ensures that chunked write operations respect the same conflict resolution and custom headers.

src/OpenFga.Sdk/Client/Model/ClientWriteOptions.cs (3)

19-47: LGTM! Well-documented conflict resolution enums.

The enums clearly define the behavior for handling duplicate writes and missing deletes, with explicit documentation about the default Error behavior. The enum values start at 1 rather than 0, which is appropriate for distinguishing between "not set" (null) and an explicit value.


49-74: LGTM! Clean interface and implementation for conflict options.

The IConflictOptions interface and ConflictOptions class provide a clear contract for controlling conflict resolution behavior. The nullable properties allow callers to specify only the options they need, with unset values defaulting to Error in the mapping layer.


113-135: LGTM! Proper extension of write options.

The IClientWriteOptions interface and ClientWriteOptions class are correctly extended to support conflict options and per-request headers. This maintains consistency with the broader pattern of adding Headers support across all client option types.

src/OpenFga.Sdk/Api/OpenFgaApi.cs (1)

52-74: LGTM! Consistent per-request options pattern across API methods.

All API method signatures have been consistently updated to include an optional IRequestOptions? options parameter inserted before the CancellationToken. The headers are correctly extracted via options?.Headers and passed to the underlying API client. The XML documentation has been properly updated to document the new parameter.

This pattern is applied uniformly across all 17 public API methods in this file (BatchCheck, Check, CreateStore, DeleteStore, Expand, GetStore, ListObjects, ListStores, ListUsers, Read, ReadAssertions, ReadAuthorizationModel, ReadAuthorizationModels, ReadChanges, Write, WriteAssertions, WriteAuthorizationModel).

While the optional parameter minimizes breaking changes, verify that existing code using named parameters for cancellationToken still compiles correctly, as the parameter order has changed.

Example of potentially affected code:

await api.Check(storeId, body, cancellationToken: token);

Also applies to: 85-107, 117-133

Copilot AI review requested due to automatic review settings October 16, 2025 23:53
Copy link
Copy Markdown
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

This PR implements support for custom headers and write conflict resolution options for the OpenFGA .NET SDK, bringing it in line with OpenFGA API v1.10.0 capabilities.

  • Adds comprehensive custom headers support at both client configuration and per-request levels
  • Implements write conflict resolution options for handling duplicate writes and missing deletes
  • Introduces proper header validation and security measures to prevent reserved header overrides

Reviewed Changes

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

Show a summary per file
File Description
RequestOptions.cs New interface and class for API-level header support
ClientWriteOptions.cs Adds conflict resolution enums, options, and headers support
ClientRequestOptions.cs Refactored to interface-based pattern with headers support
ClientConfiguration.cs Adds default headers support with validation
Client.cs Implements header extraction, conflict option mapping, and API integration
ApiClient.cs Enhances header building and OAuth token management
OpenFgaApi.cs Updates all API methods to accept optional request options
OpenFgaClientTests.cs Comprehensive test coverage for new functionality
README.md Documentation for custom headers and conflict options

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@evansims evansims changed the base branch from main to feat/per-request-custom-headers October 16, 2025 23:54
@evansims evansims changed the title eat(DXAZD-1962): add write conflict resolution options feat: add write conflict resolution options Oct 16, 2025
@evansims evansims marked this pull request as draft October 17, 2025 02:27
@evansims evansims force-pushed the feat/write-conflict-options branch from 1adf577 to 5453646 Compare October 21, 2025 00:17
@evansims evansims marked this pull request as ready for review October 21, 2025 00:26
@evansims evansims requested a review from Copilot October 21, 2025 00:28
Copy link
Copy Markdown
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 5 out of 5 changed files in this pull request and generated 1 comment.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 21, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

SoulPancake
SoulPancake previously approved these changes Oct 21, 2025
Copy link
Copy Markdown
Member

@SoulPancake SoulPancake left a comment

Choose a reason for hiding this comment

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

LG

ewanharris
ewanharris previously approved these changes Oct 21, 2025
Base automatically changed from feat/per-request-custom-headers to main October 21, 2025 15:10
@evansims evansims dismissed stale reviews from ewanharris and SoulPancake October 21, 2025 15:10

The base branch was changed.

@evansims evansims force-pushed the feat/write-conflict-options branch from a8e05da to 7ada57e Compare October 21, 2025 15:23
"SendAsync",
ItExpr.Is<HttpRequestMessage>(req =>
req.RequestUri == new Uri($"{_config.BasePath}/stores/{_config.StoreId}/write") &&
req.Method == HttpMethod.Post),

Check notice

Code scanning / CodeQL

Missed 'using' opportunity Note test

This variable is manually
disposed
in a
finally block
- consider a C# using statement as a preferable resource management technique.

Copilot Autofix

AI 5 months ago

The most idiomatic fix is to leverage the using declaration so that HttpResponseMessage (responseMsg) is automatically disposed when it goes out of scope, rather than explicitly disposing it in a finally block. Given responseMsg is only assigned in the lambda executed during the mock handler, and not outside that, and its lifecycle is confined to the test method, we can introduce a new code block scoped via the using statement around the body of the test that consumes responseMsg. After the awaited call, assertions can be made as before. The try-finally block is removed, replaced with the using statement covering the same lifetime as the previous try block. This maintains identical functionality and guaranteed disposal in an idiomatic C# way.

Only the method WriteTuples_WithConflictOptions_ShouldPassOptionToApi requires modification (src/OpenFga.Sdk.Test/Client/OpenFgaClientTests.cs, lines 2581-2632):

  • Remove the try/finally block, and wrap the relevant logic (from after creating tuples) in a using block for responseMsg.
  • Restructure code so responseMsg is disposed when out of scope, using the latest C# using statement style if possible.
  • Ensure test setup, invocation, and assertions remain unchanged otherwise.

Suggested changeset 1
src/OpenFga.Sdk.Test/Client/OpenFgaClientTests.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/OpenFga.Sdk.Test/Client/OpenFgaClientTests.cs b/src/OpenFga.Sdk.Test/Client/OpenFgaClientTests.cs
--- a/src/OpenFga.Sdk.Test/Client/OpenFgaClientTests.cs
+++ b/src/OpenFga.Sdk.Test/Client/OpenFgaClientTests.cs
@@ -2613,22 +2613,19 @@
             }
         };
 
-        try {
-            await fgaClient.WriteTuples(tuples, new ClientWriteOptions {
-                AuthorizationModelId = "01GXSA8YR785C4FYS3C0RTG7B1",
-                Conflict = new ConflictOptions {
-                    OnDuplicateWrites = OnDuplicateWrites.Ignore
-                }
-            });
+        await fgaClient.WriteTuples(tuples, new ClientWriteOptions {
+            AuthorizationModelId = "01GXSA8YR785C4FYS3C0RTG7B1",
+            Conflict = new ConflictOptions {
+                OnDuplicateWrites = OnDuplicateWrites.Ignore
+            }
+        });
 
-            Assert.NotNull(capturedRequest);
-            Assert.NotNull(capturedRequest.Writes);
-            Assert.Equal(WriteRequestWrites.OnDuplicateEnum.Ignore, capturedRequest.Writes.OnDuplicate);
-        }
-        finally {
-            if (responseMsg != null)
-                responseMsg.Dispose();
-        }
+        Assert.NotNull(capturedRequest);
+        Assert.NotNull(capturedRequest.Writes);
+        Assert.Equal(WriteRequestWrites.OnDuplicateEnum.Ignore, capturedRequest.Writes.OnDuplicate);
+
+        if (responseMsg != null)
+            responseMsg.Dispose();
     }
 
     /// <summary>
EOF
@@ -2613,22 +2613,19 @@
}
};

try {
await fgaClient.WriteTuples(tuples, new ClientWriteOptions {
AuthorizationModelId = "01GXSA8YR785C4FYS3C0RTG7B1",
Conflict = new ConflictOptions {
OnDuplicateWrites = OnDuplicateWrites.Ignore
}
});
await fgaClient.WriteTuples(tuples, new ClientWriteOptions {
AuthorizationModelId = "01GXSA8YR785C4FYS3C0RTG7B1",
Conflict = new ConflictOptions {
OnDuplicateWrites = OnDuplicateWrites.Ignore
}
});

Assert.NotNull(capturedRequest);
Assert.NotNull(capturedRequest.Writes);
Assert.Equal(WriteRequestWrites.OnDuplicateEnum.Ignore, capturedRequest.Writes.OnDuplicate);
}
finally {
if (responseMsg != null)
responseMsg.Dispose();
}
Assert.NotNull(capturedRequest);
Assert.NotNull(capturedRequest.Writes);
Assert.Equal(WriteRequestWrites.OnDuplicateEnum.Ignore, capturedRequest.Writes.OnDuplicate);

if (responseMsg != null)
responseMsg.Dispose();
}

/// <summary>
Copilot is powered by AI and may make mistakes. Always verify output.
@evansims evansims enabled auto-merge October 21, 2025 15:58
@evansims evansims added this pull request to the merge queue Oct 21, 2025
Merged via the queue into main with commit 774dda0 Oct 21, 2025
23 checks passed
@evansims evansims deleted the feat/write-conflict-options branch October 21, 2025 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for ignoring duplicate write tuple keys and missing delete tuple keys

5 participants