Coder Social home page Coder Social logo

openfga / java-sdk Goto Github PK

View Code? Open in Web Editor NEW
31.0 15.0 5.0 1.06 MB

OpenFGA SDK for Java - https://central.sonatype.com/artifact/dev.openfga/openfga-sdk

Home Page: https://openfga.dev

License: Apache License 2.0

Java 99.23% Makefile 0.03% Kotlin 0.73%
access-control authorization fga fine-grained-authorization openfga security zanzibar java

java-sdk's Introduction

Java SDK for OpenFGA

Maven Central Javadoc Release License FOSSA Status Join our community Twitter

This is an autogenerated Java SDK for OpenFGA. It provides a wrapper around the OpenFGA API definition.

Table of Contents

About

OpenFGA is an open source Fine-Grained Authorization solution inspired by Google's Zanzibar paper. It was created by the FGA team at Auth0 based on Auth0 Fine-Grained Authorization (FGA), available under a permissive license (Apache-2) and welcomes community contributions.

OpenFGA is designed to make it easy for application builders to model their permission layer, and to add and integrate fine-grained authorization into their applications. OpenFGA’s design is optimized for reliability and low latency at a high scale.

Resources

Installation

The OpenFGA Java SDK is available on Maven Central.

It can be used with the following:

  • Gradle (Groovy)
implementation 'dev.openfga:openfga-sdk:0.7.0'
  • Gradle (Kotlin)
implementation("dev.openfga:openfga-sdk:0.7.0")
  • Apache Maven
<dependency>
    <groupId>dev.openfga</groupId>
    <artifactId>openfga-sdk</artifactId>
    <version>0.7.0</version>
</dependency>
  • Ivy
<dependency org="dev.openfga" name="openfga-sdk" rev="0.7.0"/>
  • SBT
libraryDependencies += "dev.openfga" % "openfga-sdk" % "0.7.0"
  • Leiningen
[dev.openfga/openfga-sdk "0.7.0"]

Getting Started

Initializing the API Client

Learn how to initialize your SDK

We strongly recommend you initialize the OpenFgaClient only once and then re-use it throughout your app, otherwise you will incur the cost of having to re-initialize multiple times or at every request, the cost of reduced connection pooling and re-use, and would be particularly costly in the client credentials flow, as that flow will be preformed on every request.

The Client will by default retry API requests up to 15 times on 429 and 5xx errors.

No Credentials

import com.fasterxml.jackson.databind.ObjectMapper;
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import java.net.http.HttpClient;

public class Example {
    public static void main(String[] args) throws Exception {
        var config = new ClientConfiguration()
                .apiUrl(System.getenv("FGA_API_URL")) // If not specified, will default to "http://localhost:8080"
                .storeId(System.getenv("FGA_STORE_ID")) // Not required when calling createStore() or listStores()
                .authorizationModelId(System.getenv("FGA_MODEL_ID")); // Optional, can be overridden per request

        var fgaClient = new OpenFgaClient(config);
        var response = fgaClient.readAuthorizationModels().get();
    }
}

API Token

import com.fasterxml.jackson.databind.ObjectMapper;
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ApiToken;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.configuration.Credentials;
import java.net.http.HttpClient;

public class Example {
    public static void main(String[] args) throws Exception {
        var config = new ClientConfiguration()
                .apiUrl(System.getenv("FGA_API_URL")) // If not specified, will default to "http://localhost:8080"
                .storeId(System.getenv("FGA_STORE_ID")) // Not required when calling createStore() or listStores()
                .authorizationModelId(System.getenv("FGA_MODEL_ID")) // Optional, can be overridden per request
                .credentials(new Credentials(
                    new ApiToken(System.getenv("FGA_API_TOKEN")) // will be passed as the "Authorization: Bearer ${ApiToken}" request header
                ));

        var fgaClient = new OpenFgaClient(config);
        var response = fgaClient.readAuthorizationModels().get();
    }
}

Auth0 Client Credentials

import com.fasterxml.jackson.databind.ObjectMapper;
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.configuration.ClientCredentials;
import dev.openfga.sdk.api.configuration.Credentials;
import java.net.http.HttpClient;

public class Example {
    public static void main(String[] args) throws Exception {
        var config = new ClientConfiguration()
                .apiUrl(System.getenv("FGA_API_URL")) // If not specified, will default to "http://localhost:8080"
                .storeId(System.getenv("FGA_STORE_ID")) // Not required when calling createStore() or listStores()
                .authorizationModelId(System.getenv("FGA_MODEL_ID")) // Optional, can be overridden per request
                .credentials(new Credentials(
                    new ClientCredentials()
                            .apiTokenIssuer(System.getenv("FGA_API_TOKEN_ISSUER"))
                            .apiAudience(System.getenv("FGA_API_AUDIENCE"))
                            .clientId(System.getenv("FGA_CLIENT_ID"))
                            .clientSecret(System.getenv("FGA_CLIENT_SECRET"))
                ));

        var fgaClient = new OpenFgaClient(config);
        var response = fgaClient.readAuthorizationModels().get();
    }
}

Oauth2 Credentials

import com.fasterxml.jackson.databind.ObjectMapper;
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.configuration.ClientCredentials;
import dev.openfga.sdk.api.configuration.Credentials;
import java.net.http.HttpClient;

public class Example {
    public static void main(String[] args) throws Exception {
        var config = new ClientConfiguration()
                .apiUrl(System.getenv("FGA_API_URL")) // If not specified, will default to "http://localhost:8080"
                .storeId(System.getenv("FGA_STORE_ID")) // Not required when calling createStore() or listStores()
                .authorizationModelId(System.getenv("FGA_MODEL_ID")) // Optional, can be overridden per request
                .credentials(new Credentials(
                    new ClientCredentials()
                            .apiTokenIssuer(System.getenv("FGA_API_TOKEN_ISSUER"))
                            .scopes(System.getenv("FGA_API_SCOPES")) // optional space separated scopes
                            .clientId(System.getenv("FGA_CLIENT_ID"))
                            .clientSecret(System.getenv("FGA_CLIENT_SECRET"))
                ));

        var fgaClient = new OpenFgaClient(config);
        var response = fgaClient.readAuthorizationModels().get();
    }
}

