Skip to content

Commit

Permalink
feat: support start_time parameter on read_changes (#186)
Browse files Browse the repository at this point in the history
  • Loading branch information
rhamzeh authored Jan 6, 2025
2 parents ccd6035 + a888248 commit 7a95727
Show file tree
Hide file tree
Showing 12 changed files with 420 additions and 586 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

strategy:
matrix:
node-version: [14, 16, 18, 20]
node-version: [16, 18, 20]

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog


## [Unreleased](https://github.com/openfga/js-sdk/compare/v0.7.0...HEAD)

- fix: error correctly if apiUrl is not provided (#161)
- feat: add support for `start_time` parameter in `ReadChanges` endpoint
- BREAKING: As of this release, the min node version required by the SDK is now v16.15.0

## v0.7.0

### [0.7.0](https://github.com/openfga/js-sdk/compare/v0.6.3...v0.7.0) (2024-08-30)
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,12 +305,13 @@ Reads the list of historical relationship tuple writes and deletes.

```javascript
const type = 'document';
const startTime = "2022-01-01T00:00:00Z"
const options = {
pageSize: 25,
continuationToken: 'eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==',
};

const response = await fgaClient.readChanges({ type }, options);
const response = await fgaClient.readChanges({ type, startTime }, options);

// response.continuation_token = ...
// response.changes = [
Expand Down
24 changes: 17 additions & 7 deletions api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,10 +579,11 @@ export const OpenFgaApiAxiosParamCreator = function (configuration: Configuratio
* @param {string} [type]
* @param {number} [pageSize]
* @param {string} [continuationToken]
* @param {string} [startTime] Start date and time of changes to read. Format: ISO 8601 timestamp (e.g., 2022-01-01T00:00:00Z) If a continuation_token is provided along side start_time, the continuation_token will take precedence over start_time.
* @param {*} [options] Override http request option.
* @throws { FgaError }
*/
readChanges: (storeId: string, type?: string, pageSize?: number, continuationToken?: string, options: any = {}): RequestArgs => {
readChanges: (storeId: string, type?: string, pageSize?: number, continuationToken?: string, startTime?: string, options: any = {}): RequestArgs => {
// verify required parameter 'storeId' is not null or undefined
assertParamExists("readChanges", "storeId", storeId);
const localVarPath = "/stores/{store_id}/changes"
Expand Down Expand Up @@ -610,6 +611,12 @@ export const OpenFgaApiAxiosParamCreator = function (configuration: Configuratio
localVarQueryParameter["continuation_token"] = continuationToken;
}

if (startTime !== undefined) {
localVarQueryParameter["start_time"] = (startTime as any instanceof Date) ?
(startTime as any).toISOString() :
startTime;
}



setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
Expand Down Expand Up @@ -939,11 +946,12 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
* @param {string} [type]
* @param {number} [pageSize]
* @param {string} [continuationToken]
* @param {string} [startTime] Start date and time of changes to read. Format: ISO 8601 timestamp (e.g., 2022-01-01T00:00:00Z) If a continuation_token is provided along side start_time, the continuation_token will take precedence over start_time.
* @param {*} [options] Override http request option.
* @throws { FgaError }
*/
async readChanges(storeId: string, type?: string, pageSize?: number, continuationToken?: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ReadChangesResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.readChanges(storeId, type, pageSize, continuationToken, options);
async readChanges(storeId: string, type?: string, pageSize?: number, continuationToken?: string, startTime?: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ReadChangesResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.readChanges(storeId, type, pageSize, continuationToken, startTime, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[TelemetryAttribute.FgaClientRequestMethod]: "ReadChanges",
[TelemetryAttribute.FgaClientRequestStoreId]: storeId ?? "",
Expand Down Expand Up @@ -1145,11 +1153,12 @@ export const OpenFgaApiFactory = function (configuration: Configuration, credent
* @param {string} [type]
* @param {number} [pageSize]
* @param {string} [continuationToken]
* @param {string} [startTime] Start date and time of changes to read. Format: ISO 8601 timestamp (e.g., 2022-01-01T00:00:00Z) If a continuation_token is provided along side start_time, the continuation_token will take precedence over start_time.
* @param {*} [options] Override http request option.
* @throws { FgaError }
*/
readChanges(storeId: string, type?: string, pageSize?: number, continuationToken?: string, options?: any): PromiseResult<ReadChangesResponse> {
return localVarFp.readChanges(storeId, type, pageSize, continuationToken, options).then((request) => request(axios));
readChanges(storeId: string, type?: string, pageSize?: number, continuationToken?: string, startTime?: string, options?: any): PromiseResult<ReadChangesResponse> {
return localVarFp.readChanges(storeId, type, pageSize, continuationToken, startTime, options).then((request) => request(axios));
},
/**
* The Write API will transactionally update the tuples for a certain store. Tuples and type definitions allow OpenFGA to determine whether a relationship exists between an object and an user. In the body, `writes` adds new tuples and `deletes` removes existing tuples. When deleting a tuple, any `condition` specified with it is ignored. The API is not idempotent: if, later on, you try to add the same tuple key (even if the `condition` is different), or if you try to delete a non-existing tuple, it will throw an error. The API will not allow you to write tuples such as `document:2021-budget#viewer@document:2021-budget#viewer`, because they are implicit. An `authorization_model_id` may be specified in the body. If it is, it will be used to assert that each written tuple (not deleted) is valid for the model specified. If it is not specified, the latest authorization model ID will be used. ## Example ### Adding relationships To add `user:anne` as a `writer` for `document:2021-budget`, call write API with the following ```json { \"writes\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"writer\", \"object\": \"document:2021-budget\" } ] }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` ### Removing relationships To remove `user:bob` as a `reader` for `document:2021-budget`, call write API with the following ```json { \"deletes\": { \"tuple_keys\": [ { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" } ] } } ```
Expand Down Expand Up @@ -1356,12 +1365,13 @@ export class OpenFgaApi extends BaseAPI {
* @param {string} [type]
* @param {number} [pageSize]
* @param {string} [continuationToken]
* @param {string} [startTime] Start date and time of changes to read. Format: ISO 8601 timestamp (e.g., 2022-01-01T00:00:00Z) If a continuation_token is provided along side start_time, the continuation_token will take precedence over start_time.
* @param {*} [options] Override http request option.
* @throws { FgaError }
* @memberof OpenFgaApi
*/
public readChanges(storeId: string, type?: string, pageSize?: number, continuationToken?: string, options?: any): Promise<CallResult<ReadChangesResponse>> {
return OpenFgaApiFp(this.configuration, this.credentials).readChanges(storeId, type, pageSize, continuationToken, options).then((request) => request(this.axios));
public readChanges(storeId: string, type?: string, pageSize?: number, continuationToken?: string, startTime?: string, options?: any): Promise<CallResult<ReadChangesResponse>> {
return OpenFgaApiFp(this.configuration, this.credentials).readChanges(storeId, type, pageSize, continuationToken, startTime, options).then((request) => request(this.axios));
}

/**
Expand Down
3 changes: 2 additions & 1 deletion apiModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,8 @@ export enum ErrorCode {
DuplicateContextualTuple = 'duplicate_contextual_tuple',
InvalidAuthorizationModel = 'invalid_authorization_model',
UnsupportedSchemaVersion = 'unsupported_schema_version',
Cancelled = 'cancelled'
Cancelled = 'cancelled',
InvalidStartTime = 'invalid_start_time'
}

/**
Expand Down
4 changes: 3 additions & 1 deletion client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export interface ClientListRelationsResponse {

export interface ClientReadChangesRequest {
type: string;
startTime?: string;
}

export type ClientExpandRequest = ExpandRequestTupleKey;
Expand Down Expand Up @@ -390,13 +391,14 @@ export class OpenFgaClient extends BaseAPI {
* @param {ClientRequestOpts & PaginationOptions} [options]
* @param {number} [options.pageSize]
* @param {string} [options.continuationToken]
* @param {string} [body.startTime]
* @param {object} [options.headers] - Custom headers to send alongside the request
* @param {object} [options.retryParams] - Override the retry parameters for this request
* @param {number} [options.retryParams.maxRetry] - Override the max number of retries on each API request
* @param {number} [options.retryParams.minWaitInMs] - Override the minimum wait before a retry is initiated
*/
async readChanges(body?: ClientReadChangesRequest, options: ClientRequestOptsWithStoreId & PaginationOptions = {}): PromiseResult<ReadChangesResponse> {
return this.api.readChanges(this.getStoreId(options)!, body?.type, options.pageSize, options.continuationToken, options);
return this.api.readChanges(this.getStoreId(options)!, body?.type, options.pageSize, options.continuationToken, body?.startTime, options);
}

/**
Expand Down
Empty file added eslint
Empty file.
Loading

0 comments on commit 7a95727

Please sign in to comment.