@devcycle/js-cloud-server-sdk
Version:
The DevCycle JS Cloud Bucketing Server SDK used for feature management.
169 lines • 7.13 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.DevCycleCloudClient = void 0;
const variable_1 = require("./models/variable");
const paramUtils_1 = require("./utils/paramUtils");
const logger_1 = require("./utils/logger");
const populatedUser_1 = require("./models/populatedUser");
const types_1 = require("@devcycle/types");
const request_1 = require("./request");
const user_1 = require("./models/user");
const server_request_1 = require("../server-request/src");
const EvalHooksRunner_1 = require("./hooks/EvalHooksRunner");
const castIncomingUser = (user) => {
if (!(user instanceof user_1.DevCycleUser)) {
return new user_1.DevCycleUser(user);
}
return user;
};
/**
* Handle response errors from API
* Suppress 5xx errors and log them instead while returning defaults to the caller
* Throw 4XX errors back to the caller to notify of invalid SDK usage
* Special handling of 404 error, which indicates "variable not found"
* @param err
*/
const throwIfUserError = (err) => {
if (err instanceof server_request_1.ResponseError) {
const code = err.status;
// throw the error if it indicates there was user error in this call
// (e.g. invalid auth credentials or bad user data)
if (code !== 404 && code < 500) {
throw new Error(`DevCycle request failed with status ${code}. ${err.message || ''}`);
}
// Catch non-4xx errors so we can log them instead
return;
}
if (err instanceof SyntaxError) {
// JSON parse error, log instead of throwing
return;
}
// if not a ResponseError, throw it
throw err;
};
class DevCycleCloudClient {
constructor(sdkKey, options, platformDetails) {
this.sdkKey = sdkKey;
this.logger =
options.logger || (0, logger_1.dvcDefaultLogger)({ level: options.logLevel });
this.options = options;
this.platformDetails = platformDetails;
this.hooksRunner = new EvalHooksRunner_1.EvalHooksRunner(options.hooks);
this.logger.info('Running DevCycle NodeJS SDK in Cloud Bucketing mode');
}
/**
* Always returns true for Cloud Client because once the client is created,
* it is initialized as all request are async to the API.
*/
get isInitialized() {
return true;
}
async variable(user, key, defaultValue) {
return this.hooksRunner.runHooksForEvaluation(user, key, defaultValue, {}, async (context) => { var _a; return this._variable((_a = context === null || context === void 0 ? void 0 : context.user) !== null && _a !== void 0 ? _a : user, key, defaultValue); });
}
async _variable(user, key, defaultValue) {
const incomingUser = castIncomingUser(user);
const populatedUser = populatedUser_1.DVCPopulatedUser.fromDVCUser(incomingUser, this.platformDetails);
(0, paramUtils_1.checkParamDefined)('key', key);
(0, paramUtils_1.checkParamDefined)('defaultValue', defaultValue);
const type = (0, types_1.getVariableTypeFromValue)(defaultValue, key, this.logger, true);
try {
const res = await (0, request_1.getVariable)(populatedUser, this.sdkKey, key, this.options);
const variableResponse = await res.json();
if (variableResponse.type !== type) {
this.logger.error(`Type mismatch for variable ${key}. Expected ${type}, got ${variableResponse.type}`);
// Default Variable
return new variable_1.DVCVariable({
key,
type,
defaultValue,
eval: {
reason: types_1.EVAL_REASONS.DEFAULT,
details: types_1.DEFAULT_REASON_DETAILS.TYPE_MISMATCH,
},
});
}
return new variable_1.DVCVariable({
key,
type,
defaultValue,
value: variableResponse.value,
eval: variableResponse.eval,
});
}
catch (err) {
throwIfUserError(err);
if (err instanceof server_request_1.ResponseError && err.status !== 404) {
this.logger.error(`Request to get variable: ${key} failed with response message: ${err.message}`);
}
// Default Variable
return new variable_1.DVCVariable({
key,
type,
defaultValue,
eval: {
reason: types_1.EVAL_REASONS.DEFAULT,
details: types_1.DEFAULT_REASON_DETAILS.ERROR,
},
});
}
}
async variableValue(user, key, defaultValue) {
return (await this.variable(user, key, defaultValue)).value;
}
async allVariables(user) {
const incomingUser = castIncomingUser(user);
const populatedUser = populatedUser_1.DVCPopulatedUser.fromDVCUser(incomingUser, this.platformDetails);
try {
const res = await (0, request_1.getAllVariables)(populatedUser, this.sdkKey, this.options);
const variablesResponse = await res.json();
return (variablesResponse || {});
}
catch (err) {
throwIfUserError(err);
this.logger.error(`Request to get all variable failed with response message: ${err.message}`);
return {};
}
}
async allFeatures(user) {
const incomingUser = castIncomingUser(user);
const populatedUser = populatedUser_1.DVCPopulatedUser.fromDVCUser(incomingUser, this.platformDetails);
try {
const res = await (0, request_1.getAllFeatures)(populatedUser, this.sdkKey, this.options);
const featuresResponse = await res.json();
return (featuresResponse || {});
}
catch (err) {
throwIfUserError(err);
this.logger.error(`Request to get all features failed with response message: ${err.message}`);
return {};
}
}
async track(user, event) {
const incomingUser = castIncomingUser(user);
if (event === undefined ||
event === null ||
typeof event.type !== 'string' ||
event.type.length === 0) {
throw new Error('Invalid Event');
}
(0, paramUtils_1.checkParamDefined)('type', event.type);
const populatedUser = populatedUser_1.DVCPopulatedUser.fromDVCUser(incomingUser, this.platformDetails);
try {
await (0, request_1.postTrack)(populatedUser, event, this.sdkKey, this.options);
this.logger.debug('DVC Event Tracked');
}
catch (err) {
throwIfUserError(err);
this.logger.error(`DVC Error Tracking Event. Response message: ${err.message}`);
}
}
async addHook(hook) {
this.hooksRunner.enqueue(hook);
}
async clearHooks() {
this.hooksRunner.clear();
}
}
exports.DevCycleCloudClient = DevCycleCloudClient;
//# sourceMappingURL=cloudClient.js.map
;