Get your Store ID

You need your store id to call the OpenFGA API (unless it is to call the CreateStore or ListStores methods).

If your server is configured with authentication enabled, you also need to have your credentials ready.

Calling the API

Stores

List Stores

Get a paginated list of stores.

API Documentation

Passing ClientListStoresOptions is optional. All fields of ClientListStoresOptions are optional.

var options = new ClientListStoresOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    .pageSize(10)
    .continuationToken("...");
var stores = fgaClient.listStores(options);

// stores = [{ "id": "01FQH7V8BEG3GPQW93KTRFR8JB", "name": "FGA Demo Store", "created_at": "2022-01-01T00:00:00.000Z", "updated_at": "2022-01-01T00:00:00.000Z" }]
Create Store

Initialize a store.

API Documentation

Passing ClientCreateStoreOptions is optional. All fields of ClientCreateStoreOptions are optional.

var request = new CreateStoreRequest().name("FGA Demo");
var options = new ClientCreateStoreOptions().additionalHeaders(Map.of("Some-Http-Header", "Some value"));
var store = fgaClient.createStore(request, options).get();

// store.getId() = "01FQH7V8BEG3GPQW93KTRFR8JB"

// store the store.getId() in database

// update the storeId of the client instance
fgaClient.setStoreId(store.getId());

// continue calling the API normally
Get Store

Get information about the current store.

API Documentation

Requires a client initialized with a storeId

Passing ClientGetStoreOptions is optional. All fields of ClientGetStoreOptions are optional.

var options = new ClientGetStoreOptions().additionalHeaders(Map.of("Some-Http-Header", "Some value"));
var store = fgaClient.getStore(options).get();

// store = { "id": "01FQH7V8BEG3GPQW93KTRFR8JB", "name": "FGA Demo Store", "created_at": "2022-01-01T00:00:00.000Z", "updated_at": "2022-01-01T00:00:00.000Z" }
Delete Store

Delete a store.

API Documentation

Requires a client initialized with a storeId

Passing ClientDeleteStoreOptions is optional. All fields of ClientDeleteStoreOptions are optional.

var options = new ClientDeleteStoreOptions().additionalHeaders(Map.of("Some-Http-Header", "Some value"));
var store = fgaClient.deleteStore(options).get();

Authorization Models

Read Authorization Models

Read all authorization models in the store.

API Documentation

Passing ClientReadAuthorizationModelsOptions is optional. All fields of ClientReadAuthorizationModelsOptions are optional.

var options = new ClientReadAuthorizationModelsOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    .pageSize(10)
    .continuationToken("...");
var response = fgaClient.readAuthorizationModels(options).get();

// response.getAuthorizationModels() = [
// { id: "01GXSA8YR785C4FYS3C0RTG7B1", schemaVersion: "1.1", typeDefinitions: [...] },
// { id: "01GXSBM5PVYHCJNRNKXMB4QZTW", schemaVersion: "1.1", typeDefinitions: [...] }];
Write Authorization Model

Create a new authorization model.

API Documentation

Note: To learn how to build your authorization model, check the Docs at https://openfga.dev/docs.

Learn more about the OpenFGA configuration language.

You can use the OpenFGA CLI or Syntax Transformer to convert between the OpenFGA DSL and the JSON authorization model.

Passing ClientWriteAuthorizationModelOptions is optional. All fields of ClientWriteAuthorizationModelOptions are optional.

var request = new WriteAuthorizationModelRequest()
    .schemaVersion("1.1")
    .typeDefinitions(List.of(
        new TypeDefinition().type("user").relations(Map.of()),
        new TypeDefinition()
            .type("document")
            .relations(Map.of(
                "writer", new Userset(),
                "viewer", new Userset().union(new Usersets()
                    .child(List.of(
                        new Userset(),
                        new Userset().computedUserset(new ObjectRelation().relation("writer"))
                    ))
                )
            ))
            .metadata(new Metadata()
                .relations(Map.of(
                    "writer", new RelationMetadata().directlyRelatedUserTypes(
                        List.of(new RelationReference().type("user"))
                    ),
                    "viewer", new RelationMetadata().directlyRelatedUserTypes(
                        List.of(new RelationReference().type("user"))
                    )
                ))
            )
    ));
var options = new ClientWriteAuthorizationModelOptions().additionalHeaders(Map.of("Some-Http-Header", "Some value"));

var response = fgaClient.writeAuthorizationModel(request, options).get();

// response.getAuthorizationModelId() = "01GXSA8YR785C4FYS3C0RTG7B1"

Read a Single Authorization Model

Read a particular authorization model.

API Documentation

Passing ClientReadAuthorizationModelOptions is optional. All fields of ClientReadAuthorizationModelOptions are optional.

var options = new ClientReadAuthorizationModelOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    // You can rely on the model id set in the configuration or override it for this specific request
    .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1");

var response = fgaClient.readAuthorizationModel(options).get();

// response.getAuthorizationModel().getId() = "01GXSA8YR785C4FYS3C0RTG7B1"
// response.getAuthorizationModel().getSchemaVersion() = "1.1"
// response.getAuthorizationModel().getTypeDefinitions() = [{ "type": "document", "relations": { ... } }, { "type": "user", "relations": { ... }}]
Read the Latest Authorization Model

Reads the latest authorization model (note: this ignores the model id in configuration).

API Documentation

Passing ClientReadLatestAuthorizationModelOptions is optional. All fields of ClientReadLatestAuthorizationModelOptions are optional.

