Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ jobs:
- name: Run All Tests
run: |-
make test-integration-client-java
env:
OPEN_API_REF: 0ac19aac54f21f3c78970126b84b4c69c6e3b9a2

- name: Check for SDK changes
run: |
Expand Down
70 changes: 70 additions & 0 deletions config/clients/java/template/README_calling_api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,76 @@ var options = new ClientWriteOptions()
var response = fgaClient.write(request, options).get();
```

###### Conflict options for write operations

Write conflict handling can be controlled using the `onDuplicate` option for writes and the `onMissing` option for deletes.

> Note: this requires OpenFGA [v1.10.0](https://github.com/openfga/openfga/releases/tag/v1.10.0) or later.

- `onDuplicate`: Controls behavior when attempting to create a tuple that already exists
- `WriteRequestWrites.OnDuplicateEnum.ERROR` (default): Return an error
- `WriteRequestWrites.OnDuplicateEnum.IGNORE`: Skip the duplicate tuple and continue

- `onMissing`: Controls behavior when attempting to delete a tuple that doesn't exist
- `WriteRequestDeletes.OnMissingEnum.ERROR` (default): Return an error
- `WriteRequestDeletes.OnMissingEnum.IGNORE`: Skip the missing tuple and continue

**Using conflict options with the `write()` method:**

```java
var request = new ClientWriteRequest()
.writes(List.of(
new ClientTupleKey()
.user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
.relation("viewer")
._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a")
))
.deletes(List.of(
new ClientTupleKeyWithoutCondition()
.user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
.relation("writer")
._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a")
));

var options = new ClientWriteOptions()
.onDuplicate(WriteRequestWrites.OnDuplicateEnum.IGNORE)
.onMissing(WriteRequestDeletes.OnMissingEnum.IGNORE);

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

**Using conflict options with the `writeTuples()` convenience method:**

```java
var tuples = List.of(
new ClientTupleKey()
.user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
.relation("viewer")
._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a")
);

var options = new ClientWriteTuplesOptions()
.onDuplicate(WriteRequestWrites.OnDuplicateEnum.IGNORE);

var response = fgaClient.writeTuples(tuples, options).get();
```

**Using conflict options with the `deleteTuples()` convenience method:**

```java
var tuples = List.of(
new ClientTupleKeyWithoutCondition()
.user("user:81684243-9356-4421-8fbf-a4f8d36aa31b")
.relation("writer")
._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a")
);

var options = new ClientDeleteTuplesOptions()
.onMissing(WriteRequestDeletes.OnMissingEnum.IGNORE);

var response = fgaClient.deleteTuples(tuples, options).get();
```

#### Relationship Queries

