Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,26 @@
import dev.openfga.sdk.api.model.WriteRequestWrites;
import java.util.Map;

/**
* Options for controlling the behavior of write operations on the OpenFGA client.
*
* <p>Use this class to configure how tuples are written, including whether to use transactions,
* how writes are chunked, and how duplicate or missing tuples are handled.
*/
public class ClientWriteOptions implements AdditionalHeadersSupplier {
private Map<String, String> additionalHeaders;
private String authorizationModelId;
private Boolean disableTransactions = false;
private boolean transactionsEnabled = true;
private int transactionChunkSize;
private WriteRequestWrites.OnDuplicateEnum onDuplicate;
private WriteRequestDeletes.OnMissingEnum onMissing;

/**
* Sets additional HTTP headers to include in write requests.
*
* @param additionalHeaders a map of header names to values
* @return this {@code ClientWriteOptions} instance for method chaining
*/
public ClientWriteOptions additionalHeaders(Map<String, String> additionalHeaders) {
this.additionalHeaders = additionalHeaders;
return this;
Expand All @@ -22,47 +34,139 @@ public Map<String, String> getAdditionalHeaders() {
return this.additionalHeaders;
}

/**
* Sets the authorization model ID to use for write operations.
*
* <p>If not set, the client will use the authorization model ID from its configuration.
*
* @param authorizationModelId the authorization model ID
* @return this {@code ClientWriteOptions} instance for method chaining
*/
public ClientWriteOptions authorizationModelId(String authorizationModelId) {
this.authorizationModelId = authorizationModelId;
return this;
}

/**
* Returns the authorization model ID configured for write operations.
*
* @return the authorization model ID, or {@code null} if not set
*/
public String getAuthorizationModelId() {
return authorizationModelId;
}

/**
* Sets whether transactions should be used when writing tuples.
*
* <p>When {@code true}, all writes and deletes are sent as a single atomic request — if any
* tuple fails, the entire operation is rolled back. When {@code false}, tuples are split into
* chunks and sent as separate requests, each chunk atomic on its own but with no atomicity
* guarantee across the full set. Chunk size is controlled by {@link #transactionChunkSize(int)}.
*
* @param enabled {@code true} to enable transactions (default), {@code false} to disable them
* @return this {@code ClientWriteOptions} instance for method chaining
* @see #isTransactionsEnabled()
*/
public ClientWriteOptions transactions(boolean enabled) {
this.transactionsEnabled = enabled;
return this;
}

/**
* Returns whether transactions are enabled for write operations.
*
* @return {@code true} if transactions are enabled (default), {@code false} if disabled
* @see #transactions(boolean)
*/
public boolean isTransactionsEnabled() {
return transactionsEnabled;
}

/**
* Sets whether transactions should be disabled when writing tuples.
*
* @param disableTransactions {@code true} to disable transactions, {@code false} to enable them
* @return this {@code ClientWriteOptions} instance for method chaining
*/
public ClientWriteOptions disableTransactions(boolean disableTransactions) {
this.disableTransactions = disableTransactions;
this.transactionsEnabled = !disableTransactions;
return this;
}

/**
* Returns whether transactions are disabled for write operations.
*
* @return {@code true} if transactions are disabled, {@code false} if enabled (default)
*/
public boolean disableTransactions() {
return disableTransactions != null && disableTransactions;
return !transactionsEnabled;
}

/**
* Sets the number of tuples to include in each non-transactional write chunk.
*
* <p>Only applies when transactions are disabled via {@link #transactions(boolean)}. Writes
* and deletes are split into chunks of this size and sent as separate requests.
*
* @param transactionChunkSize the chunk size; must be greater than zero
* @return this {@code ClientWriteOptions} instance for method chaining
* @see #transactions(boolean)
*/
public ClientWriteOptions transactionChunkSize(int transactionChunkSize) {
this.transactionChunkSize = transactionChunkSize;
return this;
}

/**
* Returns the chunk size for non-transactional writes.
*
* <p>Defaults to {@code 1} if not explicitly set.
*
* @return the configured chunk size, or {@code 1} if none was set
*/
public int getTransactionChunkSize() {
return transactionChunkSize > 0 ? transactionChunkSize : 1;
Comment on lines +112 to 129

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Enforce the chunk-size contract or relax the Javadoc.

Line 112 says the value “must be greater than zero”, but transactionChunkSize(int) accepts 0/negative values and getTransactionChunkSize() silently converts them to 1. That makes the public contract inaccurate and hides misconfiguration.

Suggested fix
 public ClientWriteOptions transactionChunkSize(int transactionChunkSize) {
+    if (transactionChunkSize <= 0) {
+        throw new IllegalArgumentException("transactionChunkSize must be greater than zero");
+    }
     this.transactionChunkSize = transactionChunkSize;
     return this;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* @param transactionChunkSize the chunk size; must be greater than zero
* @return this {@code ClientWriteOptions} instance for method chaining
* @see #transactions(boolean)
*/
public ClientWriteOptions transactionChunkSize(int transactionChunkSize) {
this.transactionChunkSize = transactionChunkSize;
return this;
}
/**
* Returns the chunk size for non-transactional writes.
*
* <p>Defaults to {@code 1} if not explicitly set.
*
* @return the configured chunk size, or {@code 1} if none was set
*/
public int getTransactionChunkSize() {
return transactionChunkSize > 0 ? transactionChunkSize : 1;
* `@param` transactionChunkSize the chunk size; must be greater than zero
* `@return` this {`@code` ClientWriteOptions} instance for method chaining
* `@see` `#transactions`(boolean)
*/
public ClientWriteOptions transactionChunkSize(int transactionChunkSize) {
if (transactionChunkSize <= 0) {
throw new IllegalArgumentException("transactionChunkSize must be greater than zero");
}
this.transactionChunkSize = transactionChunkSize;
return this;
}
/**
* Returns the chunk size for non-transactional writes.
*
* <p>Defaults to {`@code` 1} if not explicitly set.
*
* `@return` the configured chunk size, or {`@code` 1} if none was set
*/
public int getTransactionChunkSize() {
return transactionChunkSize > 0 ? transactionChunkSize : 1;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/dev/openfga/sdk/api/configuration/ClientWriteOptions.java`
around lines 112 - 129, The ClientWriteOptions transactionChunkSize contract is
inconsistent: transactionChunkSize(int) currently allows zero/negative values
and getTransactionChunkSize() masks them by returning 1. Update the setter to
enforce the documented “greater than zero” requirement by rejecting invalid
values (or, if you prefer the fallback behavior, relax the Javadoc to match it)
and keep getTransactionChunkSize() aligned with that public contract.

}

/**
* Sets the behavior when a duplicate tuple is encountered during a write.
*
* @param onDuplicate the action to take on duplicate tuples
* @return this {@code ClientWriteOptions} instance for method chaining
* @see WriteRequestWrites.OnDuplicateEnum
*/
public ClientWriteOptions onDuplicate(WriteRequestWrites.OnDuplicateEnum onDuplicate) {
this.onDuplicate = onDuplicate;
return this;
}

/**
* Returns the configured behavior for duplicate tuples during writes.
*
* @return the on-duplicate action, or {@code null} if not set
*/
public WriteRequestWrites.OnDuplicateEnum getOnDuplicate() {
return onDuplicate;
}

/**
* Sets the behavior when a tuple to be deleted is not found.
*
* @param onMissing the action to take when a tuple is missing during delete
* @return this {@code ClientWriteOptions} instance for method chaining
* @see WriteRequestDeletes.OnMissingEnum
*/
public ClientWriteOptions onMissing(WriteRequestDeletes.OnMissingEnum onMissing) {
this.onMissing = onMissing;
return this;
}

/**
* Returns the configured behavior for missing tuples during deletes.
*
* @return the on-missing action, or {@code null} if not set
*/
public WriteRequestDeletes.OnMissingEnum getOnMissing() {
return onMissing;
}
Expand Down
Loading