var options = new ClientReadLatestAuthorizationModelOptions().additionalHeaders(Map.of("Some-Http-Header", "Some value"));
var response = fgaClient.readLatestAuthorizationModel(options).get();

// response.getAuthorizationModel().getId() = "01GXSA8YR785C4FYS3C0RTG7B1"
// response.getAuthorizationModel().SchemaVersion() = "1.1"
// response.getAuthorizationModel().TypeDefinitions() = [{ "type": "document", "relations": { ... } }, { "type": "user", "relations": { ... }}]

Relationship Tuples

Read Relationship Tuple Changes (Watch)

Reads the list of historical relationship tuple writes and deletes.

API Documentation

Passing ClientReadChangesOptions is optional. All fields of ClientReadChangesOptions are optional.

var request = new ClientReadChangesRequest().type("document");
var options = new ClientReadChangesOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    .pageSize(10)
    .continuationToken("...");

var response = fgaClient.readChanges(request, options).get();

// response.getContinuationToken() = ...
// response.getChanges() = [
//   { tupleKey: { user, relation, object }, operation: TupleOperation.WRITE, timestamp: ... },
//   { tupleKey: { user, relation, object }, operation: TupleOperation.DELETE, timestamp: ... }
// ]
Read Relationship Tuples

Reads the relationship tuples stored in the database. It does not evaluate nor exclude invalid tuples according to the authorization model.

API Documentation

Passing ClientReadOptions is optional. All fields of ClientReadOptions are optional.

// Find if a relationship tuple stating that a certain user is a viewer of a certain document
var request = new ClientReadRequest()
    .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
    .relation("viewer")
    ._object("document:roadmap");

// Find all relationship tuples where a certain user has a relationship as any relation to a certain document
var request = new ClientReadRequest()
    .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
    ._object("document:roadmap");

// Find all relationship tuples where a certain user is a viewer of any document
var request = new ClientReadRequest()
    .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
    .relation("viewer")
    ._object("document:");

// Find all relationship tuples where any user has a relationship as any relation with a particular document
var request = new ClientReadRequest()
    ._object("document:roadmap");

// Read all stored relationship tuples
var request = new ClientReadRequest();

var options = new ClientReadOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    .pageSize(10)
    .continuationToken("...");

var response = fgaClient.read(request, options).get();

// In all the above situations, the response will be of the form:
// response = { tuples: [{ key: { user, relation, object }, timestamp }, ...]}
Write (Create and Delete) Relationship Tuples

Create and/or delete relationship tuples to update the system state.

API Documentation

Passing ClientWriteOptions is optional. All fields of ClientWriteOptions are optional.

Transaction mode (default)

By default, write runs in a transaction mode where any invalid operation (deleting a non-existing tuple, creating an existing tuple, one of the tuples was invalid) or a server error will fail the entire operation.

var request = new ClientWriteRequest()
    .writes(List.of(
        new TupleKey()
            .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
            .relation("viewer")
            ._object("document:roadmap"),
        new TupleKey()
            .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
            .relation("viewer")
            ._object("document:budget")
    ))
    .deletes(List.of(
        new TupleKey()
            .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
            .relation("writer")
            ._object("document:roadmap")
    ));

var options = new ClientWriteOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    // You can rely on the model id set in the configuration or override it for this specific request
    .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1")
    .disableTransactions(false);

var response = fgaClient.write(request, options).get();

Convenience WriteTuples and DeleteTuples methods are also available.

Non-transaction mode

The SDK will split the writes into separate requests and send them sequentially to avoid violating rate limits.

Passing ClientWriteOptions with .disableTransactions(true) is required to use non-transaction mode. All other fields of ClientWriteOptions are optional.

var request = new ClientWriteRequest()
    .writes(List.of(
        new ClientTupleKey()
            .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
            .relation("viewer")
            ._object("document:roadmap"),
        new ClientTupleKey()
            .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
            .relation("viewer")
            ._object("document:budget")
    ))
    .deletes(List.of(
        new ClientTupleKeyWithoutCondition()
            .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
            .relation("writer")
            ._object("document:roadmap")
    ));
var options = new ClientWriteOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    // You can rely on the model id set in the configuration or override it for this specific request
    .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1")
    .disableTransactions(true)
    .transactionChunkSize(5); // Maximum number of requests to be sent in a transaction in a particular chunk

var response = fgaClient.write(request, options).get();

Relationship Queries

Check

Check if a user has a particular relation with an object.

API Documentation

Passing ClientCheckOptions is optional. All fields of ClientCheckOptions are optional.

var request = new ClientCheckRequest()
    .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
    .relation("writer")
    ._object("document:roadmap");
var options = new ClientCheckOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    // You can rely on the model id set in the configuration or override it for this specific request
    .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1");

var response = fgaClient.check(request, options).get();
// response.getAllowed() = true
Batch Check

Run a set of checks. Batch Check will return allowed: false if it encounters an error, and will return the error in the body. If 429s or 5xxs are encountered, the underlying check will retry up to 15 times before giving up.

Passing ClientBatchCheckOptions is optional. All fields of ClientBatchCheckOptions are optional.

var request = List.of(
    new ClientCheckRequest()
        .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
        .relation("viewer")
        ._object("document:roadmap")
        .contextualTuples(List.of(
            new ClientTupleKey()
                .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
                .relation("editor")
                ._object("document:roadmap")
        )),
    new ClientCheckRequest()
        .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
        .relation("admin")
        ._object("document:roadmap"),
        .contextualTuples(List.of(
            new ClientTupleKey()
                .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
                .relation("editor")
                ._object("document:roadmap")
        )),
    new ClientCheckRequest()
        .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
        .relation("creator")
        ._object("document:roadmap"),
    new ClientCheckRequest()
        .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
        .relation("deleter")
        ._object("document:roadmap")
);
var options = new ClientBatchCheckOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    // You can rely on the model id set in the configuration or override it for this specific request
    .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1")
    .maxParallelRequests(5); // Max number of requests to issue in parallel, defaults to 10

