Skip to content

Commit

Permalink
release: v0.6.0 (#119)
Browse files Browse the repository at this point in the history
  • Loading branch information
ewanharris authored Jun 28, 2024
2 parents 1a6bffb + 847c456 commit 011b066
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 40 deletions.
2 changes: 2 additions & 0 deletions .openapi-generator/FILES
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ configuration.ts
credentials/credentials.ts
credentials/index.ts
credentials/types.ts
docs/opentelemetry.md
errors.ts
example/Makefile
example/README.md
Expand All @@ -36,6 +37,7 @@ example/example1/package.json
git_push.sh
index.ts
package.json
telemetry.ts
tests/client.test.ts
tests/helpers/default-config.ts
tests/helpers/index.ts
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## v0.6.0

### [0.6.0](https://github.com/openfga/js-sdk/compare/v0.5.0...v0.6.0) (2024-06-28)
- feat: add opentelemetry metrics reporting (#117)

## v0.5.0

### [0.5.0](https://github.com/openfga/js-sdk/compare/v0.4.0...v0.5.0) (2024-06-14)
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ This is an autogenerated JavaScript SDK for OpenFGA. It provides a wrapper aroun
- [Retries](#retries)
- [API Endpoints](#api-endpoints)
- [Models](#models)
- [OpenTelemetry](#opentelemetry)
- [Contributing](#contributing)
- [Issues](#issues)
- [Pull Requests](#pull-requests)
Expand Down Expand Up @@ -711,6 +712,10 @@ const fgaClient = new OpenFgaClient({
[Models](https://github.com/openfga/js-sdk/blob/main/apiModel.ts)


### OpenTelemetry

This SDK supports producing metrics that can be consumed as part of an [OpenTelemetry](https://opentelemetry.io/) setup. For more information, please see [the documentation]((https://github.com/openfga/js-sdk/blob/main/docs/opentelemetry.md)

## Contributing

### Issues
Expand Down
62 changes: 33 additions & 29 deletions api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {
createRequestFunction,
RequestArgs,
CallResult,
PromiseResult} from "./common";
PromiseResult
} from "./common";
import { attributeNames } from "./telemetry";
import { Configuration } from "./configuration";
import { Credentials } from "./credentials";
Expand Down Expand Up @@ -757,8 +758,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async check(storeId: string, body: CheckRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<CheckResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.check(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "check",
[attributeNames.requestStoreId]: storeId,
[attributeNames.user]: body.tuple_key.user
});
},
Expand All @@ -771,8 +772,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
*/
async createStore(body: CreateStoreRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<CreateStoreResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.createStore(body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "createStore"
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "createStore",
});
},
/**
Expand All @@ -784,7 +785,10 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
*/
async deleteStore(storeId: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.deleteStore(storeId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "deleteStore",
[attributeNames.requestStoreId]: storeId,
});
},
/**
* The Expand API will return all users and usersets that have certain relationship with an object in a certain store. This is different from the `/stores/{store_id}/read` API in that both users and computed usersets are returned. Body parameters `tuple_key.object` and `tuple_key.relation` are all required. The response will return a tree whose leaves are the specific users and usersets. Union, intersection and difference operator are located in the intermediate nodes. ## Example To expand all users that have the `reader` relationship with object `document:2021-budget`, use the Expand API with the following request body ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` OpenFGA\'s response will be a userset tree of the users and usersets that have read access to the document. ```json { \"tree\":{ \"root\":{ \"type\":\"document:2021-budget#reader\", \"union\":{ \"nodes\":[ { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"users\":{ \"users\":[ \"user:bob\" ] } } }, { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"computed\":{ \"userset\":\"document:2021-budget#writer\" } } } ] } } } } ``` The caller can then call expand API for the `writer` relationship for the `document:2021-budget`.
Expand All @@ -797,8 +801,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async expand(storeId: string, body: ExpandRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ExpandResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.expand(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "expand",
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "expand"
});
},
/**
Expand All @@ -811,8 +815,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async getStore(storeId: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<GetStoreResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getStore(storeId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "getStore"
[attributeNames.requestMethod]: "getStore",
[attributeNames.requestStoreId]: storeId,
});
},
/**
Expand All @@ -826,8 +830,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async listObjects(storeId: string, body: ListObjectsRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ListObjectsResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listObjects(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "listObjects",
[attributeNames.requestStoreId]: storeId,
[attributeNames.user]: body.user
});
},
Expand All @@ -841,8 +845,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
*/
async listStores(pageSize?: number, continuationToken?: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ListStoresResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listStores(pageSize, continuationToken, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "listStores"
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "listStores",
});
},
/**
Expand All @@ -856,8 +860,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async listUsers(storeId: string, body: ListUsersRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ListUsersResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.listUsers(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "listUsers"
[attributeNames.requestMethod]: "listUsers",
[attributeNames.requestStoreId]: storeId,
});
},
/**
Expand All @@ -871,8 +875,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async read(storeId: string, body: ReadRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ReadResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.read(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "read"
[attributeNames.requestMethod]: "read",
[attributeNames.requestStoreId]: storeId,
});
},
/**
Expand All @@ -886,8 +890,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async readAssertions(storeId: string, authorizationModelId: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ReadAssertionsResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.readAssertions(storeId, authorizationModelId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "readAssertions"
[attributeNames.requestMethod]: "readAssertions",
[attributeNames.requestStoreId]: storeId,
});
},
/**
Expand All @@ -901,8 +905,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async readAuthorizationModel(storeId: string, id: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ReadAuthorizationModelResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.readAuthorizationModel(storeId, id, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "readAuthorizationModel"
[attributeNames.requestMethod]: "readAuthorizationModel",
[attributeNames.requestStoreId]: storeId,
});
},
/**
Expand All @@ -917,8 +921,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async readAuthorizationModels(storeId: string, pageSize?: number, continuationToken?: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ReadAuthorizationModelsResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.readAuthorizationModels(storeId, pageSize, continuationToken, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "readAuthorizationModels"
[attributeNames.requestMethod]: "readAuthorizationModels",
[attributeNames.requestStoreId]: storeId,
});
},
/**
Expand All @@ -934,8 +938,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async readChanges(storeId: string, type?: string, pageSize?: number, continuationToken?: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ReadChangesResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.readChanges(storeId, type, pageSize, continuationToken, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "readChanges"
[attributeNames.requestMethod]: "readChanges",
[attributeNames.requestStoreId]: storeId,
});
},
/**
Expand All @@ -949,8 +953,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async write(storeId: string, body: WriteRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<object>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.write(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "write"
[attributeNames.requestMethod]: "write",
[attributeNames.requestStoreId]: storeId,
});
},
/**
Expand All @@ -965,8 +969,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async writeAssertions(storeId: string, authorizationModelId: string, body: WriteAssertionsRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.writeAssertions(storeId, authorizationModelId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "writeAssertions"
[attributeNames.requestMethod]: "writeAssertions",
[attributeNames.requestStoreId]: storeId,
});
},
/**
Expand All @@ -980,8 +984,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async writeAuthorizationModel(storeId: string, body: WriteAuthorizationModelRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<WriteAuthorizationModelResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.writeAuthorizationModel(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestMethod]: "writeAuthorizationModel"
[attributeNames.requestMethod]: "writeAuthorizationModel",
[attributeNames.requestStoreId]: storeId,
});
},
};
Expand Down
4 changes: 1 addition & 3 deletions common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
import { metrics } from "@opentelemetry/api";


import { Configuration } from "./configuration";
import type { Credentials } from "./credentials";
import {
Expand All @@ -29,7 +28,7 @@ import {
import { setNotEnumerableProperty } from "./utils";
import { buildAttributes } from "./telemetry";

const meter = metrics.getMeter("@openfga/sdk", "0.5.0");
const meter = metrics.getMeter("@openfga/sdk", "0.6.0");
const durationHist = meter.createHistogram("fga-client.request.duration", {
description: "The duration of requests",
unit: "milliseconds",
Expand Down Expand Up @@ -230,4 +229,3 @@ export const createRequestFunction = function (axiosArgs: RequestArgs, axiosInst
return result;
};
};

4 changes: 2 additions & 2 deletions configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const DEFAULT_MAX_RETRY = 15;
// default minimum wait period in retry - but will backoff exponentially
const DEFAULT_MIN_WAIT_MS = 100;

const DEFAULT_USER_AGENT = "openfga-sdk js/0.5.0";
const DEFAULT_USER_AGENT = "openfga-sdk js/0.6.0";

export interface RetryParams {
maxRetry?: number;
Expand Down Expand Up @@ -73,7 +73,7 @@ export class Configuration {
* @type {string}
* @memberof Configuration
*/
private static sdkVersion = "0.5.0";
private static sdkVersion = "0.6.0";

/**
* provide the full api URL (e.g. `https://api.fga.example`)
Expand Down
6 changes: 5 additions & 1 deletion credentials/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class Credentials {
}
break;
case CredentialsMethod.ClientCredentials: {
const meter = metrics.getMeter("@openfga/sdk", "0.5.0");
const meter = metrics.getMeter("@openfga/sdk", "0.6.0");
this.tokenCounter = meter.createCounter("fga-client.credentials.request");
break;
}
Expand Down Expand Up @@ -122,6 +122,7 @@ export class Credentials {
if (this.accessToken && (!this.accessTokenExpiryDate || this.accessTokenExpiryDate > new Date())) {
return this.accessToken;
}

return this.refreshAccessToken();
}
}
Expand All @@ -132,6 +133,7 @@ export class Credentials {
*/
private async refreshAccessToken() {
const clientCredentials = (this.authConfig as { method: CredentialsMethod.ClientCredentials; config: ClientCredentialsConfig })?.config;

try {
const response = await attemptHttpRequest<{
client_id: string,
Expand Down Expand Up @@ -162,7 +164,9 @@ export class Credentials {
this.accessToken = response.data.access_token;
this.accessTokenExpiryDate = new Date(Date.now() + response.data.expires_in * 1000);
}

this.tokenCounter?.add(1, buildAttributes(response, this.authConfig));

return this.accessToken;
} catch (err: unknown) {
if (err instanceof FgaApiError) {
Expand Down
31 changes: 31 additions & 0 deletions docs/opentelemetry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# OpenTelemetry

This SDK produces [metrics](https://opentelemetry.io/docs/concepts/signals/metrics/) using [OpenTelemetry](https://opentelemetry.io/) that allow you to view data such as request timings. These metrics also include attributes for the model and store ID, as well as the API called to allow you to build reporting.

When an OpenTelemetry SDK instance is configured, the metrics will be exported and sent to the collector configured as part of your applications configuration. If you are not using OpenTelemetry, the metric functionality is a no-op and the events are never sent.

In cases when metrics events are sent, they will not be viewable outside of infrastructure configured in your application, and are never available to the OpenFGA team or contributors.

## Metrics

### Supported Metrics

| Metric Name | Type | Description |
|---------------------------------|-----------|---------------------------------------------------------------------------------|
| `fga-client.request.duration` | Histogram | The total request time for FGA requests |
| `fga-client.query.duration` | Histogram | The amount of time the FGA server took to process the request |
|` fga-client.credentials.request`| Counter | The total number of times a new token was requested when using ClientCredentials|

### Supported attributes

| Attribute Name | Type | Description |
|--------------------------------|----------|-------------------------------------------------------------------------------------|
| `fga-client.response.model_id` | `string` | The authorization model ID that the FGA server used |
| `fga-client.request.method` | `string` | The FGA method/action that was performed |
| `fga-client.request.store_id` | `string` | The store ID that was sent as part of the request |
| `fga-client.request.model_id` | `string` | The authorization model ID that was sent as part of the request, if any |
| `fga-client.request.client_id` | `string` | The client ID associated with the request, if any |
| `fga-client.user` | `string` | The user that is associated with the action of the request for check and list users |
| `http.status_code ` | `int` | The status code of the response |
| `http.method` | `string` | The HTTP method for the request |
| `http.host` | `string` | Host identifier of the origin the request was sent to |
2 changes: 1 addition & 1 deletion example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Steps
2. In the Example `package.json` change the `@openfga/sdk` dependency from a semver range like below
```json
"dependencies": {
"@openfga/sdk": "^0.5.0"
"@openfga/sdk": "^0.6.0"
}
```
to a `file:` reference like below
Expand Down
Loading

0 comments on commit 011b066

Please sign in to comment.