Skip to content

Commit

Permalink
feat(python): add support for the BatchCheck API (#459)
Browse files Browse the repository at this point in the history
  • Loading branch information
ewanharris authored Dec 19, 2024
2 parents a390427 + 6fbec89 commit 19ffce1
Show file tree
Hide file tree
Showing 31 changed files with 1,607 additions and 192 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Main config
OPENFGA_DOCKER_TAG = v1.7.0
OPEN_API_REF ?= 7c098f10acd22137c659c407818a4e0880044afe
OPENFGA_DOCKER_TAG = v1.8.1
OPEN_API_REF ?= 0bb89b73d6550b627f79c53b4b97dec1ee3fe0ad
OPEN_API_URL = https://raw.githubusercontent.com/openfga/api/${OPEN_API_REF}/docs/openapiv2/apidocs.swagger.json
OPENAPI_GENERATOR_CLI_DOCKER_TAG = v6.4.0
NODE_DOCKER_TAG = 20-alpine
Expand Down
2 changes: 2 additions & 0 deletions config/clients/dotnet/CHANGELOG.md.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [Unreleased](https://github.com/openfga/dotnet-sdk/compare/v{{packageVersion}}...HEAD)

- feat: add support for `start_time` parameter in `ReadChanges` endpoint

## v0.5.1

### [0.5.1](https://{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/compare/v0.5.0...v0.5.1) (2024-09-09)
Expand Down
4 changes: 2 additions & 2 deletions config/clients/dotnet/template/Client/Client.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public class {{appShortName}}Client : IDisposable {
/**
* BatchCheck - Run a set of checks (evaluates)
*/
public async Task<BatchCheckResponse> BatchCheck(List<ClientCheckRequest> body,
public async Task<ClientBatchCheckClientResponse> BatchCheck(List<ClientCheckRequest> body,
IClientBatchCheckOptions? options = default,
CancellationToken cancellationToken = default) {
var responses = new ConcurrentBag<BatchCheckSingleResponse>();
Expand All @@ -321,7 +321,7 @@ public class {{appShortName}}Client : IDisposable {
}
});

return new BatchCheckResponse {Responses = responses.ToList()};
return new ClientBatchCheckClientResponse {Responses = responses.ToList()};
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,27 @@ public class BatchCheckSingleResponse : IEquatable<BatchCheckSingleResponse>, IV
/// <summary>
/// CheckResponse
/// </summary>
[DataContract(Name = "BatchCheckResponse")]
public class BatchCheckResponse : IEquatable<BatchCheckResponse>, IValidatableObject {
[DataContract(Name = "ClientBatchCheckClientResponse")]
public class ClientBatchCheckClientResponse : IEquatable<ClientBatchCheckClientResponse>, IValidatableObject {
/// <summary>
/// Initializes a new instance of the <see cref="BatchCheckResponse" /> class.
/// Initializes a new instance of the <see cref="ClientBatchCheckClientResponse" /> class.
/// </summary>
public BatchCheckResponse() {
public ClientBatchCheckClientResponse() {
Responses = new List<BatchCheckSingleResponse>();
}

/// <summary>
/// Initializes a new instance of the <see cref="BatchCheckResponse" /> class.
/// Initializes a new instance of the <see cref="ClientBatchCheckClientResponse" /> class.
/// </summary>
public BatchCheckResponse(List<BatchCheckSingleResponse> responses) {
public ClientBatchCheckClientResponse(List<BatchCheckSingleResponse> responses) {
Responses = responses;
}

[DataMember(Name = "responses", EmitDefaultValue = true)]
[JsonPropertyName("responses")]
public List<BatchCheckSingleResponse> Responses { get; set; }

public bool Equals(BatchCheckResponse? other) => throw new NotImplementedException();
public bool Equals(ClientBatchCheckClientResponse? other) => throw new NotImplementedException();

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) =>
throw new NotImplementedException();
Expand Down
2 changes: 1 addition & 1 deletion config/clients/dotnet/template/OpenFgaClientTests.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -1183,7 +1183,7 @@ public class {{appShortName}}ClientTests {
ItExpr.IsAny<CancellationToken>()
);

Assert.IsType<BatchCheckResponse>(response);
Assert.IsType<ClientBatchCheckClientResponse>(response);

var allowedResponses = response.Responses.FindAll(res => res.Allowed == true);
Assert.Equal(2, allowedResponses.Count);
Expand Down
2 changes: 2 additions & 0 deletions config/clients/go/CHANGELOG.md.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [Unreleased](https://github.com/openfga/go-sdk/compare/v{{packageVersion}}...HEAD)

- feat: add support for `start_time` parameter in `ReadChanges` endpoint

## v0.6.3

### [0.6.3](https://github.com/openfga/go-sdk/compare/v0.6.2...v0.6.3) (2024-10-22)
Expand Down
2 changes: 2 additions & 0 deletions config/clients/java/CHANGELOG.md.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [Unreleased](https://github.com/openfga/java-sdk/compare/v{{packageVersion}}...HEAD)

- feat: add support for `start_time` parameter in `ReadChanges` endpoint

## v0.7.1

### [0.7.1](https://github.com/openfga/java-sdk/compare/v0.7.0...v0.7.1) (2024-09-23)
Expand Down
2 changes: 1 addition & 1 deletion config/clients/java/template/OpenFgaApiTest.java.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -1424,7 +1424,7 @@ public class OpenFgaApiTest {
// Given
String postPath = "https://api.fga.example/stores/01YCP46JKYM8FJCQ37NMBYHE5X/expand";
String expectedBody = String.format(
"{\"tuple_key\":{\"relation\":\"%s\",\"object\":\"%s\"},\"authorization_model_id\":\"%s\",\"consistency\":\"%s\"}",
"{\"tuple_key\":{\"relation\":\"%s\",\"object\":\"%s\"},\"authorization_model_id\":\"%s\",\"consistency\":\"%s\",\"contextual_tuples\":null}",
DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_AUTH_MODEL_ID, ConsistencyPreference.HIGHER_CONSISTENCY);
String responseBody = String.format(
"{\"tree\":{\"root\":{\"union\":{\"nodes\":[{\"leaf\":{\"users\":{\"users\":[\"%s\"]}}}]}}}}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1829,8 +1829,7 @@ public class OpenFgaClientTest {
// Given
String postPath = "https://api.fga.example/stores/01YCP46JKYM8FJCQ37NMBYHE5X/expand";
String expectedBody = String.format(
"{\"tuple_key\":{\"relation\":\"%s\",\"object\":\"%s\"},\"authorization_model_id\":\"%s\",\"consistency\":\"%s\"}",
DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_AUTH_MODEL_ID, ConsistencyPreference.HIGHER_CONSISTENCY);
"{\"tuple_key\":{\"relation\":\"%s\",\"object\":\"%s\"},\"authorization_model_id\":\"%s\",\"consistency\":\"%s\",\"contextual_tuples\":null}", DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_AUTH_MODEL_ID, ConsistencyPreference.HIGHER_CONSISTENCY);
String responseBody = String.format(
"{\"tree\":{\"root\":{\"union\":{\"nodes\":[{\"leaf\":{\"users\":{\"users\":[\"%s\"]}}}]}}}}",
DEFAULT_USER);
Expand Down
3 changes: 2 additions & 1 deletion config/clients/js/CHANGELOG.md.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

## [Unreleased](https://github.com/openfga/js-sdk/compare/v{{packageVersion}}...HEAD)

fix: error correctly if apiUrl is not provided (#161)
- fix: error correctly if apiUrl is not provided (#161)
- feat: add support for `start_time` parameter in `ReadChanges` endpoint

## v0.7.0

Expand Down
12 changes: 12 additions & 0 deletions config/clients/python/CHANGELOG.md.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

## [Unreleased](https://github.com/openfga/python-sdk/compare/v{{packageVersion}}...HEAD)

- feat: remove client-side validation - thanks @GMorris-professional (#155)
- feat: add support for `start_time` parameter in `ReadChanges` endpoint (#156) - Note, this feature requires v1.8.0 of OpenFGA or newer
- feat!: add support for `BatchCheck` API (#154) - Note, this feature requires v1.8.2 of OpenFGA or newer
- fix: change default max retry limit to 3 from 15 - thanks @ovindu-a (#155)

BREAKING CHANGE:

Usage of the existing batch_check should now use client_batch_check instead, additionally the existing
BatchCheckResponse has been renamed to ClientBatchCheckClientResponse.

Please see (#154)(https://github.com/openfga/python-sdk/pull/154) for more details on this change.

## v0.8.1

### [0.8.1](https://github.com/openfga/python-sdk/compare/v0.8.0...v0.8.1) (2024-11-26)
Expand Down
16 changes: 16 additions & 0 deletions config/clients/python/config.overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,26 @@
"destinationFilename": "openfga_sdk/client/models/assertion.py",
"templateType": "SupportingFiles"
},
"src/client/models/batch_check_item.py.mustache": {
"destinationFilename": "openfga_sdk/client/models/batch_check_item.py",
"templateType": "SupportingFiles"
},
"src/client/models/batch_check_request.py.mustache": {
"destinationFilename": "openfga_sdk/client/models/batch_check_request.py",
"templateType": "SupportingFiles"
},
"src/client/models/batch_check_response.py.mustache": {
"destinationFilename": "openfga_sdk/client/models/batch_check_response.py",
"templateType": "SupportingFiles"
},
"src/client/models/batch_check_single_response.py.mustache": {
"destinationFilename": "openfga_sdk/client/models/batch_check_single_response.py",
"templateType": "SupportingFiles"
},
"src/client/models/client_batch_check_response.py.mustache": {
"destinationFilename": "openfga_sdk/client/models/client_batch_check_response.py",
"templateType": "SupportingFiles"
},
"src/client/models/check_request.py.mustache": {
"destinationFilename": "openfga_sdk/client/models/check_request.py",
"templateType": "SupportingFiles"
Expand Down
35 changes: 22 additions & 13 deletions config/clients/python/template/README_calling_api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -492,17 +492,19 @@ If 429s or 5xxs are encountered, the underlying check will retry up to {{default

```python
# from {{packageName}} import OpenFgaClient
# from {{packageName}}.client import ClientCheckRequest
# from {{packageName}}.client.models import ClientTuple

# from {{packageName}}.client.models import (
# ClientTuple,
# ClientBatchCheckItem,
# ClientBatchCheckRequest,
# )
# Initialize the fga_client
# fga_client = OpenFgaClient(configuration)

options = {
# You can rely on the model id set in the configuration or override it for this specific request
"authorization_model_id": "01GXSA8YR785C4FYS3C0RTG7B1"
}
body = [ClientCheckRequest(
checks = [ClientBatchCheckItem(
user="user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation="viewer",
object="document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
Expand All @@ -516,7 +518,7 @@ body = [ClientCheckRequest(
context=dict(
ViewCount=100
)
), ClientCheckRequest(
), ClientBatchCheckItem(
user="user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation="admin",
object="document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
Expand All @@ -527,20 +529,21 @@ body = [ClientCheckRequest(
object="document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
),
]
), ClientCheckRequest(
), ClientBatchCheckItem(
user="user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation="creator",
object="document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
), ClientCheckRequest(
), ClientBatchCheckItem(
user="user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation="deleter",
object="document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
)]

response = await fga_client.batch_check(body, options)
# response.responses = [{
response = await fga_client.batch_check(ClientBatchCheckRequest(checks=checks), options)
# response.result = [{
# allowed: false,
# request: {
# correlation_id: "de3630c2-f9be-4ee5-9441-cb1fbd82ce75",
# tuple: {
# user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
# relation: "viewer",
# object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
Expand All @@ -555,7 +558,8 @@ response = await fga_client.batch_check(body, options)
# }
# }, {
# allowed: false,
# request: {
# correlation_id: "6d7c7129-9607-480e-bfd0-17c16e46b9ec",
# tuple: {
# user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
# relation: "admin",
# object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
Expand All @@ -567,14 +571,19 @@ response = await fga_client.batch_check(body, options)
# }
# }, {
# allowed: false,
# request: {
# correlation_id: "210899b9-6bc3-4491-bdd1-d3d79780aa31",
# tuple: {
# user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
# relation: "creator",
# object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
# },
# error: <FgaError ...>
# error: {
# input_error: "validation_error",
# message: "relation 'document#creator' not found"
# }
# }, {
# allowed: true,
# correlation_id: "55cc1946-9fc3-4710-bd40-8fe2687ed8da",
# request: {
# user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
# relation: "deleter",
Expand Down
35 changes: 18 additions & 17 deletions config/clients/python/template/docs/opentelemetry.md.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,24 @@ If you configure the OpenTelemetry SDK, these metrics will be exported and sent

### Supported Attributes

| Attribute Name | Type | Enabled by Default | Description |
| ------------------------------ | ------ | ------------------ | --------------------------------------------------------------------------------- |
| `fga-client.request.client_id` | string | Yes | Client ID associated with the request, if any |
| `fga-client.request.method` | string | Yes | FGA method/action that was performed (e.g., Check, ListObjects) in TitleCase |
| `fga-client.request.model_id` | string | Yes | Authorization model ID that was sent as part of the request, if any |
| `fga-client.request.store_id` | string | Yes | Store ID that was sent as part of the request |
| `fga-client.response.model_id` | string | Yes | Authorization model ID that the FGA server used |
| `fga-client.user` | string | No | User associated with the action of the request for check and list users |
| `http.client.request.duration` | int | No | Duration for the SDK to complete the request, in milliseconds |
| `http.host` | string | Yes | Host identifier of the origin the request was sent to |
| `http.request.method` | string | Yes | HTTP method for the request |
| `http.request.resend_count` | int | Yes | Number of retries attempted, if any |
| `http.response.status_code` | int | Yes | Status code of the response (e.g., `200` for success) |
| `http.server.request.duration` | int | No | Time taken by the FGA server to process and evaluate the request, in milliseconds |
| `url.scheme` | string | Yes | HTTP scheme of the request (`http`/`https`) |
| `url.full` | string | Yes | Full URL of the request |
| `user_agent.original` | string | Yes | User Agent used in the query |
| Attribute Name | Type | Enabled by Default | Description |
| ------------------------------------- | ------ | ------------------ | --------------------------------------------------------------------------------- |
| `fga-client.request.batch_check_size` | int | No | The total size of the `check` list in a `BatchCheck` call |
| `fga-client.request.client_id` | string | Yes | Client ID associated with the request, if any |
| `fga-client.request.method` | string | Yes | FGA method/action that was performed (e.g., Check, ListObjects) in TitleCase |
| `fga-client.request.model_id` | string | Yes | Authorization model ID that was sent as part of the request, if any |
| `fga-client.request.store_id` | string | Yes | Store ID that was sent as part of the request |
| `fga-client.response.model_id` | string | Yes | Authorization model ID that the FGA server used |
| `fga-client.user` | string | No | User associated with the action of the request for check and list users |
| `http.client.request.duration` | int | No | Duration for the SDK to complete the request, in milliseconds |
| `http.host` | string | Yes | Host identifier of the origin the request was sent to |
| `http.request.method` | string | Yes | HTTP method for the request |
| `http.request.resend_count` | int | Yes | Number of retries attempted, if any |
| `http.response.status_code` | int | Yes | Status code of the response (e.g., `200` for success) |
| `http.server.request.duration` | int | No | Time taken by the FGA server to process and evaluate the request, in milliseconds |
| `url.scheme` | string | Yes | HTTP scheme of the request (`http`/`https`) |
| `url.full` | string | Yes | Full URL of the request |
| `user_agent.original` | string | Yes | User Agent used in the query |

## Customizing Reporting

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
import os
import uuid

from {{packageName}} import (
ClientConfiguration,
Expand All @@ -21,6 +22,8 @@ from {{packageName}} import (
)
from {{packageName}}.client.models import (
ClientAssertion,
ClientBatchCheckItem,
ClientBatchCheckRequest,
ClientCheckRequest,
ClientListObjectsRequest,
ClientListRelationsRequest,
Expand Down Expand Up @@ -266,6 +269,36 @@ async def main():
)
print(f"Allowed: {response.allowed}")

# Performing a BatchCheck
print("Checking for access via BatchCheck")

anne_cor_id = str(uuid.uuid4())
response = await fga_client.batch_check(
ClientBatchCheckRequest(
checks=[
ClientBatchCheckItem(
user="user:anne",
relation="viewer",
object="document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
context=dict(ViewCount=100),
correlation_id=anne_cor_id, # correlation_id is an optional parameter, the SDK will insert a value if not provided.
),
ClientBatchCheckItem(
user="user:bob",
relation="viewer",
object="document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a",
context=dict(ViewCount=100),
),
]
)
)

for result in response.result:
if result.correlation_id == anne_cor_id:
print(f"Anne allowed: {result.allowed}")
else:
print(f"{result.request.user} allowed: {result.allowed}")

# List objects with context
print("Listing objects for access with context")

Expand Down
5 changes: 5 additions & 0 deletions config/clients/python/template/src/api.py.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ class {{classname}}:
),
}

telemetry_attributes = TelemetryAttributes.fromBody(
body=body_params,
attributes=telemetry_attributes,
)

return {{#asyncio}}await ({{/asyncio}}self.api_client.call_api(
'{{{path}}}'.replace('{store_id}', store_id), '{{httpMethod}}',
path_params,
Expand Down
Loading

0 comments on commit 19ffce1

Please sign in to comment.