var response = fgaClient.batchCheck(request, options).get();

/*
response.getResponses() = [{
  allowed: false,
  request: {
    user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
    relation: "viewer",
    _object: "document:roadmap",
    contextualTuples: [{
      user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
      relation: "editor",
      _object: "document:roadmap"
    }]
  }
}, {
  allowed: false,
  request: {
    user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
    relation: "admin",
    _object: "document:roadmap",
    contextualTuples: [{
      user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
      relation: "editor",
      _object: "document:roadmap"
    }]
  }
}, {
  allowed: false,
  request: {
    user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
    relation: "creator",
    _object: "document:roadmap",
  },
  error: <FgaError ...>
}, {
  allowed: true,
  request: {
    user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
    relation: "deleter",
    _object: "document:roadmap",
  }},
]
*/
Expand

Expands the relationships in userset tree format.

API Documentation

Passing ClientExpandOptions is optional. All fields of ClientExpandOptions are optional.

var request = new ClientExpandRequest()
    .relation("viewer")
    ._object("document:roadmap");
var options = new ClientExpandOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    // You can rely on the model id set in the configuration or override it for this specific request
    .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1");

var response = fgaClient.expand(request, options).get();

// response.getTree().getRoot() = {"name":"document:roadmap#viewer","leaf":{"users":{"users":["user:81684243-9356-4421-8fbf-a4f8d36aa31b","user:f52a4f7a-054d-47ff-bb6e-3ac81269988f"]}}}
List Objects

List the objects of a particular type a user has access to.

API Documentation

Passing ClientListObjectsOptions is optional. All fields of ClientListObjectsOptions are optional.

var request = new ClientListObjectsRequest()
    .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
    .relation("viewer")
    .type("document")
    .contextualTuples(List.of(
        new ClientTupleKey()
            .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
            .relation("writer")
            ._object("document:budget")
    ));
var options = new ClientListObjectsOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    // You can rely on the model id set in the configuration or override it for this specific request
    .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1");

var response = fgaClient.listObjects(request, options).get();

// response.getObjects() = ["document:roadmap"]
List Relations

List the relations a user has on an object.

Passing ClientListRelationsOptions is optional. All fields of ClientListRelationsOptions are optional.

var request = new ClientListRelationsRequest()
    .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
    ._object("document:roadmap")
    .relations(List.of("can_view", "can_edit", "can_delete", "can_rename"))
    .contextualTuples(List.of(
        new ClientTupleKey()
            .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
            .relation("editor")
            ._object("document:roadmap")
        )
    );
var options = new ClientListRelationsOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    // When unspecified, defaults to 10
    .maxParallelRequests()
    // You can rely on the model id set in the configuration or override it for this specific request
    .authorizationModelId(DEFAULT_AUTH_MODEL_ID);

var response = fgaClient.listRelations(request, options).get();

// response.getRelations() = ["can_view", "can_edit"]
List Users

List the users who have a certain relation to a particular type.

API Documentation

// Only a single filter is allowed for the time being
var userFilters = new ArrayList<UserTypeFilter>() {
    {
        add(new UserTypeFilter().type("user"));
        // user filters can also be of the form
        // add(new UserTypeFilter().type("team").relation("member"));
    }
};

var request = new ClientListUsersRequest()
    ._object(new FgaObject().type("document").id("roadmap"))
    .relation("can_read")
    .userFilters(userFilters)
    .context(Map.of("view_count", 100))
    .contextualTupleKeys(List.of(
        new ClientTupleKey()
            .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
            .relation("editor")
            ._object("folder:product"),
        new ClientTupleKey()
            .user("folder:product")
            .relation("parent")
            ._object("document:roadmap")
));

var options = new ClientListUsersOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    // You can rely on the model id set in the configuration or override it for this specific request
    .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1");

var response = fgaClient.listUsers(request, options).get();

// response.getUsers() = [{object: {type: "user", id: "81684243-9356-4421-8fbf-a4f8d36aa31b"}}, {userset: { type: "user" }}, ...]

Assertions

Read Assertions

Read assertions for a particular authorization model.

API Documentation

Passing ClientReadAssertionsOptions is optional. All fields of ClientReadAssertionsOptions are optional.

var options = new ClientReadAssertionsOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    // You can rely on the model id set in the configuration or override it for this specific request
    .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1");
var response = fgaClient.readAssertions(options).get();
Write Assertions

Update the assertions for a particular authorization model.

API Documentation

Passing ClientWriteAssertionsOptions is optional. All fields of ClientWriteAssertionsOptions are optional.

var options = new ClientWriteAssertionsOptions()
    .additionalHeaders(Map.of("Some-Http-Header", "Some value"))
    .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1");
var assertions = List.of(
    new ClientAssertion()
        .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
        .relation("viewer")
        ._object("document:roadmap")
        .expectation(true)
);
fgaClient.writeAssertions(assertions, options).get();

Retries

If a network request fails with a 429 or 5xx error from the server, the SDK will automatically retry the request up to 15 times with a minimum wait time of 100 milliseconds between each attempt.

To customize this behavior, call maxRetries and minimumRetryDelay on the ClientConfiguration builder. maxRetries determines the maximum number of retries (up to 15), while minimumRetryDelay sets the minimum wait time between retries in milliseconds.

import com.fasterxml.jackson.databind.ObjectMapper;
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import java.net.http.HttpClient;

