Skip to content

Commit

Permalink
fix: document behaviour of continuation token (#83)
Browse files Browse the repository at this point in the history
Close #82
  • Loading branch information
adriantam authored Jul 11, 2023
1 parent 888b909 commit a4d5aab
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 19 deletions.
18 changes: 11 additions & 7 deletions docs/openapiv2/apidocs.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"/stores": {
"get": {
"summary": "List all stores",
"description": "Returns a paginated list of OpenFGA stores.",
"description": "Returns a paginated list of OpenFGA stores and a continuation token to get additional stores.\nThe continuation token will be empty if there are no more stores.\n",
"operationId": "ListStores",
"responses": {
"200": {
Expand Down Expand Up @@ -325,7 +325,7 @@
"/stores/{store_id}/authorization-models": {
"get": {
"summary": "Return all the authorization models for a particular store",
"description": "The ReadAuthorizationModels API will return all the authorization models for a certain store.\nOpenFGA's response will contain an array of all authorization models, sorted in descending order of creation.\n\n## Example\nAssume that a store's authorization model has been configured twice. To get all the authorization models that have been created in this store, call GET authorization-models. The API will return a response that looks like:\n```json\n{\n \"authorization_models\": [\n {\n \"id\": \"01G50QVV17PECNVAHX1GG4Y5NC\",\n \"type_definitions\": [...]\n },\n {\n \"id\": \"01G4ZW8F4A07AKQ8RHSVG9RW04\",\n \"type_definitions\": [...]\n },\n ]\n}\n```\nIf there are more authorization models available, the response will contain an extra field `continuation_token`:\n```json\n{\n \"authorization_models\": [\n {\n \"id\": \"01G50QVV17PECNVAHX1GG4Y5NC\",\n \"type_definitions\": [...]\n },\n {\n \"id\": \"01G4ZW8F4A07AKQ8RHSVG9RW04\",\n \"type_definitions\": [...]\n },\n ],\n \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\"\n}\n```\n",
"description": "The ReadAuthorizationModels API will return all the authorization models for a certain store.\nOpenFGA's response will contain an array of all authorization models, sorted in descending order of creation.\n\n## Example\nAssume that a store's authorization model has been configured twice. To get all the authorization models that have been created in this store, call GET authorization-models. The API will return a response that looks like:\n```json\n{\n \"authorization_models\": [\n {\n \"id\": \"01G50QVV17PECNVAHX1GG4Y5NC\",\n \"type_definitions\": [...]\n },\n {\n \"id\": \"01G4ZW8F4A07AKQ8RHSVG9RW04\",\n \"type_definitions\": [...]\n },\n ],\n \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\"\n}\n```\nIf there are no more authorization models available, the `continuation_token` field will be empty\n```json\n{\n \"authorization_models\": [\n {\n \"id\": \"01G50QVV17PECNVAHX1GG4Y5NC\",\n \"type_definitions\": [...]\n },\n {\n \"id\": \"01G4ZW8F4A07AKQ8RHSVG9RW04\",\n \"type_definitions\": [...]\n },\n ],\n \"continuation_token\": \"\"\n}\n```\n",
"operationId": "ReadAuthorizationModels",
"responses": {
"200": {
Expand Down Expand Up @@ -777,7 +777,7 @@
"/stores/{store_id}/read": {
"post": {
"summary": "Get tuples from the store that matches a query, without following userset rewrite rules",
"description": "The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. \nIn the body:\n1. `tuple_key` is optional. If not specified, it will return all tuples in the store.\n2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`).\n3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only.\n## Examples\n### Query for all objects in a type definition\nTo query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of\n```json\n{\n \"tuple_key\": {\n \"user\": \"user:bob\",\n \"relation\": \"reader\",\n \"object\": \"document:\"\n }\n}\n```\nThe API will return tuples and an optional continuation token, something like\n```json\n{\n \"tuples\": [\n {\n \"key\": {\n \"user\": \"user:bob\",\n \"relation\": \"reader\",\n \"object\": \"document:2021-budget\"\n },\n \"timestamp\": \"2021-10-06T15:32:11.128Z\"\n }\n ]\n}\n```\nThis means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store.\n### Query for all stored relationship tuples that have a particular relation and object\nTo query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of \n```json\n{\n \"tuple_key\": {\n \"object\": \"document:2021-budget\",\n \"relation\": \"reader\"\n }\n}\n```\nThe API will return something like \n```json\n{\n \"tuples\": [\n {\n \"key\": {\n \"user\": \"user:bob\",\n \"relation\": \"reader\",\n \"object\": \"document:2021-budget\"\n },\n \"timestamp\": \"2021-10-06T15:32:11.128Z\"\n }\n ]\n}\n```\nThis means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them.\n### Query for all users with all relationships for a particular document\nTo query for all users that have any relationship with `document:2021-budget`, call read API with body of \n```json\n{\n \"tuple_key\": {\n \"object\": \"document:2021-budget\"\n }\n}\n```\nThe API will return something like \n```json\n{\n \"tuples\": [\n {\n \"key\": {\n \"user\": \"user:anne\",\n \"relation\": \"writer\",\n \"object\": \"document:2021-budget\"\n },\n \"timestamp\": \"2021-10-05T13:42:12.356Z\"\n },\n {\n \"key\": {\n \"user\": \"user:bob\",\n \"relation\": \"reader\",\n \"object\": \"document:2021-budget\"\n },\n \"timestamp\": \"2021-10-06T15:32:11.128Z\"\n }\n ]\n}\n```\nThis means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`).\n",
"description": "The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. \nIn the body:\n1. `tuple_key` is optional. If not specified, it will return all tuples in the store.\n2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`).\n3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only.\n## Examples\n### Query for all objects in a type definition\nTo query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of\n```json\n{\n \"tuple_key\": {\n \"user\": \"user:bob\",\n \"relation\": \"reader\",\n \"object\": \"document:\"\n }\n}\n```\nThe API will return tuples and a continuation token, something like\n```json\n{\n \"tuples\": [\n {\n \"key\": {\n \"user\": \"user:bob\",\n \"relation\": \"reader\",\n \"object\": \"document:2021-budget\"\n },\n \"timestamp\": \"2021-10-06T15:32:11.128Z\"\n }\n ],\n \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\"\n}\n```\nThis means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store.\nThe continuation token will be empty if there are no more tuples to query.### Query for all stored relationship tuples that have a particular relation and object\nTo query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of \n```json\n{\n \"tuple_key\": {\n \"object\": \"document:2021-budget\",\n \"relation\": \"reader\"\n }\n}\n```\nThe API will return something like \n```json\n{\n \"tuples\": [\n {\n \"key\": {\n \"user\": \"user:bob\",\n \"relation\": \"reader\",\n \"object\": \"document:2021-budget\"\n },\n \"timestamp\": \"2021-10-06T15:32:11.128Z\"\n }\n ],\n \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\"\n}\n```\nThis means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them.\n### Query for all users with all relationships for a particular document\nTo query for all users that have any relationship with `document:2021-budget`, call read API with body of \n```json\n{\n \"tuple_key\": {\n \"object\": \"document:2021-budget\"\n }\n}\n```\nThe API will return something like \n```json\n{\n \"tuples\": [\n {\n \"key\": {\n \"user\": \"user:anne\",\n \"relation\": \"writer\",\n \"object\": \"document:2021-budget\"\n },\n \"timestamp\": \"2021-10-05T13:42:12.356Z\"\n },\n {\n \"key\": {\n \"user\": \"user:bob\",\n \"relation\": \"reader\",\n \"object\": \"document:2021-budget\"\n },\n \"timestamp\": \"2021-10-06T15:32:11.128Z\"\n }\n ],\n \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\"\n}\n```\nThis means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`).\n",
"operationId": "Read",
"responses": {
"200": {
Expand Down Expand Up @@ -1316,7 +1316,8 @@
},
"continuation_token": {
"type": "string",
"example": "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ=="
"example": "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==",
"description": "The continuation token will be empty if there are no more stores."
}
}
},
Expand Down Expand Up @@ -1432,7 +1433,8 @@
},
"continuation_token": {
"type": "string",
"example": "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ=="
"example": "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==",
"description": "The continuation token will be empty if there are no more models."
}
}
},
Expand All @@ -1447,7 +1449,8 @@
},
"continuation_token": {
"type": "string",
"example": "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ=="
"example": "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==",
"description": "The continuation token will be identical if there are no new changes."
}
}
},
Expand All @@ -1462,7 +1465,8 @@
},
"continuation_token": {
"type": "string",
"example": "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ=="
"example": "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==",
"description": "The continuation token will be empty if there are no more tuples."
}
}
},
Expand Down
43 changes: 31 additions & 12 deletions openfga/v1/openfga_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ service OpenFGAService {
" }\n"
"}\n"
"```\n"
"The API will return tuples and an optional continuation token, something like\n"
"The API will return tuples and a continuation token, something like\n"
"```json\n"
"{\n"
" \"tuples\": [\n"
Expand All @@ -58,11 +58,13 @@ service OpenFGAService {
" },\n"
" \"timestamp\": \"2021-10-06T15:32:11.128Z\"\n"
" }\n"
" ]\n"
" ],\n"
" \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\"\n"
"}\n"
"```\n"
"This means that `user:bob` has a `reader` relationship with 1 document "
"`document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store.\n"
"The continuation token will be empty if there are no more tuples to query."
"### Query for all stored relationship tuples that have a particular relation and object\n"
"To query for all users that have `reader` relationship with "
"`document:2021-budget`, call read API with body of \n"
Expand All @@ -86,7 +88,8 @@ service OpenFGAService {
" },\n"
" \"timestamp\": \"2021-10-06T15:32:11.128Z\"\n"
" }\n"
" ]\n"
" ],\n"
" \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\"\n"
"}\n"
"```\n"
"This means that `document:2021-budget` has 1 `reader` (`user:bob`). "
Expand Down Expand Up @@ -122,7 +125,8 @@ service OpenFGAService {
" },\n"
" \"timestamp\": \"2021-10-06T15:32:11.128Z\"\n"
" }\n"
" ]\n"
" ],\n"
" \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\"\n"
"}\n"
"```\n"
"This means that `document:2021-budget` has 1 `reader` (`user:bob`) "
Expand Down Expand Up @@ -326,10 +330,11 @@ service OpenFGAService {
" \"id\": \"01G4ZW8F4A07AKQ8RHSVG9RW04\",\n"
" \"type_definitions\": [...]\n"
" },\n"
" ]\n"
" ],\n"
" \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\"\n"
"}\n"
"```\n"
"If there are more authorization models available, the response will contain an extra field `continuation_token`:\n"
"If there are no more authorization models available, the `continuation_token` field will be empty\n"
"```json\n"
"{\n"
" \"authorization_models\": [\n"
Expand All @@ -342,7 +347,7 @@ service OpenFGAService {
" \"type_definitions\": [...]\n"
" },\n"
" ],\n"
" \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\"\n"
" \"continuation_token\": \"\"\n"
"}\n"
"```\n"
""
Expand Down Expand Up @@ -616,7 +621,9 @@ service OpenFGAService {
summary: "List all stores"
tags: ["Stores"]
operation_id: "ListStores"
description: "Returns a paginated list of OpenFGA stores."
description:
"Returns a paginated list of OpenFGA stores and a continuation token to get additional stores.\n"
"The continuation token will be empty if there are no more stores.\n"
};
}

Expand Down Expand Up @@ -802,7 +809,10 @@ message ReadResponse {
string continuation_token = 2 [
json_name = "continuation_token",
(validate.rules).string.max_bytes = 5120,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {example: "\"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\""}
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "The continuation token will be empty if there are no more tuples.",
example: "\"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\""
}
];
}

Expand Down Expand Up @@ -977,7 +987,10 @@ message ReadAuthorizationModelsResponse {
string continuation_token = 2 [
json_name = "continuation_token",
(validate.rules).string.max_bytes = 5120,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {example: "\"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\""}
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "The continuation token will be empty if there are no more models.",
example: "\"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\""
}
];
}

Expand Down Expand Up @@ -1071,7 +1084,10 @@ message ReadChangesResponse {
string continuation_token = 2 [
json_name = "continuation_token",
(validate.rules).string.max_bytes = 5120,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {example: "\"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\""}
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "The continuation token will be identical if there are no new changes."
example: "\"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\""
}
];
}

Expand Down Expand Up @@ -1159,6 +1175,9 @@ message ListStoresResponse {
string continuation_token = 2 [
json_name = "continuation_token",
(validate.rules).string.max_bytes = 5120,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {example: "\"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\""}
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "The continuation token will be empty if there are no more stores.",
example: "\"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\""
}
];
}

0 comments on commit a4d5aab

Please sign in to comment.