Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(python): add support for the BatchCheck API #459

Merged
merged 8 commits into from
Dec 19, 2024
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
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
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
Loading