From c0a7f5b764c30d64b2f79f8f85668289d5501bb0 Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Fri, 31 Oct 2025 17:07:12 -0400 Subject: [PATCH 1/9] feat: support write conflict options for SDKs JS and CLI will be added when available --- .../content/getting-started/update-tuples.mdx | 20 +++- .../Docs/SnippetViewer/WriteRequestViewer.tsx | 93 ++++++++++++++----- 2 files changed, 88 insertions(+), 25 deletions(-) diff --git a/docs/content/getting-started/update-tuples.mdx b/docs/content/getting-started/update-tuples.mdx index a44c177a60..d6cab72309 100644 --- a/docs/content/getting-started/update-tuples.mdx +++ b/docs/content/getting-started/update-tuples.mdx @@ -257,11 +257,17 @@ For example, if you want to ensure `user:anne` has `reader` access to `document: object: 'document:Z' }, ]} - writeOptions={{ - on_duplicate: 'ignore', + conflictOptions={{ + onDuplicateWrites: 'ignore', }} skipSetup={true} allowedLanguages={[ + // SupportedLanguage.JS_SDK, + SupportedLanguage.GO_SDK, + SupportedLanguage.DOTNET_SDK, + SupportedLanguage.PYTHON_SDK, + SupportedLanguage.JAVA_SDK, + // SupportedLanguage.CLI, SupportedLanguage.CURL, ]} /> @@ -281,13 +287,19 @@ Similarly, you can use `on_missing: "ignore"` when deleting tuples that might no object: 'document:Z' }, ]} - deleteOptions={{ - on_missing: 'ignore', + conflictOptions={{ + onMissingDeletes: 'ignore', }} expectedResponse={{ data: {}, }} allowedLanguages={[ + // SupportedLanguage.JS_SDK, + SupportedLanguage.GO_SDK, + SupportedLanguage.DOTNET_SDK, + SupportedLanguage.PYTHON_SDK, + SupportedLanguage.JAVA_SDK, + // SupportedLanguage.CLI, SupportedLanguage.CURL, ]} /> diff --git a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx index e236b36ec5..125b4410f5 100644 --- a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx @@ -1,7 +1,7 @@ -import { getFilteredAllowedLangs, SupportedLanguage, DefaultAuthorizationModelId } from './SupportedLanguage'; -import { defaultOperationsViewer } from './DefaultTabbedViewer'; import assertNever from 'assert-never/index'; import { RelationshipCondition } from '@components/Docs/RelationshipTuples'; +import { getFilteredAllowedLangs, SupportedLanguage, DefaultAuthorizationModelId } from './SupportedLanguage'; +import { defaultOperationsViewer } from './DefaultTabbedViewer'; interface RelationshipTuple { user: string; @@ -25,11 +25,9 @@ interface WriteRequestViewerOpts { isDelete?: boolean; skipSetup?: boolean; allowedLanguages?: SupportedLanguage[]; - writeOptions?: { - on_duplicate?: 'error' | 'ignore'; - }; - deleteOptions?: { - on_missing?: 'error' | 'ignore'; + conflictOptions?: { + onDuplicateWrites?: 'error' | 'ignore'; + onMissingDeletes?: 'error' | 'ignore'; }; } @@ -88,8 +86,8 @@ ${ return cleanTuple; }), }; - if (opts.writeOptions?.on_duplicate) { - requestBody.writes.on_duplicate = opts.writeOptions.on_duplicate; + if (opts.conflictOptions?.onDuplicateWrites) { + requestBody.writes.on_duplicate = opts.conflictOptions.onDuplicateWrites; } } @@ -101,8 +99,8 @@ ${ return cleanTuple; }), }; - if (opts.deleteOptions?.on_missing) { - requestBody.deletes.on_missing = opts.deleteOptions.on_missing; + if (opts.conflictOptions?.onMissingDeletes) { + requestBody.deletes.on_missing = opts.conflictOptions.onMissingDeletes; } } @@ -209,9 +207,19 @@ await fgaClient.write({ }`; return ` -options := ClientWriteOptions{ - AuthorizationModelId: PtrString("${modelId}"), -} +options := ClientWriteOptions{${modelId ? `\n AuthorizationModelId: openfga.PtrString("${modelId}"),` : ''}${ + opts.conflictOptions + ? `\n Conflict: ClientWriteConflictOptions{${ + opts.conflictOptions.onDuplicateWrites + ? `\n OnDuplicateWrites: CLIENT_WRITE_REQUEST_ON_DUPLICATE_WRITES_${opts.conflictOptions.onDuplicateWrites.toUpperCase()},` + : '' + }${ + opts.conflictOptions.onMissingDeletes + ? `\n OnMissingDeletes: CLIENT_WRITE_REQUEST_ON_MISSING_DELETES_${opts.conflictOptions.onMissingDeletes.toUpperCase()},` + : '' + }\n},` + : '' + }\n} body := ClientWriteRequest{${opts.relationshipTuples ? writes : ''}${opts.deleteRelationshipTuples ? deletes : ''} } @@ -274,8 +282,23 @@ ${deleteTuples} }`; const separator = `${opts.deleteRelationshipTuples && opts.relationshipTuples ? ',\n ' : ''}`; return ` -var options = new ClientWriteOptions { - AuthorizationModelId = "${modelId}", +var options = new ClientWriteOptions {${modelId ? `\n AuthorizationModelId = ${modelId},` : ''}${ + opts.conflictOptions + ? ` + Conflict = new ConflictOptions {${ + opts.conflictOptions.onDuplicateWrites + ? ` + OnDuplicateWrites = OnDuplicateWrites.${opts.conflictOptions.onDuplicateWrites.charAt(0).toUpperCase() + opts.conflictOptions.onDuplicateWrites.slice(1)},` + : '' + }${ + opts.conflictOptions.onMissingDeletes + ? ` + OnMissingDeletes = OnMissingDeletes.${opts.conflictOptions.onMissingDeletes.charAt(0).toUpperCase() + opts.conflictOptions.onMissingDeletes.slice(1)}` + : '' + } + }` + : '' + } }; var body = new ClientWriteRequest() { ${opts.relationshipTuples ? writes : ''}${separator}${opts.deleteRelationshipTuples ? deletes : ''}, @@ -327,9 +350,23 @@ ${_description ? ` # ${_description}\n ` : const deletes = ` deletes=[${deleteTuples} ],`; - return `options = { - "authorization_model_id": "${modelId}" -} + return `options = {${modelId ? `\n "authorization_model_id": "${modelId}"` : ''}${ + opts.conflictOptions + ? `, + "conflict": ConflictOptions(${ + opts.conflictOptions.onDuplicateWrites + ? ` + on_duplicate_writes=ClientWriteRequestOnDuplicateWrites.${opts.conflictOptions.onDuplicateWrites.toUpperCase()},` + : '' + }${ + opts.conflictOptions.onMissingDeletes + ? ` + on_missing_deletes=ClientWriteRequestOnMissingDeletes.${opts.conflictOptions.onMissingDeletes.toUpperCase()}` + : '' + } + )` + : '' + }\n} body = ClientWriteRequest( ${opts.relationshipTuples ? writes : ''}${opts.deleteRelationshipTuples ? deletes : ''} ) @@ -423,8 +460,22 @@ ${opts.deleteRelationshipTuples ? deletes : ''}`; .deletes(List.of(${deleteTuples} ))`; - return `var options = new ClientWriteOptions() - .authorizationModelId("${modelId}"); + return `var options = new ClientWriteOptions()${ + modelId + ? ` + .authorizationModelId("${modelId}")` + : '' + }${ + opts.conflictOptions?.onDuplicateWrites + ? ` + .onDuplicate(WriteRequestWrites.OnDuplicateEnum.${opts.conflictOptions.onDuplicateWrites.toUpperCase()})` + : '' + }${ + opts.conflictOptions?.onMissingDeletes + ? ` + .onMissing(WriteRequestDeletes.OnMissingEnum.${opts.conflictOptions.onMissingDeletes.toUpperCase()})` + : '' + }; var body = new ClientWriteRequest()${opts.relationshipTuples ? writes : ' '}${ opts.deleteRelationshipTuples ? deletes : '' From f2e8305f3cf894c1f8510ca4e4e4f8e4fba34756 Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Tue, 4 Nov 2025 10:52:56 -0500 Subject: [PATCH 2/9] feat: added CLI write conflict options --- docs/content/getting-started/update-tuples.mdx | 4 ++-- .../Docs/SnippetViewer/WriteRequestViewer.tsx | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/content/getting-started/update-tuples.mdx b/docs/content/getting-started/update-tuples.mdx index d6cab72309..05eb69ed69 100644 --- a/docs/content/getting-started/update-tuples.mdx +++ b/docs/content/getting-started/update-tuples.mdx @@ -267,7 +267,7 @@ For example, if you want to ensure `user:anne` has `reader` access to `document: SupportedLanguage.DOTNET_SDK, SupportedLanguage.PYTHON_SDK, SupportedLanguage.JAVA_SDK, - // SupportedLanguage.CLI, + SupportedLanguage.CLI, SupportedLanguage.CURL, ]} /> @@ -299,7 +299,7 @@ Similarly, you can use `on_missing: "ignore"` when deleting tuples that might no SupportedLanguage.DOTNET_SDK, SupportedLanguage.PYTHON_SDK, SupportedLanguage.JAVA_SDK, - // SupportedLanguage.CLI, + SupportedLanguage.CLI, SupportedLanguage.CURL, ]} /> diff --git a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx index 125b4410f5..7980a738cb 100644 --- a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx @@ -48,6 +48,10 @@ function writeRequestViewer(lang: SupportedLanguage, opts: WriteRequestViewerOpt tuple.condition.context, )}'` : '' + } ${ + opts.conflictOptions?.onDuplicateWrites + ? `--on-duplicate ${opts.conflictOptions.onDuplicateWrites}` + : '' }`, ) .join('\n') @@ -57,7 +61,11 @@ function writeRequestViewer(lang: SupportedLanguage, opts: WriteRequestViewerOpt ${ opts.deleteRelationshipTuples?.length ? opts.deleteRelationshipTuples - .map((tuple) => `fga tuple delete --store-id=\${FGA_STORE_ID} ${tuple.user} ${tuple.relation} ${tuple.object}`) + .map((tuple) => `fga tuple delete --store-id=\${FGA_STORE_ID} ${tuple.user} ${tuple.relation} ${tuple.object} ${ + opts.conflictOptions?.onMissingDeletes + ? `--on-missing ${opts.conflictOptions.onMissingDeletes}` + : '' + }`) .join('\n') : '' }`; From 233001a09c1080c495c9baad4fdf6e55b88f61c5 Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Tue, 4 Nov 2025 12:04:06 -0500 Subject: [PATCH 3/9] chore: run lint --- docusaurus.config.js | 2 +- .../Docs/AuthorizationModel/SyntaxTransformer.ts | 2 +- .../Docs/SnippetViewer/CheckRequestViewer.tsx | 8 ++++---- .../Docs/SnippetViewer/ExpandRequestViewer.tsx | 8 ++++---- .../Docs/SnippetViewer/ListObjectsRequestViewer.tsx | 6 +++--- .../Docs/SnippetViewer/ListUsersRequestViewer.tsx | 6 +++--- .../Docs/SnippetViewer/ReadChangesRequestViewer.tsx | 4 ++-- .../Docs/SnippetViewer/ReadRequestViewer.tsx | 12 ++++++------ src/components/Docs/SnippetViewer/SdkSetup.tsx | 10 +++++----- .../Docs/SnippetViewer/WriteAuthzModelViewer.tsx | 2 +- .../Docs/SnippetViewer/WriteRequestViewer.tsx | 4 ++-- src/components/SwaggerUI/swagger-ui.tsx | 2 +- 12 files changed, 33 insertions(+), 33 deletions(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index 9f810124da..0e84083875 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -60,7 +60,7 @@ const config = { : 'https://raw.githubusercontent.com/openfga/api/main/docs/openapiv2/apidocs.swagger.json', // Customization for product information - /* eslint-disable max-len */ + description: `OpenFGA is an open source Fine-Grained Authorization solution based on Google's Zanzibar.`, productName: `OpenFGA`, // link to product description section (relative to baseURL) diff --git a/src/components/Docs/AuthorizationModel/SyntaxTransformer.ts b/src/components/Docs/AuthorizationModel/SyntaxTransformer.ts index 75475e3033..4ddd00ee1e 100644 --- a/src/components/Docs/AuthorizationModel/SyntaxTransformer.ts +++ b/src/components/Docs/AuthorizationModel/SyntaxTransformer.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { AuthorizationModel, TypeDefinition } from '@openfga/sdk'; import { transformer } from '@openfga/syntax-transformer'; diff --git a/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx b/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx index e43fdbbd42..4d6c43dbb4 100644 --- a/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx @@ -64,7 +64,7 @@ ${ # Response: {"allowed":${allowed}}`; case SupportedLanguage.CURL: - /* eslint-disable max-len */ + return `curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/check \\ -H "Authorization: Bearer $FGA_API_TOKEN" \\ # Not needed if service does not require authorization -H "content-type: application/json" \\${ @@ -83,7 +83,7 @@ ${ }${context ? `,"context":${JSON.stringify(context)}}` : '}'}' # Response: {"allowed":${allowed}}`; - /* eslint-enable max-len */ + case SupportedLanguage.JS_SDK: return ` // Run a check @@ -109,7 +109,7 @@ const { allowed } = await fgaClient.check({ // allowed = ${allowed}`; case SupportedLanguage.GO_SDK: - /* eslint-disable no-tabs */ + return ` options := ClientCheckOptions{${ modelId @@ -314,7 +314,7 @@ var response = fgaClient.check(body, options).get(); default: assertNever(lang); } - /* eslint-enable no-tabs */ + } export function CheckRequestViewer(opts: CheckRequestViewerOpts): JSX.Element { diff --git a/src/components/Docs/SnippetViewer/ExpandRequestViewer.tsx b/src/components/Docs/SnippetViewer/ExpandRequestViewer.tsx index cde483c78a..c09fa14e12 100644 --- a/src/components/Docs/SnippetViewer/ExpandRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/ExpandRequestViewer.tsx @@ -14,12 +14,12 @@ function expandRequestViewer(lang: SupportedLanguage, opts: ExpandRequestViewerO const modelId = opts.authorizationModelId ? opts.authorizationModelId : DefaultAuthorizationModelId; switch (lang) { case SupportedLanguage.CLI: - // eslint-disable-next-line max-len + return `fga query expand --store-id=\${FGA_STORE_ID} --model-id=${modelId} ${opts.relation} ${opts.object} # Response: {"tree": ...}`; case SupportedLanguage.CURL: - // eslint-disable-next-line max-len + return `curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/expand \\ -H "Authorization: Bearer $FGA_API_TOKEN" \\ # Not needed if service does not require authorization -H "content-type: application/json" \\ @@ -38,7 +38,7 @@ const { tree } = await fgaClient.expand({ // tree = ...`; case SupportedLanguage.GO_SDK: - /* eslint-disable no-tabs */ + return ` options := ClientExpandOptions{ AuthorizationModelId: PtrString("${modelId}"), @@ -54,7 +54,7 @@ data, err := fgaClient.Expand(context.Background()). // data = { tree: ...}`; - /* eslint-enable no-tabs */ + case SupportedLanguage.DOTNET_SDK: return ` var options = new ClientCheckOptions { diff --git a/src/components/Docs/SnippetViewer/ListObjectsRequestViewer.tsx b/src/components/Docs/SnippetViewer/ListObjectsRequestViewer.tsx index 87fa69baf1..59dd545c48 100644 --- a/src/components/Docs/SnippetViewer/ListObjectsRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/ListObjectsRequestViewer.tsx @@ -35,7 +35,7 @@ function listObjectsRequestViewer(lang: SupportedLanguage, opts: ListObjectsRequ # Response: {"objects": [${expectedResults.map((r) => `"${r}"`).join(', ')}]}`; case SupportedLanguage.CURL: - /* eslint-disable max-len */ + return `curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/list-objects \\ -H "Authorization: Bearer $FGA_API_TOKEN" \\ # Not needed if service does not require authorization -H "content-type: application/json" \\ @@ -66,7 +66,7 @@ function listObjectsRequestViewer(lang: SupportedLanguage, opts: ListObjectsRequ # Response: {"objects": [${expectedResults.map((r) => `"${r}"`).join(', ')}]}`; - /* eslint-enable max-len */ + case SupportedLanguage.JS_SDK: return `const response = await fgaClient.listObjects({ user: "${user}", @@ -97,7 +97,7 @@ function listObjectsRequestViewer(lang: SupportedLanguage, opts: ListObjectsRequ }); // response.objects = [${expectedResults.map((r) => `"${r}"`).join(', ')}]`; case SupportedLanguage.GO_SDK: - /* eslint-disable no-tabs */ + return `options := ClientListObjectsOptions{ AuthorizationModelId: PtrString("${modelId}"), } diff --git a/src/components/Docs/SnippetViewer/ListUsersRequestViewer.tsx b/src/components/Docs/SnippetViewer/ListUsersRequestViewer.tsx index e822a8572a..3469145df3 100644 --- a/src/components/Docs/SnippetViewer/ListUsersRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/ListUsersRequestViewer.tsx @@ -48,7 +48,7 @@ function listUsersRequestViewer(lang: SupportedLanguage, opts: ListUsersRequestV # Response: ${response}`; case SupportedLanguage.CURL: - /* eslint-disable max-len */ + return `curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/list-users \\ -H "Authorization: Bearer $FGA_API_TOKEN" \\ # Not needed if service does not require authorization -H "content-type: application/json" \\ @@ -91,7 +91,7 @@ function listUsersRequestViewer(lang: SupportedLanguage, opts: ListUsersRequestV # Response: ${response}`; - /* eslint-enable max-len */ + case SupportedLanguage.JS_SDK: return `const response = await fgaClient.listUsers({ object: { @@ -132,7 +132,7 @@ function listUsersRequestViewer(lang: SupportedLanguage, opts: ListUsersRequestV }); // response.users = [${expectedResults.users.map((u) => JSON.stringify(u)).join(',')}]`; case SupportedLanguage.GO_SDK: - /* eslint-disable no-tabs */ + return `options := ClientListUsersOptions{ AuthorizationModelId: PtrString("${modelId}"), } diff --git a/src/components/Docs/SnippetViewer/ReadChangesRequestViewer.tsx b/src/components/Docs/SnippetViewer/ReadChangesRequestViewer.tsx index 3f79621810..c57538906d 100644 --- a/src/components/Docs/SnippetViewer/ReadChangesRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/ReadChangesRequestViewer.tsx @@ -37,14 +37,14 @@ function readChangesRequestViewer(lang: SupportedLanguage, opts: ReadChangesRequ const tokenString = `${ 'var continuationToken = "' + (opts.continuationToken ? opts.continuationToken : '') + '";\n' }`; - // eslint-disable-next-line max-len + const pageSizeString = `${'var pageSize = ' + (opts.pageSize ? opts.pageSize : '') + ';\n'}`; return `${typeString}${tokenString}${pageSizeString} await fgaClient.readChanges({ type }, { pageSize, continuationToken });`; } case SupportedLanguage.GO_SDK: { - // eslint-disable-next-line max-len + return `options := ClientReadChangesOptions{${ opts.pageSize ? `\n\tPageSize: PtrInt32(${opts.pageSize}),\n` : '' }${opts.continuationToken ? `\n\tContinuationToken: PtrString("${opts.continuationToken}"),\n` : ''}} diff --git a/src/components/Docs/SnippetViewer/ReadRequestViewer.tsx b/src/components/Docs/SnippetViewer/ReadRequestViewer.tsx index 2977339948..bc16f0fcbc 100644 --- a/src/components/Docs/SnippetViewer/ReadRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/ReadRequestViewer.tsx @@ -23,7 +23,7 @@ function readRequestViewer(lang: SupportedLanguage, opts: ReadRequestViewerOpts) ? opts.tuples .map( ({ user, relation, object }) => - // eslint-disable-next-line max-len + `{"key": {"user":"${user}","relation":"${relation}","object":"${object}"}, "timestamp": "2021-10-06T15:32:11.128Z"}`, ) .join(',') @@ -47,7 +47,7 @@ function readRequestViewer(lang: SupportedLanguage, opts: ReadRequestViewerOpts) -d '{"tuple_key":{${requestTuples}}}` : ''; - // eslint-disable-next-line max-len + return `curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/read \\ -H "Authorization: Bearer $FGA_API_TOKEN" \\ # Not needed if service does not require authorization -H "content-type: application/json" ${requestTuplePayload}' @@ -78,7 +78,7 @@ const { tuples } = await fgaClient.read({ `\tObject: PtrString("${opts.object}"),\n` : ''; - /* eslint-disable no-tabs */ + return `options := ClientReadOptions{} body := ClientReadRequest{ ${requestTuples} @@ -99,7 +99,7 @@ data, err := fgaClient.Read(context.Background()). ` Object = "${opts.object}",` : ''; - /* eslint-disable no-tabs */ + return `var options = new ClientReadOptions {} var body = new ClientReadRequest() { ${requestTuples} @@ -140,7 +140,7 @@ response = await fga_client.read(body, options) ? opts.tuples .map( ({ user, relation, object }) => - // eslint-disable-next-line max-len + `{"key": {"user":"${user}","relation":"${relation}","object":"${object}"}, "timestamp": "2021-10-06T15:32:11.128Z"}`, ) .join(',') @@ -172,7 +172,7 @@ var response = fgaClient.read(body).get(); default: assertNever(lang); } - /* eslint-enable no-tabs */ + } export function ReadRequestViewer(opts: ReadRequestViewerOpts): JSX.Element { diff --git a/src/components/Docs/SnippetViewer/SdkSetup.tsx b/src/components/Docs/SnippetViewer/SdkSetup.tsx index 790ab8a9b8..67123137e6 100644 --- a/src/components/Docs/SnippetViewer/SdkSetup.tsx +++ b/src/components/Docs/SnippetViewer/SdkSetup.tsx @@ -30,7 +30,7 @@ export function importSdkStatement(lang: SupportedLanguage, languageMappings: La return getMapping(lang, languageMappings).importStatement; } -/* eslint-disable max-len */ + export function sdkSetupHeader(lang: SupportedLanguage, languageMappings: LanguageMappings) { switch (lang) { case SupportedLanguage.CURL: @@ -50,7 +50,7 @@ const fgaClient = new ${languageMappings['js'].apiName}({ });`; case SupportedLanguage.GO_SDK: - /* eslint-disable no-tabs */ + return `import ( "os" @@ -70,7 +70,7 @@ func main() { } }`; - /* eslint-enable no-tabs */ + case SupportedLanguage.DOTNET_SDK: return `// import the SDK ${importSdkStatement(lang, languageMappings)} @@ -89,7 +89,7 @@ class Example { var fgaClient = new ${languageMappings['js'].apiName}(configuration); } }`; - /* eslint-enable no-tabs */ + case SupportedLanguage.PYTHON_SDK: return ` ${importSdkStatement(lang, languageMappings)} @@ -131,7 +131,7 @@ public class Example { } } -/* eslint-enable max-len */ + export function SdkSetupHeader({ lang }: { lang: SupportedLanguage }): JSX.Element { const { siteConfig } = useDocusaurusContext(); const configuredLanguage = siteConfig.customFields.languageMapping as LanguageMappings; diff --git a/src/components/Docs/SnippetViewer/WriteAuthzModelViewer.tsx b/src/components/Docs/SnippetViewer/WriteAuthzModelViewer.tsx index e17e33db23..6b5b19ede5 100644 --- a/src/components/Docs/SnippetViewer/WriteAuthzModelViewer.tsx +++ b/src/components/Docs/SnippetViewer/WriteAuthzModelViewer.tsx @@ -31,7 +31,7 @@ const { authorization_model_id: id } = await fgaClient.writeAuthorizationModel($ } function writeAuthZModelViewerGo(authorizationModel: AuthorizationModel): string { - /* eslint-disable no-tabs */ + return ` var writeAuthorizationModelRequestString = ${JSON.stringify(JSON.stringify(authorizationModel))} var body WriteAuthorizationModelRequest diff --git a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx index 7980a738cb..1ac4e754b7 100644 --- a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx @@ -155,7 +155,7 @@ await fgaClient.write({ } case SupportedLanguage.GO_SDK: { - /* eslint-disable no-tabs */ + const writeTuples = opts.relationshipTuples ? opts.relationshipTuples .map( @@ -422,7 +422,7 @@ response = await fga_client.write(body, options) return `${opts.relationshipTuples ? writes : ''}${separator} ${opts.deleteRelationshipTuples ? deletes : ''}`; - /* eslint-enable no-tabs */ + } case SupportedLanguage.JAVA_SDK: { diff --git a/src/components/SwaggerUI/swagger-ui.tsx b/src/components/SwaggerUI/swagger-ui.tsx index 224572a2a2..4a9cc49cb6 100644 --- a/src/components/SwaggerUI/swagger-ui.tsx +++ b/src/components/SwaggerUI/swagger-ui.tsx @@ -8,7 +8,7 @@ import './swagger-ui-feature-override.css'; const DisableAuthorizePlugin = function () { return { wrapComponents: { - // eslint-disable-next-line react/display-name + authorizeBtn: () => () => null, }, }; From 01f3c743bbef29051f7ef6710ae416882e891bb4 Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Tue, 4 Nov 2025 12:05:53 -0500 Subject: [PATCH 4/9] chore: run prettier --- .../Docs/AuthorizationModel/SyntaxTransformer.ts | 2 -- .../Docs/SnippetViewer/CheckRequestViewer.tsx | 5 +---- .../Docs/SnippetViewer/ExpandRequestViewer.tsx | 4 ---- .../Docs/SnippetViewer/ListObjectsRequestViewer.tsx | 4 +--- .../Docs/SnippetViewer/ListUsersRequestViewer.tsx | 4 +--- .../Docs/SnippetViewer/ReadChangesRequestViewer.tsx | 3 +-- .../Docs/SnippetViewer/ReadRequestViewer.tsx | 6 ------ src/components/Docs/SnippetViewer/SdkSetup.tsx | 6 +----- .../Docs/SnippetViewer/WriteAuthzModelViewer.tsx | 1 - .../Docs/SnippetViewer/WriteRequestViewer.tsx | 13 ++++++------- src/components/SwaggerUI/swagger-ui.tsx | 1 - 11 files changed, 11 insertions(+), 38 deletions(-) diff --git a/src/components/Docs/AuthorizationModel/SyntaxTransformer.ts b/src/components/Docs/AuthorizationModel/SyntaxTransformer.ts index 4ddd00ee1e..df69f8a47f 100644 --- a/src/components/Docs/AuthorizationModel/SyntaxTransformer.ts +++ b/src/components/Docs/AuthorizationModel/SyntaxTransformer.ts @@ -1,5 +1,3 @@ - - import { AuthorizationModel, TypeDefinition } from '@openfga/sdk'; import { transformer } from '@openfga/syntax-transformer'; import { constants } from '@openfga/frontend-utils'; diff --git a/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx b/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx index 4d6c43dbb4..324534e45f 100644 --- a/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx @@ -64,7 +64,6 @@ ${ # Response: {"allowed":${allowed}}`; case SupportedLanguage.CURL: - return `curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/check \\ -H "Authorization: Bearer $FGA_API_TOKEN" \\ # Not needed if service does not require authorization -H "content-type: application/json" \\${ @@ -83,7 +82,7 @@ ${ }${context ? `,"context":${JSON.stringify(context)}}` : '}'}' # Response: {"allowed":${allowed}}`; - + case SupportedLanguage.JS_SDK: return ` // Run a check @@ -109,7 +108,6 @@ const { allowed } = await fgaClient.check({ // allowed = ${allowed}`; case SupportedLanguage.GO_SDK: - return ` options := ClientCheckOptions{${ modelId @@ -314,7 +312,6 @@ var response = fgaClient.check(body, options).get(); default: assertNever(lang); } - } export function CheckRequestViewer(opts: CheckRequestViewerOpts): JSX.Element { diff --git a/src/components/Docs/SnippetViewer/ExpandRequestViewer.tsx b/src/components/Docs/SnippetViewer/ExpandRequestViewer.tsx index c09fa14e12..da392b3c86 100644 --- a/src/components/Docs/SnippetViewer/ExpandRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/ExpandRequestViewer.tsx @@ -14,12 +14,10 @@ function expandRequestViewer(lang: SupportedLanguage, opts: ExpandRequestViewerO const modelId = opts.authorizationModelId ? opts.authorizationModelId : DefaultAuthorizationModelId; switch (lang) { case SupportedLanguage.CLI: - return `fga query expand --store-id=\${FGA_STORE_ID} --model-id=${modelId} ${opts.relation} ${opts.object} # Response: {"tree": ...}`; case SupportedLanguage.CURL: - return `curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/expand \\ -H "Authorization: Bearer $FGA_API_TOKEN" \\ # Not needed if service does not require authorization -H "content-type: application/json" \\ @@ -38,7 +36,6 @@ const { tree } = await fgaClient.expand({ // tree = ...`; case SupportedLanguage.GO_SDK: - return ` options := ClientExpandOptions{ AuthorizationModelId: PtrString("${modelId}"), @@ -54,7 +51,6 @@ data, err := fgaClient.Expand(context.Background()). // data = { tree: ...}`; - case SupportedLanguage.DOTNET_SDK: return ` var options = new ClientCheckOptions { diff --git a/src/components/Docs/SnippetViewer/ListObjectsRequestViewer.tsx b/src/components/Docs/SnippetViewer/ListObjectsRequestViewer.tsx index 59dd545c48..23064930ec 100644 --- a/src/components/Docs/SnippetViewer/ListObjectsRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/ListObjectsRequestViewer.tsx @@ -35,7 +35,6 @@ function listObjectsRequestViewer(lang: SupportedLanguage, opts: ListObjectsRequ # Response: {"objects": [${expectedResults.map((r) => `"${r}"`).join(', ')}]}`; case SupportedLanguage.CURL: - return `curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/list-objects \\ -H "Authorization: Bearer $FGA_API_TOKEN" \\ # Not needed if service does not require authorization -H "content-type: application/json" \\ @@ -66,7 +65,7 @@ function listObjectsRequestViewer(lang: SupportedLanguage, opts: ListObjectsRequ # Response: {"objects": [${expectedResults.map((r) => `"${r}"`).join(', ')}]}`; - + case SupportedLanguage.JS_SDK: return `const response = await fgaClient.listObjects({ user: "${user}", @@ -97,7 +96,6 @@ function listObjectsRequestViewer(lang: SupportedLanguage, opts: ListObjectsRequ }); // response.objects = [${expectedResults.map((r) => `"${r}"`).join(', ')}]`; case SupportedLanguage.GO_SDK: - return `options := ClientListObjectsOptions{ AuthorizationModelId: PtrString("${modelId}"), } diff --git a/src/components/Docs/SnippetViewer/ListUsersRequestViewer.tsx b/src/components/Docs/SnippetViewer/ListUsersRequestViewer.tsx index 3469145df3..2e8f87ed87 100644 --- a/src/components/Docs/SnippetViewer/ListUsersRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/ListUsersRequestViewer.tsx @@ -48,7 +48,6 @@ function listUsersRequestViewer(lang: SupportedLanguage, opts: ListUsersRequestV # Response: ${response}`; case SupportedLanguage.CURL: - return `curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/list-users \\ -H "Authorization: Bearer $FGA_API_TOKEN" \\ # Not needed if service does not require authorization -H "content-type: application/json" \\ @@ -91,7 +90,7 @@ function listUsersRequestViewer(lang: SupportedLanguage, opts: ListUsersRequestV # Response: ${response}`; - + case SupportedLanguage.JS_SDK: return `const response = await fgaClient.listUsers({ object: { @@ -132,7 +131,6 @@ function listUsersRequestViewer(lang: SupportedLanguage, opts: ListUsersRequestV }); // response.users = [${expectedResults.users.map((u) => JSON.stringify(u)).join(',')}]`; case SupportedLanguage.GO_SDK: - return `options := ClientListUsersOptions{ AuthorizationModelId: PtrString("${modelId}"), } diff --git a/src/components/Docs/SnippetViewer/ReadChangesRequestViewer.tsx b/src/components/Docs/SnippetViewer/ReadChangesRequestViewer.tsx index c57538906d..6d8279a21d 100644 --- a/src/components/Docs/SnippetViewer/ReadChangesRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/ReadChangesRequestViewer.tsx @@ -37,14 +37,13 @@ function readChangesRequestViewer(lang: SupportedLanguage, opts: ReadChangesRequ const tokenString = `${ 'var continuationToken = "' + (opts.continuationToken ? opts.continuationToken : '') + '";\n' }`; - + const pageSizeString = `${'var pageSize = ' + (opts.pageSize ? opts.pageSize : '') + ';\n'}`; return `${typeString}${tokenString}${pageSizeString} await fgaClient.readChanges({ type }, { pageSize, continuationToken });`; } case SupportedLanguage.GO_SDK: { - return `options := ClientReadChangesOptions{${ opts.pageSize ? `\n\tPageSize: PtrInt32(${opts.pageSize}),\n` : '' }${opts.continuationToken ? `\n\tContinuationToken: PtrString("${opts.continuationToken}"),\n` : ''}} diff --git a/src/components/Docs/SnippetViewer/ReadRequestViewer.tsx b/src/components/Docs/SnippetViewer/ReadRequestViewer.tsx index bc16f0fcbc..7530f3ce70 100644 --- a/src/components/Docs/SnippetViewer/ReadRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/ReadRequestViewer.tsx @@ -23,7 +23,6 @@ function readRequestViewer(lang: SupportedLanguage, opts: ReadRequestViewerOpts) ? opts.tuples .map( ({ user, relation, object }) => - `{"key": {"user":"${user}","relation":"${relation}","object":"${object}"}, "timestamp": "2021-10-06T15:32:11.128Z"}`, ) .join(',') @@ -47,7 +46,6 @@ function readRequestViewer(lang: SupportedLanguage, opts: ReadRequestViewerOpts) -d '{"tuple_key":{${requestTuples}}}` : ''; - return `curl -X POST $FGA_API_URL/stores/$FGA_STORE_ID/read \\ -H "Authorization: Bearer $FGA_API_TOKEN" \\ # Not needed if service does not require authorization -H "content-type: application/json" ${requestTuplePayload}' @@ -78,7 +76,6 @@ const { tuples } = await fgaClient.read({ `\tObject: PtrString("${opts.object}"),\n` : ''; - return `options := ClientReadOptions{} body := ClientReadRequest{ ${requestTuples} @@ -99,7 +96,6 @@ data, err := fgaClient.Read(context.Background()). ` Object = "${opts.object}",` : ''; - return `var options = new ClientReadOptions {} var body = new ClientReadRequest() { ${requestTuples} @@ -140,7 +136,6 @@ response = await fga_client.read(body, options) ? opts.tuples .map( ({ user, relation, object }) => - `{"key": {"user":"${user}","relation":"${relation}","object":"${object}"}, "timestamp": "2021-10-06T15:32:11.128Z"}`, ) .join(',') @@ -172,7 +167,6 @@ var response = fgaClient.read(body).get(); default: assertNever(lang); } - } export function ReadRequestViewer(opts: ReadRequestViewerOpts): JSX.Element { diff --git a/src/components/Docs/SnippetViewer/SdkSetup.tsx b/src/components/Docs/SnippetViewer/SdkSetup.tsx index 67123137e6..4d4b36fdc9 100644 --- a/src/components/Docs/SnippetViewer/SdkSetup.tsx +++ b/src/components/Docs/SnippetViewer/SdkSetup.tsx @@ -30,7 +30,6 @@ export function importSdkStatement(lang: SupportedLanguage, languageMappings: La return getMapping(lang, languageMappings).importStatement; } - export function sdkSetupHeader(lang: SupportedLanguage, languageMappings: LanguageMappings) { switch (lang) { case SupportedLanguage.CURL: @@ -50,7 +49,6 @@ const fgaClient = new ${languageMappings['js'].apiName}({ });`; case SupportedLanguage.GO_SDK: - return `import ( "os" @@ -70,7 +68,6 @@ func main() { } }`; - case SupportedLanguage.DOTNET_SDK: return `// import the SDK ${importSdkStatement(lang, languageMappings)} @@ -89,7 +86,7 @@ class Example { var fgaClient = new ${languageMappings['js'].apiName}(configuration); } }`; - + case SupportedLanguage.PYTHON_SDK: return ` ${importSdkStatement(lang, languageMappings)} @@ -131,7 +128,6 @@ public class Example { } } - export function SdkSetupHeader({ lang }: { lang: SupportedLanguage }): JSX.Element { const { siteConfig } = useDocusaurusContext(); const configuredLanguage = siteConfig.customFields.languageMapping as LanguageMappings; diff --git a/src/components/Docs/SnippetViewer/WriteAuthzModelViewer.tsx b/src/components/Docs/SnippetViewer/WriteAuthzModelViewer.tsx index 6b5b19ede5..ccfb301b09 100644 --- a/src/components/Docs/SnippetViewer/WriteAuthzModelViewer.tsx +++ b/src/components/Docs/SnippetViewer/WriteAuthzModelViewer.tsx @@ -31,7 +31,6 @@ const { authorization_model_id: id } = await fgaClient.writeAuthorizationModel($ } function writeAuthZModelViewerGo(authorizationModel: AuthorizationModel): string { - return ` var writeAuthorizationModelRequestString = ${JSON.stringify(JSON.stringify(authorizationModel))} var body WriteAuthorizationModelRequest diff --git a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx index 1ac4e754b7..c004177f6a 100644 --- a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx @@ -61,11 +61,12 @@ function writeRequestViewer(lang: SupportedLanguage, opts: WriteRequestViewerOpt ${ opts.deleteRelationshipTuples?.length ? opts.deleteRelationshipTuples - .map((tuple) => `fga tuple delete --store-id=\${FGA_STORE_ID} ${tuple.user} ${tuple.relation} ${tuple.object} ${ - opts.conflictOptions?.onMissingDeletes - ? `--on-missing ${opts.conflictOptions.onMissingDeletes}` - : '' - }`) + .map( + (tuple) => + `fga tuple delete --store-id=\${FGA_STORE_ID} ${tuple.user} ${tuple.relation} ${tuple.object} ${ + opts.conflictOptions?.onMissingDeletes ? `--on-missing ${opts.conflictOptions.onMissingDeletes}` : '' + }`, + ) .join('\n') : '' }`; @@ -155,7 +156,6 @@ await fgaClient.write({ } case SupportedLanguage.GO_SDK: { - const writeTuples = opts.relationshipTuples ? opts.relationshipTuples .map( @@ -422,7 +422,6 @@ response = await fga_client.write(body, options) return `${opts.relationshipTuples ? writes : ''}${separator} ${opts.deleteRelationshipTuples ? deletes : ''}`; - } case SupportedLanguage.JAVA_SDK: { diff --git a/src/components/SwaggerUI/swagger-ui.tsx b/src/components/SwaggerUI/swagger-ui.tsx index 4a9cc49cb6..d737494866 100644 --- a/src/components/SwaggerUI/swagger-ui.tsx +++ b/src/components/SwaggerUI/swagger-ui.tsx @@ -8,7 +8,6 @@ import './swagger-ui-feature-override.css'; const DisableAuthorizePlugin = function () { return { wrapComponents: { - authorizeBtn: () => () => null, }, }; From 6a6d6175b53ed36ce6bf022b63ecf7d082401e9b Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Tue, 4 Nov 2025 12:40:43 -0500 Subject: [PATCH 5/9] fix: wrap modelID as a string Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/components/Docs/SnippetViewer/WriteRequestViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx index c004177f6a..0f017abf39 100644 --- a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx @@ -290,7 +290,7 @@ ${deleteTuples} }`; const separator = `${opts.deleteRelationshipTuples && opts.relationshipTuples ? ',\n ' : ''}`; return ` -var options = new ClientWriteOptions {${modelId ? `\n AuthorizationModelId = ${modelId},` : ''}${ +var options = new ClientWriteOptions {${modelId ? `\n AuthorizationModelId = "${modelId}",` : ''}${ opts.conflictOptions ? ` Conflict = new ConflictOptions {${ From d12f7aaedaac83700f7e7b56e8f10f096ce8cfdc Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Wed, 5 Nov 2025 16:20:12 -0500 Subject: [PATCH 6/9] feat: add js write conflict documentation --- docs/content/getting-started/update-tuples.mdx | 4 ++-- .../Docs/SnippetViewer/WriteRequestViewer.tsx | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/content/getting-started/update-tuples.mdx b/docs/content/getting-started/update-tuples.mdx index 05eb69ed69..60c68707c5 100644 --- a/docs/content/getting-started/update-tuples.mdx +++ b/docs/content/getting-started/update-tuples.mdx @@ -262,7 +262,7 @@ For example, if you want to ensure `user:anne` has `reader` access to `document: }} skipSetup={true} allowedLanguages={[ - // SupportedLanguage.JS_SDK, + SupportedLanguage.JS_SDK, SupportedLanguage.GO_SDK, SupportedLanguage.DOTNET_SDK, SupportedLanguage.PYTHON_SDK, @@ -294,7 +294,7 @@ Similarly, you can use `on_missing: "ignore"` when deleting tuples that might no data: {}, }} allowedLanguages={[ - // SupportedLanguage.JS_SDK, + SupportedLanguage.JS_SDK, SupportedLanguage.GO_SDK, SupportedLanguage.DOTNET_SDK, SupportedLanguage.PYTHON_SDK, diff --git a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx index 0f017abf39..4d6bec6dcd 100644 --- a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx @@ -147,12 +147,24 @@ ${ const writes = `writes: [${writeTuples.length > 0 ? `${writeTuples}\n ]` : ']'}`; const deletes = `deletes: [${deleteTuples.length > 0 ? `${deleteTuples}\n ]` : ']'}`; const separator = `${opts.deleteRelationshipTuples && opts.relationshipTuples ? ',\n ' : ''}`; + return ` +const options = {${modelId ? ` + authorizationModelId: "${modelId}",` : ''}${ + opts.conflictOptions ? ` + conflict: {${ + opts.conflictOptions.onDuplicateWrites ? ` + onDuplicateWrites: OnDuplicateWrites.${opts.conflictOptions.onDuplicateWrites.charAt(0).toUpperCase() + opts.conflictOptions.onDuplicateWrites.slice(1)},` : '' + }${ + opts.conflictOptions.onMissingDeletes ? ` + onMissingDeletes: OnMissingDeletes.${opts.conflictOptions.onMissingDeletes.charAt(0).toUpperCase() + opts.conflictOptions.onMissingDeletes.slice(1)}` : '' + } + }` : ''} +}; + await fgaClient.write({ ${opts.relationshipTuples ? writes : ''}${separator}${opts.deleteRelationshipTuples ? deletes : ''}, -}, { - authorizationModelId: "${modelId}" -});`; +}, options);`; } case SupportedLanguage.GO_SDK: { From ffdaa7d8166e5960d9918e601b61d04d99a7c1da Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Wed, 5 Nov 2025 16:37:40 -0500 Subject: [PATCH 7/9] chore(deps): bump @openfga/sdk to latest --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9da2781cab..e926a80d3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@easyops-cn/docusaurus-search-local": "0.52.1", "@lottiefiles/react-lottie-player": "3.6.0", "@openfga/frontend-utils": "^0.2.0-beta.11", - "@openfga/sdk": "^0.9.0", + "@openfga/sdk": "^0.9.1", "@openfga/syntax-transformer": "^0.2.0", "assert-never": "1.4.0", "clsx": "2.1.1", @@ -4829,13 +4829,13 @@ } }, "node_modules/@openfga/sdk": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@openfga/sdk/-/sdk-0.9.0.tgz", - "integrity": "sha512-nWT23dAaMfPh67Dl9Fyiowwzcp1Fbfwy4UjCkxgyci5fz1bCftutMwOgeFD6T4YcUNQGQ9/n0I15JH50vfcBvg==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@openfga/sdk/-/sdk-0.9.1.tgz", + "integrity": "sha512-Mv/U4DC9tmX3g1BwXhwPkihy38UVjcoAgn1eqKevTy7p1vlFXlsGEpIpz67eQKRK7pZ2akX/h9rOcfHHkg6N4A==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.9.0", - "axios": "^1.8.3", + "axios": "^1.12.2", "jose": "^5.10.0", "tiny-async-pool": "^2.1.0" }, diff --git a/package.json b/package.json index 20f8e2800f..ed585fe7b4 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@easyops-cn/docusaurus-search-local": "0.52.1", "@lottiefiles/react-lottie-player": "3.6.0", "@openfga/frontend-utils": "^0.2.0-beta.11", - "@openfga/sdk": "^0.9.0", + "@openfga/sdk": "^0.9.1", "@openfga/syntax-transformer": "^0.2.0", "assert-never": "1.4.0", "clsx": "2.1.1", From f605477b03f81d4191488b3900e499a60f3e7f5f Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Wed, 5 Nov 2025 16:39:57 -0500 Subject: [PATCH 8/9] chore: run lint --- .../Docs/SnippetViewer/WriteRequestViewer.tsx | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx index 4d6bec6dcd..2a143aa4c4 100644 --- a/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/WriteRequestViewer.tsx @@ -149,17 +149,28 @@ ${ const separator = `${opts.deleteRelationshipTuples && opts.relationshipTuples ? ',\n ' : ''}`; return ` -const options = {${modelId ? ` - authorizationModelId: "${modelId}",` : ''}${ - opts.conflictOptions ? ` +const options = {${ + modelId + ? ` + authorizationModelId: "${modelId}",` + : '' + }${ + opts.conflictOptions + ? ` conflict: {${ - opts.conflictOptions.onDuplicateWrites ? ` - onDuplicateWrites: OnDuplicateWrites.${opts.conflictOptions.onDuplicateWrites.charAt(0).toUpperCase() + opts.conflictOptions.onDuplicateWrites.slice(1)},` : '' + opts.conflictOptions.onDuplicateWrites + ? ` + onDuplicateWrites: OnDuplicateWrites.${opts.conflictOptions.onDuplicateWrites.charAt(0).toUpperCase() + opts.conflictOptions.onDuplicateWrites.slice(1)},` + : '' }${ - opts.conflictOptions.onMissingDeletes ? ` - onMissingDeletes: OnMissingDeletes.${opts.conflictOptions.onMissingDeletes.charAt(0).toUpperCase() + opts.conflictOptions.onMissingDeletes.slice(1)}` : '' + opts.conflictOptions.onMissingDeletes + ? ` + onMissingDeletes: OnMissingDeletes.${opts.conflictOptions.onMissingDeletes.charAt(0).toUpperCase() + opts.conflictOptions.onMissingDeletes.slice(1)}` + : '' } - }` : ''} + }` + : '' + } }; await fgaClient.write({ From 5ca0bf4f56020dc24aeeb6f7078ed7e3b7b3c4d4 Mon Sep 17 00:00:00 2001 From: Raghd Hamzeh Date: Wed, 5 Nov 2025 16:43:33 -0500 Subject: [PATCH 9/9] chore: fix note around write conflixt SDK support --- docs/content/getting-started/update-tuples.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/getting-started/update-tuples.mdx b/docs/content/getting-started/update-tuples.mdx index 60c68707c5..5ae8090744 100644 --- a/docs/content/getting-started/update-tuples.mdx +++ b/docs/content/getting-started/update-tuples.mdx @@ -273,7 +273,7 @@ For example, if you want to ensure `user:anne` has `reader` access to `document: /> :::caution -At the moment, this feature is only available on the API (v1.10.0). Supported SDKs will follow shortly after. +At the moment, this feature requires [OpenFGA v1.10.0](https://github.com/openfga/openfga/releases/tag/v1.10.0)+. All latest releases of the SDKs support this. ::: Similarly, you can use `on_missing: "ignore"` when deleting tuples that might not exist.