Skip to content

Commit

Permalink
feat: support apiUrl configuration option
Browse files Browse the repository at this point in the history
  • Loading branch information
ewanharris committed Feb 2, 2024
1 parent d607784 commit 847c6a4
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 20 deletions.
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ The documentation below refers to the `OpenFgaClient`, to read the documentation
const { OpenFgaClient } = require('@openfga/sdk'); // OR import { OpenFgaClient } from '@openfga/sdk';

const fgaClient = new OpenFgaClient({
apiScheme: process.env.FGA_API_SCHEME, // optional, defaults to "https"
apiHost: process.env.FGA_API_HOST, // required, define without the scheme (e.g. api.fga.example instead of https://api.fga.example)
apiUrl: process.env.FGA_API_URL, // required
storeId: process.env.FGA_STORE_ID, // not needed when calling `CreateStore` or `ListStores`
authorizationModelId: process.env.FGA_AUTHORIZATION_MODEL_ID, // Optional, can be overridden per request
});
Expand All @@ -107,8 +106,7 @@ const fgaClient = new OpenFgaClient({
const { OpenFgaClient } = require('@openfga/sdk'); // OR import { OpenFgaClient } from '@openfga/sdk';

const fgaClient = new OpenFgaClient({
apiScheme: process.env.FGA_API_SCHEME, // optional, defaults to "https"
apiHost: process.env.FGA_API_HOST, // required, define without the scheme (e.g. api.fga.example instead of https://api.fga.example)
apiUrl: process.env.FGA_API_URL, // required
storeId: process.env.FGA_STORE_ID, // not needed when calling `CreateStore` or `ListStores`
authorizationModelId: process.env.FGA_AUTHORIZATION_MODEL_ID, // Optional, can be overridden per request
credentials: {
Expand All @@ -126,8 +124,7 @@ const fgaClient = new OpenFgaClient({
const { OpenFgaClient } = require('@openfga/sdk'); // OR import { OpenFgaClient } from '@openfga/sdk';

const fgaClient = new OpenFgaClient({
apiScheme: process.env.FGA_API_SCHEME, // optional, defaults to "https"
apiHost: process.env.FGA_API_HOST, // required, define without the scheme (e.g. api.fga.example instead of https://api.fga.example)
apiUrl: process.env.FGA_API_URL, // required
storeId: process.env.FGA_STORE_ID, // not needed when calling `CreateStore` or `ListStores`
authorizationModelId: process.env.FGA_AUTHORIZATION_MODEL_ID, // Optional, can be overridden per request
credentials: {
Expand Down
39 changes: 34 additions & 5 deletions configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,15 @@ export interface RetryParams {
}

export interface UserConfigurationParams {
apiUrl?: string;
/**
* @deprecated Replace usage of `apiScheme` + `apiHost` with `apiUrl`
*/
apiScheme?: string;
apiHost: string;
/**
* @deprecated Replace usage of `apiScheme` + `apiHost` with `apiUrl`
*/
apiHost?: string;
storeId?: string;
credentials?: CredentialsConfig;
baseOptions?: any;
Expand Down Expand Up @@ -69,18 +76,28 @@ export class Configuration {
*/
private static sdkVersion = "0.3.1";

/**
* provide the full api URL
*
* @type {string}
* @memberof Configuration
*/
apiUrl: string;

/**
* provide scheme (e.g. `https`)
*
* @type {string}
* @memberof Configuration
* @deprecated
*/
apiScheme = "https";
/**
* provide server host (e.g. `api.fga.example`)
*
* @type {string}
* @memberof Configuration
* @deprecated
*/
apiHost: string;
/**
Expand Down Expand Up @@ -115,6 +132,7 @@ export class Configuration {
constructor(params: UserConfigurationParams = {} as unknown as UserConfigurationParams) {
this.apiScheme = params.apiScheme || this.apiScheme;
this.apiHost = params.apiHost!;
this.apiUrl = params.apiUrl!;
this.storeId = params.storeId!;

const credentialParams = params.credentials;
Expand Down Expand Up @@ -167,12 +185,17 @@ export class Configuration {
* @throws {FgaValidationError}
*/
public isValid(): boolean {
assertParamExists("Configuration", "apiScheme", this.apiScheme);
assertParamExists("Configuration", "apiHost", this.apiHost);
if (!this.apiUrl) {
assertParamExists("Configuration", "apiScheme", this.apiScheme);
assertParamExists("Configuration", "apiHost", this.apiHost);
}

if (!isWellFormedUriString(this.getBasePath())) {
throw new FgaValidationError(
`Configuration.apiScheme (${this.apiScheme}) and Configuration.apiHost (${this.apiHost}) do not form a valid URI (${this.getBasePath()})`);
this.apiUrl ?
`Configuration.apiUrl (${this.apiUrl}) is not a valid URI (${this.getBasePath()})` :
`Configuration.apiScheme (${this.apiScheme}) and Configuration.apiHost (${this.apiHost}) do not form a valid URI (${this.getBasePath()})`
);
}

if (this.storeId && !isWellFormedUlidString(this.storeId)) {
Expand All @@ -189,5 +212,11 @@ export class Configuration {
/**
* Returns the API base path (apiScheme+apiHost)
*/
public getBasePath: () => string = () => `${this.apiScheme}://${this.apiHost}`;
public getBasePath: () => string = () => {
if (this.apiUrl) {
return this.apiUrl;
} else {
return `${this.apiScheme}://${this.apiHost}`;
}
};
}
4 changes: 2 additions & 2 deletions tests/helpers/default-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { Configuration, UserConfigurationParams } from "../../configuration";
import { CredentialsMethod } from "../../credentials";

export const OPENFGA_STORE_ID = "01H0H015178Y2V4CX10C2KGHF4";
export const OPENFGA_API_HOST = "api.fga.example";
export const OPENFGA_API_URL = "http://api.fga.example";
export const OPENFGA_API_TOKEN_ISSUER = "tokenissuer.fga.example";
export const OPENFGA_API_AUDIENCE = "https://api.fga.example/";
export const OPENFGA_CLIENT_ID = "01H0H3D8TD07EWAQHXY9BWJG3V";
Expand All @@ -24,7 +24,7 @@ export const OPENFGA_API_TOKEN = "fga_abcdef";

export const baseConfig: UserConfigurationParams = {
storeId: OPENFGA_STORE_ID,
apiHost: OPENFGA_API_HOST,
apiUrl: OPENFGA_API_URL,
credentials: {
method: CredentialsMethod.ClientCredentials,
config: {
Expand Down
26 changes: 19 additions & 7 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { AuthCredentialsConfig } from "../credentials";
import {
baseConfig,
defaultConfiguration,
OPENFGA_API_HOST,
OPENFGA_API_URL,
OPENFGA_API_TOKEN_ISSUER,
OPENFGA_STORE_ID
} from "./helpers/default-config";
Expand Down Expand Up @@ -64,16 +64,28 @@ describe("OpenFGA SDK", function () {

it("should require host in configuration", () => {
expect(
() => new OpenFgaApi({ ...baseConfig, apiHost: undefined! })
() => new OpenFgaApi({ ...baseConfig, apiUrl: undefined! })
).toThrowError();
});

it("should validate host in configuration (adding scheme as part of the host)", () => {
expect(
() => new OpenFgaApi({ ...baseConfig, apiHost: "https://api.fga.example" })
() => new OpenFgaApi({ ...baseConfig, apiUrl: "//api.fga.example" })
).toThrowError();
});

it("should allow using apiHost if apiUrl is not provided", () => {
expect(
() => new OpenFgaApi({ ...baseConfig, apiHost: "api.fga.example" })
).not.toThrowError();
});

it("should still validate apiHost", () => {
expect(
() => new OpenFgaApi({ ...baseConfig, apiHost: "//api.fga.example" })
).not.toThrowError();
});

it("should validate apiTokenIssuer in configuration (should not allow scheme as part of the apiTokenIssuer)", () => {
expect(
() => new OpenFgaApi({
Expand All @@ -94,7 +106,7 @@ describe("OpenFGA SDK", function () {
() =>
new OpenFgaApi({
storeId: baseConfig.storeId!,
apiHost: baseConfig.apiHost,
apiUrl: baseConfig.apiUrl,
})
).not.toThrowError();
});
Expand All @@ -104,7 +116,7 @@ describe("OpenFGA SDK", function () {
() =>
new OpenFgaApi({
storeId: baseConfig.storeId!,
apiHost: baseConfig.apiHost,
apiUrl: baseConfig.apiUrl,
credentials: {
method: CredentialsMethod.ApiToken as any
}
Expand Down Expand Up @@ -240,7 +252,7 @@ describe("OpenFGA SDK", function () {

const fgaApi = new OpenFgaApi({
storeId: baseConfig.storeId!,
apiHost: baseConfig.apiHost,
apiUrl: baseConfig.apiUrl,
});
expect(scope.isDone()).toBe(false);

Expand All @@ -258,7 +270,7 @@ describe("OpenFGA SDK", function () {

it("should allow updating the storeId after initialization", async () => {
const fgaApi = new OpenFgaApi({
apiHost: OPENFGA_API_HOST
apiUrl: OPENFGA_API_URL
});
expect(fgaApi.storeId).toBe(undefined);
fgaApi.storeId = OPENFGA_STORE_ID;
Expand Down

0 comments on commit 847c6a4

Please sign in to comment.