public class Example {
    public static void main(String[] args) throws Exception {
        var config = new ClientConfiguration()
                .apiUrl(System.getenv("FGA_API_URL")) // If not specified, will default to "http://localhost:8080"
                .storeId(System.getenv("FGA_STORE_ID")) // Not required when calling createStore() or listStores()
                .authorizationModelId(System.getenv("FGA_MODEL_ID")) // Optional, can be overridden per request
                .maxRetries(3) // retry up to 3 times on API requests
                .minimumRetryDelay(250); // wait a minimum of 250 milliseconds between requests

        var fgaClient = new OpenFgaClient(config);
        var response = fgaClient.readAuthorizationModels().get();
    }
}

API Endpoints

Method HTTP request Description
check POST /stores/{store_id}/check Check whether a user is authorized to access an object
createStore POST /stores Create a store
deleteStore DELETE /stores/{store_id} Delete a store
expand POST /stores/{store_id}/expand Expand all relationships in userset tree format, and following userset rewrite rules. Useful to reason about and debug a certain relationship
getStore GET /stores/{store_id} Get a store
listObjects POST /stores/{store_id}/list-objects List all objects of the given type that the user has a relation with
listStores GET /stores List all stores
listUsers POST /stores/{store_id}/list-users List the users matching the provided filter who have a certain relation to a particular type.
read POST /stores/{store_id}/read Get tuples from the store that matches a query, without following userset rewrite rules
readAssertions GET /stores/{store_id}/assertions/{authorization_model_id} Read assertions for an authorization model ID
readAuthorizationModel GET /stores/{store_id}/authorization-models/{id} Return a particular version of an authorization model
readAuthorizationModels GET /stores/{store_id}/authorization-models Return all the authorization models for a particular store
readChanges GET /stores/{store_id}/changes Return a list of all the tuple changes
write POST /stores/{store_id}/write Add or delete tuples from the store
writeAssertions PUT /stores/{store_id}/assertions/{authorization_model_id} Upsert assertions for an authorization model ID
writeAuthorizationModel POST /stores/{store_id}/authorization-models Create a new authorization model

Models

OpenTelemetry

This SDK supports producing metrics that can be consumed as part of an OpenTelemetry setup. For more information, please see the documentation

Contributing

Issues

If you have found a bug or if you have a feature request, please report them on the sdk-generator repo issues section. Please do not report security vulnerabilities on the public GitHub issue tracker.

Pull Requests

All changes made to this repo will be overwritten on the next generation, so we kindly ask that you send all pull requests related to the SDKs to the sdk-generator repo instead.

Author

OpenFGA

License

This project is licensed under the Apache-2.0 license. See the LICENSE file for more info.

The code in this repo was auto generated by OpenAPI Generator from a template based on the Java template, licensed under the Apache License 2.0.

java-sdk's People

Contributors

adriantam avatar booniepepper avatar dependabot[bot] avatar didier-simplecommedev avatar eddumelendez avatar evansims avatar ewanharris avatar jimmyjames avatar le-yams avatar paulosuzart avatar rhamzeh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

java-sdk's Issues

At Present the error messages does not contain any details.

Please do not report security vulnerabilities here. See the Responsible Disclosure Program.

Thank you in advance for helping us to improve this library! Please read through the template below and answer all relevant questions. Your additional work here is greatly appreciated and will help us respond as quickly as possible.

By submitting an issue to this repository, you agree to the terms within the OpenFGA Code of Conduct.

Description

The OpenFGA java sdk at present does not return any error messages with proper types. It's only returning FgaApiValidationError. java sdk default error log has so little information. It just prints method which is not very helpful to find error.
Caused by: dev.openfga.sdk.errors.FgaApiValidationError: check

At present there is no way to identify such errors. It would be really helpful if these error codes are returned in the response from SDK.

Version of SDK

v0.4.2

Version of OpenFGA (if known)

v1.1.0

OpenFGA Flags/Custom Configuration Applicable

environment:
- OPENFGA_DATASTORE_ENGINE=postgres
- OPENFGA_DATASTORE_URI=postgres://postgres:password@postgres:5432/postgres?sslmode=disable
- OPENFGA_TRACE_ENABLED=true
- OPENFGA_TRACE_SAMPLE_RATIO=1
- OPENFGA_TRACE_OTLP_ENDPOINT=otel-collector:4317
- OPENFGA_METRICS_ENABLE_RPC_HISTOGRAMS=true

Reproduction

Detail the steps taken to reproduce this error, what was expected, and whether this issue can be reproduced consistently or if it is intermittent.

  1. Initialize OpenFgaClient with openfga_sdk.ClientConfiguration parameter api_host=127.0.0.1, credentials method client_credentials
  2. Invoke method read_authorization_models
  3. See exception thrown

Sample Code the Produces Issues

<code snippet>

Backtrace (if applicable)

<backtrace>

Expected behavior

A clear and concise description of what you expected to happen.

Additional context

Add any other context about the problem here.

The argument types of write api and delete api are inconsistent.

Checklist

  • I have looked into the README and have not found a suitable solution or answer.
  • I have looked into the documentation and have not found a suitable solution or answer.
  • I have searched the issues and have not found a suitable solution or answer.
  • I have upgraded to the latest version of OpenFGA and the issue still persists.
  • I have searched the Slack community and have not found a suitable solution or answer.
  • I agree to the terms within the OpenFGA Code of Conduct.

Description

I am using an object I created with the ClientTupleKey type for both write operation and delete operation, but I encounter a type error for delete operation.

The method deletes(List) in the type ClientWriteRequest is not applicable for the arguments (List) Java(67108979)

package dev.openfga.sdk.api.client.model;

import java.util.List;

public class ClientWriteRequest {
   private List<ClientTupleKey> writes;
   private List<ClientTupleKeyWithoutCondition> deletes;

   public ClientWriteRequest() {
   }

   public static ClientWriteRequest ofWrites(List<ClientTupleKey> writes) {
      return (new ClientWriteRequest()).writes(writes);
   }

   public ClientWriteRequest writes(List<ClientTupleKey> writes) {
      this.writes = writes;
      return this;
   }

