Skip to content

Commit

Permalink
feat: enhancements to OpenTelemetry support
Browse files Browse the repository at this point in the history
  • Loading branch information
evansims authored and jimmyjames committed Sep 24, 2024
1 parent 09191e7 commit 721a44a
Show file tree
Hide file tree
Showing 18 changed files with 573 additions and 159 deletions.
84 changes: 42 additions & 42 deletions api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
CallResult,
PromiseResult
} from "./common";
import { attributeNames } from "./telemetry";
import { Configuration } from "./configuration";
import { Credentials } from "./credentials";
import { assertParamExists } from "./validation";
Expand Down Expand Up @@ -110,6 +109,7 @@ import {
WriteRequestDeletes,
WriteRequestWrites,
} from "./apiModel";
import { TelemetryAttributes } from "./telemetry/attributes";


/**
Expand Down Expand Up @@ -759,10 +759,10 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async check(storeId: string, body: CheckRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<CheckResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.check(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "check",
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestModelId]: body.authorization_model_id,
[attributeNames.user]: body.tuple_key.user
[TelemetryAttributes.fgaClientRequestMethod.name]: "Check",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId ?? "",
[TelemetryAttributes.fgaClientRequestModelId.name]: body.authorization_model_id ?? "",
[TelemetryAttributes.fgaClientUser.name]: body.tuple_key.user
});
},
/**
Expand All @@ -775,7 +775,7 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async createStore(body: CreateStoreRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<CreateStoreResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.createStore(body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "createStore",
[TelemetryAttributes.fgaClientRequestMethod.name]: "createStore",
});
},
/**
Expand All @@ -788,8 +788,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async deleteStore(storeId: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<void>> {
const localVarAxiosArgs = localVarAxiosParamCreator.deleteStore(storeId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "deleteStore",
[attributeNames.requestStoreId]: storeId,
[TelemetryAttributes.fgaClientRequestMethod.name]: "deleteStore",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId,
});
},
/**
Expand All @@ -803,9 +803,9 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async expand(storeId: string, body: ExpandRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ExpandResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.expand(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "expand",
[attributeNames.requestModelId]: body.authorization_model_id,
[attributeNames.requestStoreId]: storeId,
[TelemetryAttributes.fgaClientRequestMethod.name]: "expand",
[TelemetryAttributes.fgaClientRequestModelId.name]: body.authorization_model_id ?? "",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId ?? "",
});
},
/**
Expand All @@ -818,8 +818,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async getStore(storeId: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<GetStoreResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.getStore(storeId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "getStore",
[attributeNames.requestStoreId]: storeId,
[TelemetryAttributes.fgaClientRequestMethod.name]: "getStore",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId,
});
},
/**
Expand All @@ -833,14 +833,14 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async listObjects(storeId: string, body: ListObjectsRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ListObjectsResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.listObjects(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "listObjects",
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestModelId]: body.authorization_model_id,
[attributeNames.user]: body.user
[TelemetryAttributes.fgaClientRequestMethod.name]: "listObjects",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId ?? "",
[TelemetryAttributes.fgaClientRequestModelId.name]: body.authorization_model_id ?? "",
[TelemetryAttributes.fgaClientUser.name]: body.user
});
},
/**
* Returns a paginated list of OpenFGA stores and a continuation token to get additional stores. The continuation token will be empty if there are no more stores.
* Returns a paginated list of OpenFGA stores and a continuation token to get additional stores. The continuation token will be empty if there are no more stores.
* @summary List all stores
* @param {number} [pageSize]
* @param {string} [continuationToken]
Expand All @@ -850,7 +850,7 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async listStores(pageSize?: number, continuationToken?: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ListStoresResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.listStores(pageSize, continuationToken, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "listStores",
[TelemetryAttributes.fgaClientRequestMethod.name]: "listStores",
});
},
/**
Expand All @@ -864,9 +864,9 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async listUsers(storeId: string, body: ListUsersRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ListUsersResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.listUsers(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "listUsers",
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestModelId]: body.authorization_model_id,
[TelemetryAttributes.fgaClientRequestMethod.name]: "listUsers",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId ?? "",
[TelemetryAttributes.fgaClientRequestModelId.name]: body.authorization_model_id ?? "",
});
},
/**
Expand All @@ -880,12 +880,12 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async read(storeId: string, body: ReadRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ReadResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.read(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "read",
[attributeNames.requestStoreId]: storeId,
[TelemetryAttributes.fgaClientRequestMethod.name]: "read",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId,
});
},
/**
* The ReadAssertions API will return, for a given authorization model id, all the assertions stored for it. An assertion is an object that contains a tuple key, and the expectation of whether a call to the Check API of that tuple key will return true or false.
* The ReadAssertions API will return, for a given authorization model id, all the assertions stored for it. An assertion is an object that contains a tuple key, and the expectation of whether a call to the Check API of that tuple key will return true or false.
* @summary Read assertions for an authorization model ID
* @param {string} storeId
* @param {string} authorizationModelId
Expand All @@ -895,9 +895,9 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async readAssertions(storeId: string, authorizationModelId: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ReadAssertionsResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.readAssertions(storeId, authorizationModelId, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "readAssertions",
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestModelId]: authorizationModelId,
[TelemetryAttributes.fgaClientRequestMethod.name]: "readAssertions",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId,
[TelemetryAttributes.fgaClientRequestModelId.name]: authorizationModelId,
});
},
/**
Expand All @@ -911,8 +911,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async readAuthorizationModel(storeId: string, id: string, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<ReadAuthorizationModelResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.readAuthorizationModel(storeId, id, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "readAuthorizationModel",
[attributeNames.requestStoreId]: storeId,
[TelemetryAttributes.fgaClientRequestMethod.name]: "readAuthorizationModel",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId,
});
},
/**
Expand All @@ -927,8 +927,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 = localVarAxiosParamCreator.readAuthorizationModels(storeId, pageSize, continuationToken, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "readAuthorizationModels",
[attributeNames.requestStoreId]: storeId,
[TelemetryAttributes.fgaClientRequestMethod.name]: "readAuthorizationModels",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId,
});
},
/**
Expand All @@ -944,8 +944,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 = localVarAxiosParamCreator.readChanges(storeId, type, pageSize, continuationToken, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "readChanges",
[attributeNames.requestStoreId]: storeId,
[TelemetryAttributes.fgaClientRequestMethod.name]: "readChanges",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId,
});
},
/**
Expand All @@ -959,9 +959,9 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async write(storeId: string, body: WriteRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<object>> {
const localVarAxiosArgs = localVarAxiosParamCreator.write(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "write",
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestModelId]: body.authorization_model_id,
[TelemetryAttributes.fgaClientRequestMethod.name]: "write",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId ?? "",
[TelemetryAttributes.fgaClientRequestModelId.name]: body.authorization_model_id ?? "",
});
},
/**
Expand All @@ -976,9 +976,9 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async writeAssertions(storeId: string, authorizationModelId: string, body: WriteAssertionsRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<void>> {
const localVarAxiosArgs = localVarAxiosParamCreator.writeAssertions(storeId, authorizationModelId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "writeAssertions",
[attributeNames.requestStoreId]: storeId,
[attributeNames.requestModelId]: authorizationModelId,
[TelemetryAttributes.fgaClientRequestMethod.name]: "writeAssertions",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId,
[TelemetryAttributes.fgaClientRequestModelId.name]: authorizationModelId,
});
},
/**
Expand All @@ -992,8 +992,8 @@ export const OpenFgaApiFp = function(configuration: Configuration, credentials:
async writeAuthorizationModel(storeId: string, body: WriteAuthorizationModelRequest, options?: any): Promise<(axios?: AxiosInstance) => PromiseResult<WriteAuthorizationModelResponse>> {
const localVarAxiosArgs = localVarAxiosParamCreator.writeAuthorizationModel(storeId, body, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, configuration, credentials, {
[attributeNames.requestMethod]: "writeAuthorizationModel",
[attributeNames.requestStoreId]: storeId,
[TelemetryAttributes.fgaClientRequestMethod.name]: "writeAuthorizationModel",
[TelemetryAttributes.fgaClientRequestStoreId.name]: storeId,
});
},
};
Expand Down
66 changes: 44 additions & 22 deletions common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@


import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
import { metrics } from "@opentelemetry/api";

import { Configuration } from "./configuration";
import type { Credentials } from "./credentials";
Expand All @@ -26,17 +25,9 @@ import {
FgaError
} from "./errors";
import { setNotEnumerableProperty } from "./utils";
import { buildAttributes } from "./telemetry";

const meter = metrics.getMeter("@openfga/sdk", "0.6.3");
const durationHist = meter.createHistogram("fga-client.request.duration", {
description: "The duration of requests",
unit: "milliseconds",
});
const queryDurationHist = meter.createHistogram("fga-client.query.duration", {
description: "The duration of queries on the FGA server",
unit: "milliseconds",
});
import { TelemetryAttributes } from "./telemetry/attributes";
import { TelemetryMetrics } from "./telemetry/metrics";
import { TelemetryHistograms } from "./telemetry/histograms";

/**
*
Expand Down Expand Up @@ -127,6 +118,10 @@ export const toPathString = function (url: URL) {

type ObjectOrVoid = object | void;

interface StringIndexable {
[key: string]: any;
}

export type CallResult<T extends ObjectOrVoid> = T & {
$response: AxiosResponse<T>
};
Expand Down Expand Up @@ -192,7 +187,7 @@ export async function attemptHttpRequest<B, R>(
/**
* creates an axios request function
*/
export const createRequestFunction = function (axiosArgs: RequestArgs, axiosInstance: AxiosInstance, configuration: Configuration, credentials: Credentials, methodAttributes: Record<string, unknown> = {}) {
export const createRequestFunction = function (axiosArgs: RequestArgs, axiosInstance: AxiosInstance, configuration: Configuration, credentials: Credentials, methodAttributes: Record<string, string | number> = {}) {
configuration.isValid();

const retryParams = axiosArgs.options?.retryParams ? axiosArgs.options?.retryParams : configuration.retryParams;
Expand All @@ -209,22 +204,49 @@ export const createRequestFunction = function (axiosArgs: RequestArgs, axiosInst
maxRetry,
minWaitInMs,
}, axios);
const executionTime = Date.now() - start;

const data = typeof response?.data === "undefined" ? {} : response?.data;
const result: CallResult<any> = { ...data };
setNotEnumerableProperty(result, "$response", response);

const attributes = buildAttributes(response, configuration.credentials, methodAttributes);

if (response?.headers) {
const duration = response.headers["fga-query-duration-ms"];
if (duration !== undefined) {
queryDurationHist.record(parseInt(duration, 10), attributes);
}
const telemetryAttributes = new TelemetryAttributes();
const telemetryMetrics = new TelemetryMetrics();

let attributes: StringIndexable = {};

attributes = telemetryAttributes.fromRequest({
start: start,
credentials: credentials,
attributes: methodAttributes,
});

attributes = telemetryAttributes.fromResponse({
response,
credentials,
attributes,
});

if (attributes[TelemetryAttributes.httpServerRequestDuration.name]) {
telemetryMetrics.histogram(
TelemetryHistograms.queryDuration,
parseInt(attributes[TelemetryAttributes.httpServerRequestDuration.name] as string, 10),
telemetryAttributes.prepare(
attributes,
Object.keys(configuration.telemetryConfig.metrics.histogramQueryDuration.attributes())
)
);
}

durationHist.record(executionTime, attributes);
if (attributes[TelemetryAttributes.httpClientRequestDuration.name]) {
telemetryMetrics.histogram(
TelemetryHistograms.requestDuration,
Date.now() - start,
telemetryAttributes.prepare(
attributes,
Object.keys(configuration.telemetryConfig.metrics.histogramRequestDuration.attributes())
)
);
}

return result;
};
Expand Down
10 changes: 10 additions & 0 deletions configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import { ApiTokenConfig, AuthCredentialsConfig, ClientCredentialsConfig, CredentialsMethod } from "./credentials/types";
import { FgaValidationError, } from "./errors";
import { assertParamExists, isWellFormedUlidString, isWellFormedUriString } from "./validation";
import { TelemetryConfiguration } from './telemetry/configuration';

// default maximum number of retry
const DEFAULT_MAX_RETRY = 15;
Expand Down Expand Up @@ -41,6 +42,7 @@ export interface UserConfigurationParams {
credentials?: CredentialsConfig;
baseOptions?: any;
retryParams?: RetryParams;
telemetryConfig?: TelemetryConfiguration;
}

export function GetDefaultRetryParams (maxRetry = DEFAULT_MAX_RETRY, minWaitInMs = DEFAULT_MIN_WAIT_MS) {
Expand Down Expand Up @@ -120,6 +122,13 @@ export class Configuration {
* @memberof Configuration
*/
retryParams?: RetryParams;
/**
* telemetry configuration
*
* @type {TelemetryConfiguration}
* @memberof Configuration
*/
telemetryConfig: TelemetryConfiguration;

constructor(params: UserConfigurationParams = {} as unknown as UserConfigurationParams) {
this.apiScheme = params.apiScheme || this.apiScheme;
Expand Down Expand Up @@ -168,6 +177,7 @@ export class Configuration {

this.baseOptions = baseOptions;
this.retryParams = params.retryParams;
this.telemetryConfig = params.telemetryConfig || new TelemetryConfiguration();
}

/**
Expand Down
Loading

0 comments on commit 721a44a

Please sign in to comment.