diff --git a/docs/content/getting-started/perform-check.mdx b/docs/content/getting-started/perform-check.mdx index 50808bce0..f9c10dbc9 100644 --- a/docs/content/getting-started/perform-check.mdx +++ b/docs/content/getting-started/perform-check.mdx @@ -169,16 +169,28 @@ The result's `allowed` field will return `true` if the relationship exists and ` If you want to check multiple user-object-relationship combinations in a single request, you can use the [Batch Check](/api/service#Relationship%20Queries/BatchCheck) API endpoint. Batching authorization checks together in a single request significantly reduces overall network latency. -The Batch Check endpoint requires a `correlation_id` parameter for each check. The `correlation_id` is used to "correlate" the check responses with the checks sent in the request, since `tuple_keys` and `contextual_tuples` are not returned in the response on purpose to reduce data transfer to improve network latency. A `correlation_id` can be composed of any string of alphanumeric characters or dashes between 1-36 characters in length. +:::note +The BatchCheck endpoint is currently only supported by the JS SDK (>=[v0.8.0](https://github.com/openfga/js-sdk/releases/tag/v0.8.0) and the Python SDK (>=[v0.9.0](https://github.com/openfga/python-sdk/releases/tag/v0.9.0)). Support in the other SDKs is being worked on. + +In the SDKs that don't support the server-side `BatchCheck`, the `BatchCheck` method performs client-side batch checks by making multiple check requests with limited parallelization, in SDK versions that do support the server-side `BatchCheck`, the existing method has been renamed to `ClientBatchCheck`. + +Refer to the README for each SDK for more information. Refer to the release notes of the relevant SDK version for more information on how to migrate from client-side to the server-side `BatchCheck`. +::: + +The BatchCheck endpoint requires a `correlation_id` parameter for each check. The `correlation_id` is used to "correlate" the check responses with the checks sent in the request, since `tuple_keys` and `contextual_tuples` are not returned in the response on purpose to reduce data transfer to improve network latency. A `correlation_id` can be composed of any string of alphanumeric characters or dashes between 1-36 characters in length. This means you can use: - simple iterating integers `1,2,3,etc` - UUID `e5fe049b-f252-40b3-b795-fe485d588279` - ULID `01JBMD9YG0XH3B4GVA8A9D2PSN` - or some other unique string -But each `correlation_id` within a request must be unique. +Each `correlation_id` within a request must be unique. -*Note*: If you are using one of our SDKs, the `correlation_id` is inserted for you by default and automatically correlates the `allowed` response with the proper `tuple_key`. +:::note +If you are using one of our SDKs: +* the `correlation_id` is inserted for you by default and automatically correlates the `allowed` response with the proper `tuple_key` +* if you pass in more checks than the server supports in a single call (default `50`, configurable on the server), the SDK will automatically split and batch the `BatchCheck` requests for you, how it does this can be configured using the `maxBatchSize` and `maxParallelRequests` options in the SDK. +::: To check whether user `user:anne` has multiple relationships `writer` and `reader` with object `document:Z` @@ -199,11 +211,7 @@ To check whether user `user:anne` has multiple relationships `writer` and `reade allowed: true } ]} - skipSetup={true} - allowedLanguages={[ - SupportedLanguage.CURL, - ]} /> The result will include an `allowed` field for each authorization check that will return `true` if the relationship exists and `false` if the relationship does not exist. diff --git a/docs/content/interacting/relationship-queries.mdx b/docs/content/interacting/relationship-queries.mdx index 3582f51b8..75be67370 100644 --- a/docs/content/interacting/relationship-queries.mdx +++ b/docs/content/interacting/relationship-queries.mdx @@ -184,7 +184,9 @@ The API will return `true` d ### Caveats and when not to use it -If you are making less than 10 checks, it may be faster to call the [Check API](/api/service#Relationship%20Queries/Check) in parallel instead of Batch Check. +If you are making less than 10 checks, it may be faster to call the [Check API](/api/service#Relationship%20Queries/Check) in parallel instead of Batch Check. + +The new BatchCheck endpoint is currently supported by the JS SDK (>=[v0.8.0](https://github.com/openfga/js-sdk/releases/tag/v0.8.0) and the Python SDK (>=[v0.9.0](https://github.com/openfga/python-sdk/releases/tag/v0.9.0)). Support in the other SDKs is being worked on. ## Read diff --git a/src/components/Docs/SnippetViewer/BatchCheckRequestViewer.tsx b/src/components/Docs/SnippetViewer/BatchCheckRequestViewer.tsx index 9396ceb35..5e24ac420 100644 --- a/src/components/Docs/SnippetViewer/BatchCheckRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/BatchCheckRequestViewer.tsx @@ -26,28 +26,327 @@ function batchCheckRequestViewer(lang: SupportedLanguage, opts: BatchCheckReques switch (lang) { case SupportedLanguage.PLAYGROUND: - return ''; + throw new Error('Batch check is not supported in the Playground'); case SupportedLanguage.CLI: - return ''; + throw new Error('Batch check is not supported in the CLI'); case SupportedLanguage.JS_SDK: - return ''; + return `// Requires >=v0.8.0 for the server side BatchCheck, earlier versions support a client-side BatchCheck with a slightly different interface +const body = { + checks: [ + ${checks + .map( + (check) => `{ + user: '${check.user}', + relation: '${check.relation}', + object: '${check.object}', + correlation_id: '${check.correlation_id}'${ + check.contextualTuples + ? `, + contextual_tuples: [ + ${check.contextualTuples + .map( + (tuple) => `{ + user: '${tuple.user}', + relation: '${tuple.relation}', + object: '${tuple.object}', + }`, + ) + .join(',')} + ]` + : '' + }${ + check.context + ? `, + context: ${JSON.stringify(check.context)}` + : '' + } + }`, + ) + .join(',')} + ], +} + +const options = { +${modelId ? ` authorization_model_id: '${modelId}'` : ''}, + maxBatchSize: 50, // optional, default is 50, can be used to limit the number of checks in a single server request + maxParallelRequests: 10, // optional, default is 10, can be used to limit the parallelization of the BatchCheck chunks +}; +const { result } = await fgaClient.batchCheck(body, options); + +/* +{ + "results": [ + ${checks + .map( + (check) => `{ + "correlationId": '${check.correlation_id}', + "allowed": ${check.allowed}, + "request": { + "user": '${check.user}', + "relation": '${check.relation}', + "object": '${check.object}'${ + check.contextualTuples + ? `,\n "contextualTuples": [${check.contextualTuples + .map( + (tuple) => `{ + "user": '${tuple.user}', + "relation": '${tuple.relation}', + "object": '${tuple.object}' + }`, + ) + .join(',\n ')} + ` + : '' + }} + }`, + ) + .join(', ')} + ], +} +*/`; case SupportedLanguage.GO_SDK: - return ''; + return `// The Go SDK does not yet support server-side batch checks. This currently just calls the check endpoint in parallel. + +body := ClientBatchCheckBody{${checks + .map( + (check) => ` + { + User: "${check.user}", + Relation: "${check.relation}", + Object: "${check.object}",${ + check.contextualTuples + ? ` + ContextualTuples: []ClientTupleKey{ + ${check.contextualTuples + .map( + (tuple) => ` + { + User: "${tuple.user}", + Relation: "${tuple.relation}", + Object: "${tuple.object}", + },`, + ) + .join('')} + },` + : '' + } + },`, + ) + .join('')} +} +options := ClientBatchCheckOptions{${modelId ? `\n AuthorizationModelId: PtrString("${modelId}"),\n` : ''}} +data, err := fgaClient.BatchCheck(context.Background()).Body(requestBody).Options(options).Execute() +/* +data = [${checks + .map( + (check) => `{ + Allowed: ${check.allowed}, + Request: { + User: '${check.user}', + Relation: '${check.relation}', + Object: '${check.object}'${ + check.contextualTuples + ? `,\n ContextualTuples: [${check.contextualTuples + .map( + (tuple) => `{ + User: '${tuple.user}', + Relation: '${tuple.relation}', + Object: '${tuple.object}' + }`, + ) + .join(',\n ')}] + ` + : '\n ' + }} +}`, + ) + .join(', ')}] +*/ +`; case SupportedLanguage.DOTNET_SDK: - return ''; + return `// The .NET SDK does not yet support server-side batch checks. This currently just calls the check endpoint in parallel. + +var body = new ClientBatchCheckRequest { + Checks = new List() { + ${checks + .map( + (check) => `new() { + User = "${check.user}", + Relation = "${check.relation}", + Object = "${check.object}",${ + check.contextualTuples + ? `, + ContextualTuples = new List() { + ${check.contextualTuples + .map( + (tuple) => `new() { + User = "${tuple.user}", + Relation = "${tuple.relation}", + Object = "${tuple.object}", + }`, + ) + .join(',')}, + },` + : '' + } + },`, + ) + .join('\n ')} + }, +} +var options = new ClientBatchCheckOptions {${modelId ? `\n AuthorizationModelId: "${modelId}",\n` : ''}} +var response = await fgaClient.BatchCheck(body, options); +/* +response.Responses = [${checks + .map( + (check) => `{ + Allowed: ${check.allowed}, + Request: { + User: '${check.user}', + Relation: '${check.relation}', + Object: '${check.object}'${ + check.contextualTuples + ? `,\n ContextualTuples: [${check.contextualTuples + .map( + (tuple) => `{ + User: '${tuple.user}', + Relation: '${tuple.relation}', + Object: '${tuple.object}' + }`, + ) + .join(',\n ')}] + ` + : '\n ' + }} +}`, + ) + .join(', ')}] +*/ +`; case SupportedLanguage.PYTHON_SDK: - return ''; + return `# Requires >=v0.9.0 for the server side BatchCheck, earlier versions support a client-side BatchCheck with a slightly different interface + +checks = [${checks.map( + (check) => ` + ClientBatchCheckItem( + user="${check.user}", + relation="${check.relation}", + object="${check.object}", + correlation_id="${check.correlation_id}"${ + check.contextualTuples + ? `, + contextual_tuples=[${check.contextualTuples.map((tuple) => `ClientTuple(user="${tuple.user}", relation="${tuple.relation}", object="${tuple.object}")`).join(',')}]` + : '' + }`, + )} +] +options = {${modelId ? `\n authorization_model_id="${modelId}"` : ''}} +response = await fga_client.batch_check(ClientBatchCheckRequest(checks=checks), options) + +# response.results = [${checks + .map( + (check) => `{ +# correlation_id: '${check.correlation_id}', +# allowed: ${check.allowed}, +# request: { +# user: '${check.user}', +# relation: '${check.relation}', +# object: '${check.object}'${ + check.contextualTuples + ? `,\n# contextual_tuples: [${check.contextualTuples + .map( + (tuple) => `{ +# user: '${tuple.user}', +# relation: '${tuple.relation}', +# object: '${tuple.object}' +# }`, + ) + .join(',\n ')}] +# ` + : '' + }} +#}`, + ) + .join(', ')}]`; case SupportedLanguage.JAVA_SDK: - return ''; + return `// The Java SDK does not yet support server-side batch checks. This currently just calls the check endpoint in parallel. + +var request = List.of( + ${checks + .map( + (check) => `new ClientCheckRequest() + .user("${check.user}") + .relation("${check.relation}") + ._object("${check.object}")${ + check.contextualTuples + ? `\n .contextualTuples(List.of( + ${check.contextualTuples + .map( + (tuple) => `new ClientTupleKey() + .user("${tuple.user}") + .relation("${tuple.relation}") + ._object("${tuple.object}")`, + ) + .join(',\n ')}) + )` + : '' + }${check.context ? `.context(${check.context})` : ''}`, + ) + .join(',\n ')} +); +var options = new ClientBatchCheckOptions()${modelId ? `\n .authorizationModelId("${modelId}")` : ''}; +var response = fgaClient.batchCheck(request, options).get(); + +/* +response.getResponses() = [${checks + .map( + (check) => `{ + allowed: ${check.allowed}, + request: { + user: '${check.user}', + relation: '${check.relation}', + _object: '${check.object}'${ + check.contextualTuples + ? `,\n contextualTuples: [${check.contextualTuples + .map( + (tuple) => `{ + user: '${tuple.user}', + relation: '${tuple.relation}', + _object: '${tuple.object}' + }`, + ) + .join(',\n ')}] + ` + : '\n ' + }} +}`, + ) + .join(', ')}] +*/ +`; case SupportedLanguage.RPC: - return ''; + return `BatchCheck([${checks + .map( + (check) => ` + - user="${check.user}", relation="${check.relation}", object="${check.object}"${check.correlation_id ? `, correlation_id="${check.correlation_id}"` : ''}${check.contextualTuples?.length ? `", contextual_tuples=[${check.contextualTuples.map((tuple) => `(user="${tuple.user}", relation="${tuple.relation}", object="${tuple.object}")`)}]` : ''}`, + ) + .join('')} +]) + +Reply:${checks + .map( + (check) => ` + - correlation_id="${check.correlation_id}": ${check.allowed}`, + ) + .join('')} +`; case SupportedLanguage.CURL: { /* eslint-disable max-len */ @@ -97,7 +396,15 @@ function batchCheckRequestViewer(lang: SupportedLanguage, opts: BatchCheckReques } export function BatchCheckRequestViewer(opts: BatchCheckRequestViewerOpts): JSX.Element { - const defaultLangs = [SupportedLanguage.CURL]; + const defaultLangs = [ + SupportedLanguage.JS_SDK, + SupportedLanguage.GO_SDK, + SupportedLanguage.DOTNET_SDK, + SupportedLanguage.PYTHON_SDK, + SupportedLanguage.JAVA_SDK, + SupportedLanguage.CURL, + SupportedLanguage.RPC, + ]; const allowedLanguages = getFilteredAllowedLangs(opts.allowedLanguages, defaultLangs); return defaultOperationsViewer(allowedLanguages, opts, batchCheckRequestViewer); } diff --git a/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx b/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx index 746e27af0..c83aa4ff7 100644 --- a/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx +++ b/src/components/Docs/SnippetViewer/CheckRequestViewer.tsx @@ -152,7 +152,7 @@ var response = await fgaClient.Check(body, options); // response.Allowed = ${allowed}`; case SupportedLanguage.PYTHON_SDK: return `options = { - "authorization_model_id": "${modelId}" + authorization_model_id="${modelId}" } body = ClientCheckRequest( user="${user}", @@ -249,6 +249,7 @@ export function CheckRequestViewer(opts: CheckRequestViewerOpts): JSX.Element { SupportedLanguage.CLI, SupportedLanguage.CURL, SupportedLanguage.RPC, + SupportedLanguage.PLAYGROUND, ]; const allowedLanguages = getFilteredAllowedLangs(opts.allowedLanguages, defaultLangs); return defaultOperationsViewer(allowedLanguages, opts, checkRequestViewer);