   public List<ClientTupleKey> getWrites() {
      return this.writes;
   }

   public static ClientWriteRequest ofDeletes(List<ClientTupleKeyWithoutCondition> deletes) {
      return (new ClientWriteRequest()).deletes(deletes);
   }

   public ClientWriteRequest deletes(List<ClientTupleKeyWithoutCondition> deletes) {
      this.deletes = deletes;
      return this;
   }

   public List<ClientTupleKeyWithoutCondition> getDeletes() {
      return this.deletes;
   }
}

Expectation

The arguments of the delete api must have the same type as the arguments of the write api.

Reproduction

        ClientWriteRequest client = new ClientWriteRequest();

        List<ClientTupleKey> tuples = new ArrayList<ClientTupleKey>();

        tuples.add(new ClientTupleKey()
                .user("parent_group:" + event.getEventUserId())
                .relation("assignee")
                ._object("group:" + event.getEventUserId()));
        tuples.add(new ClientTupleKey()
                .user("member_group:" + event.getEventUserId())
                .relation("assignee")
                ._object("group:" + event.getEventUserId()));

        if(event.isEventUserChildOfObject()){
            tuples.add(new ClientTupleKey()
                    .user("parent_group:" + event.getEventUserId())
                    .relation("parent")
                    ._object("parent_group:" + event.getEventObjectId()));

            tuples.add(new ClientTupleKey()
                    .user("member_group:" + event.getEventObjectId())
                    .relation("member")
                    ._object("member_group:" + event.getEventUserId()));
        }

        if(event.isWriteOperation()) {
            client.writes(tuples);
        }
        else if(event.isDeleteOperation()) {
            client.deletes(tuples);
        }

OpenFGA SDK version

0.5.0

OpenFGA version

1.5.7

SDK Configuration

Logs

No response

References

No response

Feature Request: Exception Handling for OpenFGA Client Errors (e.g., TupleAlreadyExistsException)

Overview

When working with the OpenFGA client, errors such as ExecutionException with FgaApiValidationError as the cause make it difficult to pinpoint specific issues. For example, writing a tuple that already exists throws a generic error, requiring manual parsing of the underlying message to identify the cause.

Problem

Currently, developers must manually parse the error message to determine the specific cause, like a duplicate tuple. This approach adds complexity and can lead to mistakes when handling multiple error types.

Proposed Solution

Introduce custom exceptions (e.g., TupleAlreadyExistsException, InvalidTupleFormatException) that allow developers to handle specific errors directly without parsing error messages.

Benefits

  • Simplifies error handling by catching specific exceptions.
  • Improves code readability and maintainability.
  • Reduces the risk of incorrect error handling due to manual parsing.

Current Workaround

FgaApiValidationError error = (FgaApiValidationError) e.getCause();
String responseData = error.getResponseData();

ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> responseMap = null;
try {
    responseMap = objectMapper.readValue(responseData, new TypeReference<>() {});
} catch (JsonProcessingException ex) {
    throw new CustomException("Failed to parse response from openfga");
}

String message = (String) responseMap.get("message");
if (message.startsWith("cannot write a tuple which already exists:")) return;

Improved Solution

With custom exceptions like TupleAlreadyExistsException, the error handling could be simplified:

try {
    // Write tuple
} catch (ExecutionException e) {
    TupleAlreadyExistsException cause =e.getCause();
}

example application does not work with local SDK as documented

Checklist

  • I have looked into the README and have not found a suitable solution or answer.
  • I have looked into the documentation and have not found a suitable solution or answer.
  • I have searched the issues and have not found a suitable solution or answer.
  • I have upgraded to the latest version of OpenFGA and the issue still persists.
  • I have searched the Slack community and have not found a suitable solution or answer.
  • I agree to the terms within the OpenFGA Code of Conduct.

Description

When following the documentation to run the example with a local version of the SDK, an error occurs when running make.

Expectation

It should be possible to run the example with a local unpublished version of the SDK

Reproduction

Update the example build.gradle as shown in the example README:

dependencies {
    // implementation("dev.openfga:openfga-sdk:0.4.+")
    implementation project(path: ':')

    // ...etc
}

Run make, and observe the error:

FAILURE: Build failed with an exception.

* What went wrong:
Circular dependency between the following tasks:
:classes
\--- :compileJava
     +--- :compileKotlin
     |    \--- :jar
     |         +--- :classes (*)
     |         +--- :compileJava (*)
     |         \--- :compileKotlin (*)
     \--- :jar (*)

I don't know that trying to reference the SDK project is possible given that the repository is not organized as a gradle multi-project. I suspect the error is that the implementation project(path: ':') is adding a dependency to itself (: path is the project root). Even a hard-coded path will fail because the example project is not associated with the SDK project.

It may be easiest to update the documentation to publish the SDK to the local maven repository, and then use the mavenLocal repository.

OpenFGA SDK version

0.4.1

OpenFGA version

latest

SDK Configuration

n/a

Logs

n/a

References

No response

Export Metrics

Checklist

Describe the problem you'd like to have solved

As a consumer of the SDK, I would like to hook it to my dashboards to get data on several metrics.

Describe the ideal solution

Integrate the OpenTelemetry Java API package https://github.com/open-telemetry/opentelemetry-java/tree/main/api

We need to be able to expose the metrics as outlined in the JS and Python SDKs

Alternatives and current workarounds

No response

References

No response

Additional context

No response

OAuth2 client credentials are sent with wrong Content-Type

Description

When an OAuth2 access token is requested from an OAuth2 Authorization server's token endpoint, the credentials are sent using the application/json Content-Type, but client credentials should be sent using the application/x-www-form-urlencoded according to the OAuth 2.0 RFC

Version of SDK

v0.3.1

Reproduction

  1. Configure the openfga client using the OAuth2 Credentials example in the README
  2. Observe that the content-type is set to application/json

