Skip to content

Commit

Permalink
feat(js-sdk): add example js project (openfga#305)
Browse files Browse the repository at this point in the history
  • Loading branch information
ewanharris authored Feb 7, 2024
2 parents 30f38e4 + 2bfc7a1 commit 77f822b
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 0 deletions.
10 changes: 10 additions & 0 deletions config/clients/js/config.overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@
"npmrc.mustache": {
"destinationFilename": ".npmrc",
"templateType": "SupportingFiles"
},
"example/Makefile": {},
"example/README.md.mustache": {
"destinationFilename": "example/README.md",
"templateType": "SupportingFiles"
},
"example/example1/example1.mjs": {},
"example/example1/package.json.mustache": {
"destinationFilename": "example/example1/package.json",
"templateType": "SupportingFiles"
}
}
}
14 changes: 14 additions & 0 deletions config/clients/js/template/example/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
all: run

project_name=example1
openfga_version=latest

setup:
npm --prefix "${project_name}" install

run:
npm --prefix "${project_name}" run start

run-openfga:
docker pull docker.io/openfga/openfga:${openfga_version} && \
docker run -p 8080:8080 docker.io/openfga/openfga:${openfga_version} run
42 changes: 42 additions & 0 deletions config/clients/js/template/example/README.md.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
## Examples of using the OpenFGA JS SDK

A set of Examples on how to call the OpenFGA JS SDK

### Examples
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.

### Running the Examples

Prerequisites:
- `docker`
- `make`
- `Node.js` 16.13.0+

#### Run using a published SDK

Steps
1. Clone/Copy the example folder
2. If you have an OpenFGA server running, you can use it, otherwise run `make run-openfga` to spin up an instance (you'll need to switch to a different terminal after - don't forget to close it when done)
3. Run `make setup` to install dependencies
4. Run `make run` to run the example

#### Run using a local unpublished SDK build

Steps
1. Build the SDK
2. In the Example `package.json` change the `@openfga/sdk` dependency from a semver range like below
```json
"dependencies": {
"@openfga/sdk": "^{{packageVersion}}"
}
```
to a `file:` reference like below
```json
"dependencies": {
"@openfga/sdk": "file:../../"
}
```
3. If you have an OpenFGA server running, you can use it, otherwise run `make run-openfga` to spin up an instance (you'll need to switch to a different terminal after - don't forget to close it when done)
4. Run `make setup` to install dependencies
5. Run `make run` to run the example
213 changes: 213 additions & 0 deletions config/clients/js/template/example/example1/example1.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import { Credentials, CredentialsMethod, FgaApiValidationError, OpenFgaClient, TypeName } from "@openfga/sdk";

async function main () {
let credentials;
if (process.env.FGA_CLIENT_ID) {
credentials = new 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 || "http://localhost:8080",
storeId: process.env.FGA_STORE_ID, // not needed when calling `createStore` or `listStores`
authorizationModelId: process.env.FGA_AUTHORIZATION_MODEL_ID, // optional, recommended for production,
credentials
});

console.log("Listing stores");
const initialStores = await fgaClient.listStores();
console.log(`Stores count: ${initialStores.stores.length}`);

console.log("Creating Test Store");
const createdStore = await fgaClient.createStore({name: "Test Store"});
const store = createdStore;
console.log(`Test Store ID: ${store.id}`);

// Set the store ID
fgaClient.storeId = store.id;

console.log("Listing Stores");
const { stores } = await fgaClient.listStores();
console.log(`Stores count: ${stores.length}`);

console.log("Getting Current Store");
const currentStore = await fgaClient.getStore();
console.log(`Current Store Name ${currentStore.name}`);

console.log("Reading Authorization Models");
const { authorization_models } = await fgaClient.readAuthorizationModels();
console.log(`Models Count: ${authorization_models.length}`);

console.log("Reading Latest Authorization Model");
const { authorization_model } = await fgaClient.readLatestAuthorizationModel();
if (authorization_model) {
console.log(`Latest Authorization Model ID: ${latestModel.authorization_model.id}`);
} else {
console.log("Latest Authorization Model not found");
}

