Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5edaae9
feat: init raw requests api executor
SoulPancake Jan 28, 2026
f4bb328
fix: ignore local Claude settings
SoulPancake Jan 28, 2026
f23510c
fix: api executor test
SoulPancake Jan 28, 2026
170007e
feat: refactor changelog
SoulPancake Jan 28, 2026
94bd5f9
feat: testcontainers for raw req integration tests
SoulPancake Jan 28, 2026
1f7a04e
feat: address comments
SoulPancake Jan 29, 2026
a920106
feat: test
SoulPancake Jan 28, 2026
1bb2f0a
fix: config await
SoulPancake Feb 3, 2026
455d325
fix: address comments, tests fix
SoulPancake Feb 4, 2026
0e2a853
feat: add examples code
SoulPancake Feb 4, 2026
b755251
feat: address response tests comments
SoulPancake Feb 4, 2026
7930df4
Merge branch 'main' into feat/raw-requests-api-executor
SoulPancake Feb 4, 2026
bed99cb
fix: codeql issues
SoulPancake Feb 4, 2026
f4da59e
fix: code fmt
SoulPancake Feb 4, 2026
ae98e7f
feat: address coderabbit nit comments
SoulPancake Feb 4, 2026
d3f21eb
Merge branch 'main' into feat/raw-requests-api-executor
SoulPancake Feb 15, 2026
b15f27e
fix: lambda param
SoulPancake Feb 17, 2026
5aae9e4
fix: refactor to APIClient and re-use req builder
SoulPancake Feb 18, 2026
43de9e2
fix: examples to use ExecuteAsync
SoulPancake Feb 18, 2026
d37e986
feat: add APIExecutor wrapper
SoulPancake Feb 18, 2026
51c8bd7
fix: dedupe sendReqAsyncInternal
SoulPancake Feb 18, 2026
19bbc21
fix: make fmt
SoulPancake Feb 18, 2026
6af20d0
feat: add changelog, remove testcontainers
SoulPancake Feb 18, 2026
644f4a3
feat: use APIExecutor for openfgaapi
SoulPancake Feb 23, 2026
8d8f8e9
fix: add nullcheck
SoulPancake Feb 23, 2026
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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,25 @@
## [Unreleased](https://github.com/openfga/dotnet-sdk/compare/v0.9.1...HEAD)