Expected behavior

When an OAuth2 token is requested from an OAuth2 Authorization server, the credentials are sent using the application/x-www-form-urlencoded content-type as specified in the OAuth 2.0 RFC

A new thread is being created for every OpenFgaClient call

Description

A new HttpClient is being created for each request, leading to an increasing number of threads. Specifying a thread pool alleviates the problem, but not completely. The issue happens because here a new HttpClient is built everytime.

Version of SDK

v0.3.1

Reproduction

Below is an example of how to reproduce the issue.

  1. Initialize OpenFgaClient with any set of configs;
  2. Invoke method OpenFgaClient#write(ClientWriteRequest request) multiple times;
  3. Notice that for every write call, a new thread is created. If you have 300k write calls, 300k threads are created, eventually running out of memory.

Expected behavior

It should not create a new thread for every request.

In the non-Txn Write, we should be catching the errors and wrapping them

Checklist

  • I have looked into the README and have not found a suitable solution or answer.
  • I have looked into the documentation and have not found a suitable solution or answer.
  • I have searched the issues and have not found a suitable solution or answer.
  • I have upgraded to the latest version of OpenFGA and the issue still persists.
  • I have searched the Slack community and have not found a suitable solution or answer.
  • I agree to the terms within the OpenFGA Code of Conduct.

Description

In the non-txn Write, we should not error if a single tuple had a validation error

https://github.com/openfga/java-sdk/blob/b1e03e523c530824f5921313c2191dc5f6d93af8/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java#L427C34-L427C99

Expectation

We should match what the other SDKs are doing, e.g.

Catching the response if failed and setting status: ClientWriteStatus.FAILURE for the individual failed tuples

Reproduction

Have the following tuple already written

- user: user:81684243-9356-4421-8fbf-a4f8d36aa31b
  relation: viewer
  object: document:roadmap

and then do:

var request = new ClientWriteRequest()
    .writes(List.of(
        new ClientTupleKey()
            .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
            .relation("viewer")
            ._object("document:roadmap"),
        new ClientTupleKey()
            .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
            .relation("viewer")
            ._object("document:budget")
    ));
var options = new ClientWriteOptions()
    .disableTransactions(true)
    .transactionChunkSize(1); // Maximum number of requests to be sent in a transaction in a particular chunk

var response = fgaClient.write(request, options).get();

OpenFGA SDK version

v0.5.0

OpenFGA version

N/A

SDK Configuration

N/A

Logs

No response

References

No response

Document & Reduce Minimum Required Java Version

Describe the problem you'd like to have solved

I'm currently evaluating a number of solutions (OpenFGA, Ory Keto, Permify, etc...) to be used in building a new permissions system for a large enterprise client.

As a part of the evaluation, I'm looking to ensure that the chosen solution has support for Java 8. The client is unable to update to a newer JDK, at least for now.

I didn't see the minimum required JDK version listed in the readme, or in the release notes. After looking through the Gradle-related files, it looks like the minimum required version is Java 11 and that it has been set to this since the first commit.

Describe the ideal solution

  1. For the readme to be updated to include the minimum required Java version. A good spot may be near the line "It currently supports PostgreSQL 14 and MySQL 8.".
  2. If possible, for the minimum required Java version to be set to Java 8.

PHP SDK client

Checklist

Describe the problem you'd like to have solved

The lack of a PHP SDK client for this project limits integration options for PHP-based applications

Describe the ideal solution

Hello,

I'm currently working with your project and find it very useful. I was wondering if there are any plans to develop a PHP SDK client in the near future.

A PHP SDK would greatly benefit developers who primarily work with PHP-based applications, making it easier to integrate your project into their existing systems.

If there are no current plans for a PHP SDK, would you be open to community contributions in this area? I'd be interested in knowing your thoughts on this and any potential roadmap for SDK development.

Thank you for your time and consideration.

Alternatives and current workarounds

No response

References

No response

Additional context

No response

Intermittent FgaApiAuthenticationError when using Oauth2 Client Credentials

Checklist

  • I have looked into the README and have not found a suitable solution or answer.
  • I have looked into the documentation and have not found a suitable solution or answer.
  • I have searched the issues and have not found a suitable solution or answer.
  • I have upgraded to the latest version of OpenFGA and the issue still persists.
  • I have searched the Slack community and have not found a suitable solution or answer.
  • I agree to the terms within the OpenFGA Code of Conduct.

Description

I have observed FgaApiAuthenticationError is thrown intermittently when using OAuth2 client credentials. This occurs most frequently after a period of inactivity where the OpenFgaClient is not used for a long period of time, or at an intervals roughly equal to the OAuth2 access token's expires_in value. I think there may be an issue with the way that OAuth2 tokens are refreshed before expiry.

Expectation

I expect for OAuth2 access tokens to be refreshed automatically before being sent to the openfga api. An expired token should never be sent to the openfga api and the openFgaClient does not intermittently throw FgaApiAuthenticationError

Reproduction

Given:

Given I set up an OpenFgaClient using the Oauth2 Credentials example in the README

        var config = new ClientConfiguration()
                .apiUrl(System.getenv("FGA_API_URL")) // If not specified, will default to "http://localhost:8080"
                .storeId(System.getenv("FGA_STORE_ID")) // Not required when calling createStore() or listStores()
                .authorizationModelId(System.getenv("FGA_AUTHORIZATION_MODEL_ID")) // Optional, can be overridden per request
                .credentials(new Credentials(
                    new ClientCredentials()
                            .apiTokenIssuer(System.getenv("FGA_API_TOKEN_ISSUER"))
                            .scopes(System.getenv("FGA_API_SCOPES")) // optional space separated scopes
                            .clientId(System.getenv("FGA_CLIENT_ID"))
                            .clientSecret(System.getenv("FGA_CLIENT_SECRET"))
                ));

        var openFgaClient = new OpenFgaClient(config);