console.log("Writing an Authorization Model");
const model = await fgaClient.writeAuthorizationModel({
schema_version: "1.1",
type_definitions: [
{
type: "user"
},
{
type: "document",
relations: {
writer: { this: {} },
viewer: {
union: {
child: [
{ this: {} },
{
computedUserset: {
relation: "writer"
}
}
]
}
}
},
metadata: {
relations: {
writer: {
directly_related_user_types: [
{ type: "user" },
{ type: "user", condition: "ViewCountLessThan200" }
]
},
viewer: {
directly_related_user_types: [
{ type: "user" }
]
}
}
}
},
],
conditions: {
"ViewCountLessThan200": {
name: "ViewCountLessThan200",
expression: "ViewCount < 200",
parameters: {
ViewCount: {
type_name: TypeName.Int
},
Type: {
type_name: TypeName.String
},
Name: {
type_name: TypeName.String
}
}
}
}
});
const authorizationModelId = model.authorization_model_id;
console.log(`Authorization Model ID: ${authorizationModelId}`);

console.log("Reading Authorization Models");
const models = await fgaClient.readAuthorizationModels();
console.log(`Models Count: ${models.authorization_models.length}`);

console.log("Reading Latest Authorization Model");
const latestModel = await fgaClient.readLatestAuthorizationModel();
console.log(`Latest Authorization Model ID: ${latestModel.authorization_model.id}`);

console.log("Writing Tuples");
await fgaClient.write({
writes: [
{
user: "user:anne",
relation: "writer",
object: "document:roadmap",
condition: {
name: "ViewCountLessThan200",
context: {
Name: "Roadmap",
Type: "document"
}
}
}
]
}, { authorizationModelId });
console.log("Done Writing Tuples");

// Set the model ID
fgaClient.authorizationModelId = authorizationModelId;

console.log("Reading Tuples");
const { tuples } = await fgaClient.read();
console.log(`Read Tuples: ${JSON.stringify(tuples)}`);

console.log("Reading Tuple Changes");
const { changes } = await fgaClient.readChanges();
console.log(`Tuple Changes: ${JSON.stringify(changes)}`);

console.log("Checking for access");
try {
const { allowed } = await fgaClient.check({
user: "user:anne",
relation: "viewer",
object: "document:roadmap"
});
console.log(`Allowed: ${allowed}`);
} catch (error) {
if (error instanceof FgaApiValidationError) {
console.log(`Failed due to ${error.apiErrorMessage}`);
} else {
throw error;
}
}

console.log("Checking for access with context");
const { allowed } = await fgaClient.check({
user: "user:anne",
relation: "viewer",
object: "document:roadmap",
context: {
ViewCount: 100
}
});
console.log(`Allowed: ${allowed}`);

console.log("Writing Assertions");
await fgaClient.writeAssertions([
{
user: "user:carl",
relation: "writer",
object: "document:budget",
expectation: true
},
{
user: "user:carl",
relation: "writer",
object: "document:roadmap",
expectation: false
}
]);
console.log("Assertions Updated");

console.log("Reading Assertions");
const { assertions} = await fgaClient.readAssertions();
console.log(`Assertions: ${JSON.stringify(assertions)}`);

console.log("Deleting Current Store");
await fgaClient.deleteStore();
console.log(`Deleted Store Count: ${store.id}`);
}

main()
.catch(error => {
console.error(`error: ${error}`);
process.exitCode = 1;
});
17 changes: 17 additions & 0 deletions config/clients/js/template/example/example1/package.json.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "example1",
"private": "true",
"version": "1.0.0",
"description": "A bare bones example of using the OpenFGA SDK",
"author": "OpenFGA",
"license": "Apache-2.0",
"scripts": {
"start": "node example1.mjs"
},
"dependencies": {
"@openfga/sdk": "^{{packageVersion}}"
},
"engines": {
"node": ">=16.13.0"
}
}

0 comments on commit 77f822b

Please sign in to comment.