##### Check
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,12 +469,14 @@ public class OpenFgaClient {

var writeTuples = request.getWrites();
if (writeTuples != null && !writeTuples.isEmpty()) {
body.writes(ClientTupleKey.asWriteRequestWrites(writeTuples));
var onDuplicate = options != null ? options.getOnDuplicate() : null;
body.writes(ClientTupleKey.asWriteRequestWrites(writeTuples, onDuplicate));
}

var deleteTuples = request.getDeletes();
if (deleteTuples != null && !deleteTuples.isEmpty()) {
body.deletes(ClientTupleKeyWithoutCondition.asWriteRequestDeletes(deleteTuples));
var onMissing = options != null ? options.getOnMissing() : null;
body.deletes(ClientTupleKeyWithoutCondition.asWriteRequestDeletes(deleteTuples, onMissing));
}

if (options != null && !isNullOrWhitespace(options.getAuthorizationModelId())) {
Expand Down Expand Up @@ -677,7 +679,8 @@ public class OpenFgaClient {

var body = new WriteRequest();

body.writes(ClientTupleKey.asWriteRequestWrites(tupleKeys));
var onDuplicate = options != null ? options.getOnDuplicate() : null;
body.writes(ClientTupleKey.asWriteRequestWrites(tupleKeys, onDuplicate));

String authorizationModelId = configuration.getAuthorizationModelId();
if (!isNullOrWhitespace(authorizationModelId)) {
Expand Down Expand Up @@ -717,7 +720,8 @@ public class OpenFgaClient {

var body = new WriteRequest();

body.deletes(ClientTupleKeyWithoutCondition.asWriteRequestDeletes(tupleKeys));
var onMissing = options != null ? options.getOnMissing() : null;
body.deletes(ClientTupleKeyWithoutCondition.asWriteRequestDeletes(tupleKeys, onMissing));

String authorizationModelId = configuration.getAuthorizationModelId();
if (!isNullOrWhitespace(authorizationModelId)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,19 @@ public class ClientTupleKey extends ClientTupleKeyWithoutCondition {
}

public static WriteRequestWrites asWriteRequestWrites(Collection<ClientTupleKey> tupleKeys) {
return new WriteRequestWrites()
return asWriteRequestWrites(tupleKeys, null);
}

public static WriteRequestWrites asWriteRequestWrites(
Collection<ClientTupleKey> tupleKeys, WriteRequestWrites.OnDuplicateEnum onDuplicate) {
WriteRequestWrites writes = new WriteRequestWrites()
.tupleKeys(tupleKeys.stream()
.map(ClientTupleKey::asTupleKey)
.collect(Collectors.toList()));
if (onDuplicate != null) {
writes.onDuplicate(onDuplicate);
}
return writes;
}

/* Overrides for correct typing */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@ public class ClientTupleKeyWithoutCondition {
}

public static WriteRequestDeletes asWriteRequestDeletes(Collection<ClientTupleKeyWithoutCondition> tupleKeys) {
return new WriteRequestDeletes()
return asWriteRequestDeletes(tupleKeys, null);
}

public static WriteRequestDeletes asWriteRequestDeletes(
Collection<ClientTupleKeyWithoutCondition> tupleKeys, WriteRequestDeletes.OnMissingEnum onMissing) {
WriteRequestDeletes deletes = new WriteRequestDeletes()
.tupleKeys(tupleKeys.stream()
.map(ClientTupleKeyWithoutCondition::asTupleKeyWithoutCondition)
.collect(Collectors.toList()));
if (onMissing != null) {
deletes.onMissing(onMissing);
}
return deletes;
}

public ClientTupleKeyWithoutCondition _object(String _object) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{{>licenseInfo}}
package {{configPackage}};

import dev.openfga.sdk.api.model.WriteRequestDeletes;
import java.util.Map;

public class ClientDeleteTuplesOptions implements AdditionalHeadersSupplier {
private Map<String, String> additionalHeaders;
private WriteRequestDeletes.OnMissingEnum onMissing;

public ClientDeleteTuplesOptions additionalHeaders(Map<String, String> additionalHeaders) {
this.additionalHeaders = additionalHeaders;
Expand All @@ -15,4 +17,13 @@ public class ClientDeleteTuplesOptions implements AdditionalHeadersSupplier {
public Map<String, String> getAdditionalHeaders() {
return this.additionalHeaders;
}

public ClientDeleteTuplesOptions onMissing(WriteRequestDeletes.OnMissingEnum onMissing) {
this.onMissing = onMissing;
return this;
}

public WriteRequestDeletes.OnMissingEnum getOnMissing() {
return onMissing;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
{{>licenseInfo}}
package {{configPackage}};

import dev.openfga.sdk.api.model.WriteRequestDeletes;
import dev.openfga.sdk.api.model.WriteRequestWrites;
import java.util.Map;

public class ClientWriteOptions implements AdditionalHeadersSupplier {
private Map<String, String> additionalHeaders;
private String authorizationModelId;
private Boolean disableTransactions = false;
private int transactionChunkSize;
private WriteRequestWrites.OnDuplicateEnum onDuplicate;
private WriteRequestDeletes.OnMissingEnum onMissing;

public ClientWriteOptions additionalHeaders(Map<String, String> additionalHeaders) {
this.additionalHeaders = additionalHeaders;
Expand Down Expand Up @@ -45,4 +49,22 @@ public class ClientWriteOptions implements AdditionalHeadersSupplier {
public int getTransactionChunkSize() {
return transactionChunkSize > 0 ? transactionChunkSize : 1;
}

public ClientWriteOptions onDuplicate(WriteRequestWrites.OnDuplicateEnum onDuplicate) {
this.onDuplicate = onDuplicate;
return this;
}

public WriteRequestWrites.OnDuplicateEnum getOnDuplicate() {
return onDuplicate;
}

public ClientWriteOptions onMissing(WriteRequestDeletes.OnMissingEnum onMissing) {
this.onMissing = onMissing;
return this;
}

public WriteRequestDeletes.OnMissingEnum getOnMissing() {
return onMissing;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{{>licenseInfo}}
package {{configPackage}};

import dev.openfga.sdk.api.model.WriteRequestWrites;
import java.util.Map;

public class ClientWriteTuplesOptions implements AdditionalHeadersSupplier {
private Map<String, String> additionalHeaders;
private WriteRequestWrites.OnDuplicateEnum onDuplicate;

public ClientWriteTuplesOptions additionalHeaders(Map<String, String> additionalHeaders) {
this.additionalHeaders = additionalHeaders;
Expand All @@ -15,4 +17,13 @@ public class ClientWriteTuplesOptions implements AdditionalHeadersSupplier {
public Map<String, String> getAdditionalHeaders() {
return this.additionalHeaders;
}

public ClientWriteTuplesOptions onDuplicate(WriteRequestWrites.OnDuplicateEnum onDuplicate) {
this.onDuplicate = onDuplicate;
return this;
}

public WriteRequestWrites.OnDuplicateEnum getOnDuplicate() {
return onDuplicate;
}
}
Loading
Loading