When:
When I make an openfga check request using the openfgaClient set up using clientCredentials

openFgaClient.check(checkRequest)

Then:

  • The openFgaClient always sends a fresh OAuth2 access token to the openfga server.
  • The openFgaClient ensures that the OAuth2 access token is refreshed before being sent if it is expired
  • The openFgaClient does not intermittently throw FgaApiAuthenticationError

OpenFGA SDK version

0.4.1

OpenFGA version

1.3.3

SDK Configuration

OAuth2 Client Credentials

Logs

dev.openfga.sdk.errors.FgaApiAuthenticationError: check
  at dev.openfga.sdk.errors.FgaError.getError(FgaError.java:49)
  at dev.openfga.sdk.api.client.HttpRequestAttempt.lambda$attemptHttpRequest$2(HttpRequestAttempt.java:56)
  at java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1150)
  at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
  at java.util.concurrent.CompletableFuture.postFire(CompletableFuture.java:614)
  at java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:844)
  at java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:482)
  at java.lang.Thread.run(Thread.java:833)

References

No response

Unable to retrieve all tuples

Description

Unable to retrieve list of tuples using empty ClientReadRequest. The read method always creates a TupleKey whether you pass in null or and empty ClientReadRequest. This triggers a validation error when calling the api

I am attempting to simulate this request
curl --location --request POST 'http://localhost:8080/stores//read'

Version of SDK

v0.2.1

Version of OpenFGA (if known)

v1.3.4

OpenFGA Flags/Custom Configuration Applicable

environment:
- OPENFGA_DATASTORE_ENGINE=mysql
- OPENFGA_DATASTORE_URI=:@tcp(mysql:3306)/fga?parseTime=true
- OPENFGA_LOG_FORMAT=json

Reproduction

The error occurs every time when attempting to read all tuples. When you create an empty ClientReadRequest with "new ClientReadRequest()" and call the read method, you will recieve the following error:
{
"code": "validation_error",
"message": "the 'tuple_key' field was provided but the object type field is required and both the object id and user cannot be empty"
}

Sample Code the Produces Issues

ClientReadRequest request = new ClientReadRequest();
ClientReadOptions options = (new ClientReadOptions()).pageSize(10);
CompletableFuture future = client.read(request, options);

Expected behavior

You should recieve a response listing the tuples on the server
The exact same behavior as "curl --location --request POST 'http://localhost:8080/stores//read'"

Additional context

This issue is caused by the read method in the API. It always creates a TupleKey and adds it to the body of the request even if the ClientReadRequest is empty. It should leave the body empty and not create a TupleKey

`AccessToken#isValid()` incorrectly returns false

Description

The isValid() check of AccessToken incorrectly returns false for a valid token, causing an unnecessary fetch of a new token.

Expected behavior

AccessToken#isValid() should not return false for a valid non-expired token, and a new token should not be fetched.

Version of SDK

v0.3.1

Reproduction

  1. Initialize the OpenFgaClient with ClientCredentials
  2. Invoke any API call at least twice
  3. Observe that AccessToken#isValid() returns false, even though the token expiry is in the future, causing a new token to be retrieved.

Sample code to reproduce

// Create a new configuration with ClientCredentials
var config = new ClientConfiguration()
        .apiUrl(FGA_API_URL)
        .storeId(FGA_STORE_ID)
        .credentials(new Credentials(
                new ClientCredentials()
                        .apiTokenIssuer(FGA_API_TOKEN_ISSUER)
                        .apiAudience(FGA_API_AUDIENCE)
                        .clientId(FGA_CLIENT_ID)
                        .clientSecret(FGA_CLIENT_SECRET)
        )
);

var fgaClient = new OpenFgaClient(config);

var checkRequest = new ClientCheckRequest()
    .user("user")
    .relation("relation")
    ._object("obj");
    
var r1 = client.check(checkRequest).get();
// make another request, observe that AccessToken#isValid() returns false even though the expiry is in the future
var r2 = client.check(checkRequest).get();

Expected behavior

AccessToken#isValid() should not return false for a non-expired token, and a new token should not be unnecessarily fetched.

Allow specifying OAuth Token URL in ClientCredentials

Describe the problem you'd like to have solved

You can specify apiTokenIssuer in ClientCredentials but the token path always defaults to "/oauth/token". That may not always be true as for me it's /oauth2/token.

I need to create a separate OAuth client and manage token manually.

Describe the ideal solution

Replace or add a new field in ClientCredentials to accept apiTokenURL (the entire path) or optionally add a field to allow overriding DEFAULT_API_TOKEN_ISSUER_PATH in OAuth2Client.

Alternatives and current workarounds

Create a separate OAuth client and manage token manually.

Additional context

None.

In the client, the transaction modes are flipped

Please do not report security vulnerabilities here. See the Responsible Disclosure Program.

Thank you in advance for helping us to improve this library! Please read through the template below and answer all relevant questions. Your additional work here is greatly appreciated and will help us respond as quickly as possible.

By submitting an issue to this repository, you agree to the terms within the OpenFGA Code of Conduct.

Description

The OpenFgaClient in the Java SDK has a flipped implementation of non transactional writes.

Implementation is flipped: https://github.com/openfga/java-sdk/blob/main/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java#L366-L431
Tests are flipped: https://github.com/openfga/java-sdk/blob/main/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java#L1109-L1328

Discord Thread

Version of SDK

v0.3.2

Version of OpenFGA (if known)

All versions

OpenFGA Flags/Custom Configuration Applicable

N/A

Reproduction

Sample Code the Produces Issues

Backtrace (if applicable)

Expected behavior

A clear and concise description of what you expected to happen.

Setting disableTransaction to true should send multiple requests, setting it to false (default), should send everything in the same request

Additional context

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.