### Added
- feat: add ApiExecutor for raw requests (#176)
- feat: add `FromJson()` methods to `ClientWriteAuthorizationModelRequest` and `ClientCreateStoreRequest` to enable loading from JSON string (#180)
- feat: report a per call HTTP metric (#173)

### Breaking Changes

> [!WARNING]
> - **`ApiClient.SendRequestAsync` removed**: If you were calling `SendRequestAsync` directly on `ApiClient`, switch to `ApiExecutor.ExecuteAsync`:
>
> Before:
> ```csharp
> var result = await apiClient.SendRequestAsync<MyRequest, MyResponse>(requestBuilder, "ApiName");
> ```
>
> After:
> ```csharp
> var result = (await apiClient.ApiExecutor.ExecuteAsync<MyRequest, MyResponse>(requestBuilder, "ApiName")).Data;
> ```

## v0.9.1

### [0.9.1](https://github.com/openfga/dotnet-sdk/compare/v0.9.0...v0.9.1) (2026-01-26)
Expand Down
109 changes: 109 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ This is an autogenerated SDK for OpenFGA. It provides a wrapper around the [Open
- [Assertions](#assertions)
- [Read Assertions](#read-assertions)
- [Write Assertions](#write-assertions)
- [Calling Other Endpoints](#calling-other-endpoints)
- [Retries](#retries)
- [API Endpoints](#api-endpoints)
- [Models](#models)
Expand Down Expand Up @@ -191,6 +192,10 @@ namespace Example {
Credentials = new Credentials() {
Method = CredentialsMethod.ClientCredentials,
Config = new CredentialsConfig() {
// API Token Issuer can contain:
// - a scheme, defaults to https
// - a path, defaults to /oauth/token
// - a port
ApiTokenIssuer = Environment.GetEnvironmentVariable("FGA_API_TOKEN_ISSUER"),
ApiAudience = Environment.GetEnvironmentVariable("FGA_API_AUDIENCE"),
ClientId = Environment.GetEnvironmentVariable("FGA_CLIENT_ID"),
Expand Down Expand Up @@ -965,6 +970,110 @@ var body = new List<ClientAssertion>() {new ClientAssertion() {
await fgaClient.WriteAssertions(body, options);
```

### Calling Other Endpoints

For advanced use cases where you need to call API endpoints not yet available in the SDK's typed methods, or when you need access to full response details (status code, headers, raw response), you can use `ApiClient.ExecuteAsync()`.

#### Basic Usage

```csharp
using OpenFga.Sdk.ApiClient;

var client = new OpenFgaClient(configuration);
var executor = client.ApiExecutor;

// Build a request using RequestBuilder
var request = new RequestBuilder<object> {
Method = HttpMethod.Get,
BasePath = configuration.ApiUrl,
PathTemplate = "/stores/{store_id}",
PathParameters = new Dictionary<string, string> { { "store_id", storeId } },
QueryParameters = new Dictionary<string, string>()
};

// Execute and get full response details
var response = await executor.ExecuteAsync<object, GetStoreResponse>(request, "GetStore");

// Always check if the request was successful
if (!response.IsSuccessful) {
Console.WriteLine($"Request failed: {response.StatusCode}");
Console.WriteLine($"Error: {response.RawResponse}");
return;
}

// Access response details
Console.WriteLine($"Status: {response.StatusCode}");
Console.WriteLine($"Headers: {response.Headers.Count}");
Console.WriteLine($"Raw JSON: {response.RawResponse}");
Console.WriteLine($"Data: {response.Data.Name}");
```

#### Fluent API Style

You can also use a fluent API for cleaner request building:

```csharp
var request = RequestBuilder<object>
.Create(HttpMethod.Post, configuration.ApiUrl, "/stores/{store_id}/check")
.WithPathParameter("store_id", storeId)
.WithQueryParameter("consistency", "HIGHER_CONSISTENCY")
.WithBody(checkRequest);

var response = await executor.ExecuteAsync<object, CheckResponse>(request, "Check");
```

#### Custom Headers

```csharp
var options = new ClientRequestOptions {
Headers = new Dictionary<string, string> {
{ "X-Custom-Header", "value" },
{ "X-Trace-Id", traceId }
}
};

var response = await executor.ExecuteAsync<object, TResponse>(
request,
"CustomEndpoint",
options
);
```

#### Error Handling

Always check `response.IsSuccessful` and handle different status codes appropriately:

```csharp
var response = await executor.ExecuteAsync<object, GetStoreResponse>(request, "GetStore");

if (!response.IsSuccessful)
{
switch ((int)response.StatusCode)
{
case 404:
Console.WriteLine("Store not found");
break;
case 401:
Console.WriteLine("Unauthorized - check your credentials");
break;
case 429:
Console.WriteLine("Rate limited - retry after delay");
break;
case >= 500:
Console.WriteLine($"Server error: {response.RawResponse}");
break;
default:
Console.WriteLine($"Request failed: {response.StatusCode}");
break;
}
return;
}

// Safe to use response.Data here
Console.WriteLine($"Store Name: {response.Data.Name}");
```

For a complete example with all features, see the [ApiExecutor Example](./example/ApiExecutorExample/).

### Retries

Expand Down
5 changes: 5 additions & 0 deletions example/ApiExecutorExample/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
bin/
obj/
*.user
*.suo
.vs/
16 changes: 16 additions & 0 deletions example/ApiExecutorExample/ApiExecutorExample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>

<!-- To target the local build, use project reference -->
<ItemGroup>
<ProjectReference Include="..\..\src\OpenFga.Sdk\OpenFga.Sdk.csproj" />
</ItemGroup>

</Project>
46 changes: 46 additions & 0 deletions example/ApiExecutorExample/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.PHONY: help start-openfga stop-openfga run run-all clean

help:
@echo "ApiExecutor Example - Makefile Commands"
@echo ""
@echo " make start-openfga - Start OpenFGA server in Docker on port 8080"
@echo " make stop-openfga - Stop the OpenFGA Docker container"
@echo " make run - Run the ApiExecutor example (requires OpenFGA running)"
@echo " make run-all - Start OpenFGA, run example, then stop OpenFGA"
@echo " make clean - Clean build artifacts"
@echo ""

# Start OpenFGA server in Docker
start-openfga:
@echo "Starting OpenFGA server on localhost:8080..."
@docker run -d --name openfga-example -p 8080:8080 openfga/openfga:latest run
@echo "Waiting for OpenFGA to be ready..."
@sleep 3
@curl -s http://localhost:8080/healthz || (echo "OpenFGA failed to start" && exit 1)
@echo "✅ OpenFGA is ready!"

# Stop OpenFGA server
stop-openfga:
@echo "Stopping OpenFGA server..."
@docker stop openfga-example 2>/dev/null || true
@docker rm openfga-example 2>/dev/null || true
@echo "✅ OpenFGA stopped"

# Run the example
run:
@echo "Running ApiExecutor example..."
@dotnet run --project ApiExecutorExample.csproj

# Run everything: start server, run example, stop server
run-all: start-openfga
@echo ""
@$(MAKE) run
@echo ""
@$(MAKE) stop-openfga

# Clean build artifacts
clean:
@echo "Cleaning build artifacts..."
@dotnet clean
@rm -rf bin obj
@echo "✅ Clean complete"
Loading
Loading