diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES index 9a1a0af..14a1254 100644 --- a/.openapi-generator/FILES +++ b/.openapi-generator/FILES @@ -34,6 +34,11 @@ example/Makefile example/README.md example/example1/example1.mjs example/example1/package.json +example/opentelemetry/.npmrc +example/opentelemetry/README.md +example/opentelemetry/instrumentation.mjs +example/opentelemetry/opentelemetry.mjs +example/opentelemetry/package.json git_push.sh index.ts package.json diff --git a/README.md b/README.md index afe29b3..517e744 100644 --- a/README.md +++ b/README.md @@ -714,7 +714,7 @@ const fgaClient = new OpenFgaClient({ ### 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) +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 diff --git a/docs/opentelemetry.md b/docs/opentelemetry.md index 5b9dbd5..24765a2 100644 --- a/docs/opentelemetry.md +++ b/docs/opentelemetry.md @@ -28,4 +28,8 @@ In cases when metrics events are sent, they will not be viewable outside of infr | `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 | \ No newline at end of file +| `http.host` | `string` | Host identifier of the origin the request was sent to | + +## Example + +There is an [example project](https://github.com/openfga/js-sdk/blob/main/example/opentelemetry) that provides some guidance on how to configure OpenTelemetry available in the examples directory. \ No newline at end of file diff --git a/example/README.md b/example/README.md index 2cdac02..092edfa 100644 --- a/example/README.md +++ b/example/README.md @@ -6,6 +6,9 @@ A set of Examples on how to call the OpenFGA JS SDK Example 1: A bare-bones example. It creates a store, and runs a set of calls against it including creating a model, writing tuples and checking for access. +OpenTelemetry: +An example that demonstrates how to integrate the OpenFGA JS SDK with OpenTelemetry. + ### Running the Examples Prerequisites: diff --git a/example/opentelemetry/.env.example b/example/opentelemetry/.env.example new file mode 100644 index 0000000..31bbd87 --- /dev/null +++ b/example/opentelemetry/.env.example @@ -0,0 +1,11 @@ +# Configuration for OpenFGA +FGA_CLIENT_ID= +FGA_API_TOKEN_ISSUER= +FGA_API_AUDIENCE= +FGA_CLIENT_SECRET= +FGA_STORE_ID= +FGA_AUTHORIZATION_MODEL_ID= +FGA_API_URL="http://localhost:8080" + +# Configuration for OpenTelemetry +OTEL_SERVICE_NAME="openfga-opentelemetry-example" \ No newline at end of file diff --git a/example/opentelemetry/.npmrc b/example/opentelemetry/.npmrc new file mode 100644 index 0000000..9cf9495 --- /dev/null +++ b/example/opentelemetry/.npmrc @@ -0,0 +1 @@ +package-lock=false \ No newline at end of file diff --git a/example/opentelemetry/README.md b/example/opentelemetry/README.md new file mode 100644 index 0000000..3befd14 --- /dev/null +++ b/example/opentelemetry/README.md @@ -0,0 +1,43 @@ +# OpenTelemetry usage with OpenFGA's JS SDK + +This example demonstrates how you can use OpenTelemetry with OpenFGA's JS SDK. + +## Prerequisites + +If you do not already have an OpenFGA instance running, you can start one using the following command: + +```bash +docker run -d -p 8080:8080 openfga/openfga +``` + +You need to have an OpenTelemetry collector running to receive data. A pre-configured collector is available using Docker: + +```bash +git clone https://github.com/ewanharris/opentelemetry-collector-dev-setup.git +cd opentelemetry-collector-dev-setup +docker-compose up -d +``` + +## Configure the example + +You need to configure the example for your environment: + +```bash +cp .env.example .env +``` + +Now edit the `.env` file and set the values as appropriate. + +## Running the example + +Begin by installing the required dependencies: + +```bash +npm i +``` + +Next, run the example: + +```bash +npm start +``` \ No newline at end of file diff --git a/example/opentelemetry/instrumentation.mjs b/example/opentelemetry/instrumentation.mjs new file mode 100644 index 0000000..3ef1f88 --- /dev/null +++ b/example/opentelemetry/instrumentation.mjs @@ -0,0 +1,25 @@ +import "dotenv/config"; +import { NodeSDK } from "@opentelemetry/sdk-node"; +import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics"; +import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-proto"; +import process from "process"; + +const sdk = new NodeSDK({ + metricReader: new PeriodicExportingMetricReader({ + exportIntervalMillis: 5000, + exporter: new OTLPMetricExporter({ + concurrencyLimit: 1, + }), + }), +}); +sdk.start(); + +process.on("exit", () => { + sdk + .shutdown() + .then( + () => console.log("SDK shut down successfully"), + (err) => console.log("Error shutting down SDK", err) + ) + .finally(() => process.exit(0)); +}); \ No newline at end of file diff --git a/example/opentelemetry/opentelemetry.mjs b/example/opentelemetry/opentelemetry.mjs new file mode 100644 index 0000000..63c82a1 --- /dev/null +++ b/example/opentelemetry/opentelemetry.mjs @@ -0,0 +1,80 @@ +import "dotenv/config"; +import { CredentialsMethod, FgaApiValidationError, OpenFgaClient } from "@openfga/sdk"; + +let credentials; +if (process.env.FGA_CLIENT_ID) { + credentials = { + method: CredentialsMethod.ClientCredentials, + config: { + clientId: process.env.FGA_CLIENT_ID, + clientSecret: process.env.FGA_CLIENT_SECRET, + apiAudience: process.env.FGA_API_AUDIENCE, + apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER + } + }; +} + +const fgaClient = new OpenFgaClient({ + apiUrl: process.env.FGA_API_URL, + storeId: process.env.FGA_STORE_ID, + authorizationModelId: process.env.FGA_MODEL_ID, + credentials +}); + +async function main () { + + setTimeout(async () => { + try { + await main(); + } catch (error) { + console.log(error); + } + }, 20000); + + console.log("Reading Authorization Models"); + const { authorization_models } = await fgaClient.readAuthorizationModels(); + console.log(`Models Count: ${authorization_models.length}`); + + console.log("Reading Tuples"); + const { tuples } = await fgaClient.read(); + console.log(`Tuples count: ${tuples.length}`); + + + const checkRequests = Math.floor(Math.random() * 10); + console.log(`Making ${checkRequests} checks`); + for (let index = 0; index < checkRequests; index++) { + console.log("Checking for access" + index); + try { + const { allowed } = await fgaClient.check({ + user: "user:anne", + relation: "owner", + object: "folder:foo" + }); + console.log(`Allowed: ${allowed}`); + } catch (error) { + if (error instanceof FgaApiValidationError) { + console.log(`Failed due to ${error.apiErrorMessage}`); + } else { + throw error; + } + } + } + + console.log("writing tuple"); + await fgaClient.write({ + writes: [ + { + user: "user:anne", + relation: "owner", + object: "folder:date-"+Date.now(), + } + ] + }); +} + + +main() + .catch(error => { + console.error(`error: ${error}`); + process.exitCode = 1; + }); diff --git a/example/opentelemetry/package.json b/example/opentelemetry/package.json new file mode 100644 index 0000000..5da2adc --- /dev/null +++ b/example/opentelemetry/package.json @@ -0,0 +1,22 @@ +{ + "name": "openfga-opentelemetry-example", + "private": "true", + "version": "1.0.0", + "description": "A bare bones example of using the OpenFGA SDK with OpenTelemetry metrics reporting", + "author": "OpenFGA", + "license": "Apache-2.0", + "scripts": { + "start": "node --import ./instrumentation.mjs opentelemetry.mjs" + }, + "dependencies": { + "@openfga/sdk": "^0.6.1", + "@opentelemetry/exporter-metrics-otlp-proto": "^0.52.1", + "@opentelemetry/exporter-prometheus": "^0.52.1", + "@opentelemetry/sdk-metrics": "^1.25.1", + "@opentelemetry/sdk-node": "^0.52.1", + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=16.13.0" + } +}