@zephr/saas-node-server-sdk
Version:
Zephr Node JS Server SDK
478 lines (402 loc) • 14.3 kB
JavaScript
var fetch = require('node-fetch');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
var id = 0;
function _classPrivateFieldLooseKey(name) {
return "__private_" + id++ + "_" + name;
}
function _classPrivateFieldLooseBase(receiver, privateKey) {
if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
throw new TypeError("attempted to use private field on non-instance");
}
return receiver;
}
const Deferred = () => {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return {
promise,
resolve,
reject
};
};
/**
* Zephr Access Controller
*
* @public
* @class
* @property {string} orgId The Zephr Organisation ID
* @property {string} apiEndpoint The Zephr API endpoint url
* @property {boolean} hasStarted Has the access controller started?
*/
var _accessParams = /*#__PURE__*/_classPrivateFieldLooseKey("accessParams");
var _accessState = /*#__PURE__*/_classPrivateFieldLooseKey("accessState");
var _updateCallbacks = /*#__PURE__*/_classPrivateFieldLooseKey("updateCallbacks");
var _deferredStart = /*#__PURE__*/_classPrivateFieldLooseKey("deferredStart");
var _getCurrentAccessState = /*#__PURE__*/_classPrivateFieldLooseKey("getCurrentAccessState");
var _runDynamicFeature = /*#__PURE__*/_classPrivateFieldLooseKey("runDynamicFeature");
var _runRuleDecision = /*#__PURE__*/_classPrivateFieldLooseKey("runRuleDecision");
var _triggerOnUpdate = /*#__PURE__*/_classPrivateFieldLooseKey("triggerOnUpdate");
class ZephrAccessController {
/**
* Create access controller instance
*
* @param {object} args Constructor arguments
* @param {string=} args.apiEndpoint The Zephr API endpoint url.
*/
constructor({
apiEndpoint
} = {}) {
Object.defineProperty(this, _triggerOnUpdate, {
value: _triggerOnUpdate2
});
Object.defineProperty(this, _runRuleDecision, {
value: _runRuleDecision2
});
Object.defineProperty(this, _runDynamicFeature, {
value: _runDynamicFeature2
});
Object.defineProperty(this, _getCurrentAccessState, {
value: _getCurrentAccessState2
});
this.hasStarted = false;
Object.defineProperty(this, _accessParams, {
writable: true,
value: {}
});
Object.defineProperty(this, _accessState, {
writable: true,
value: {}
});
Object.defineProperty(this, _updateCallbacks, {
writable: true,
value: []
});
Object.defineProperty(this, _deferredStart, {
writable: true,
value: void 0
});
if (!apiEndpoint) throw new Error('API endpoint must be provided');
this.apiEndpoint = apiEndpoint;
_classPrivateFieldLooseBase(this, _deferredStart)[_deferredStart] = Deferred();
}
/**
* Start Zephr access controller
*
* @description The Zephr access controller determines the current access state of a user
* @async
* @param {object} args Access controller arguments
* @param {string=} args.jwt Secure JWT token
* @param {string=} args.path URL path of initial page
* @returns {Promise<void|Error>} Promise resolves when started
*/
async startAccessController({
jwt,
path = '/',
...inputs
} = {}) {
console.debug('Starting access controller...');
if (!jwt) {
throw new Error('JWT token must be provided');
}
_classPrivateFieldLooseBase(this, _accessParams)[_accessParams].jwt = jwt;
_classPrivateFieldLooseBase(this, _accessParams)[_accessParams].path = path;
try {
const accessState = await _classPrivateFieldLooseBase(this, _getCurrentAccessState)[_getCurrentAccessState](inputs);
_classPrivateFieldLooseBase(this, _accessState)[_accessState] = accessState;
this.hasStarted = true;
_classPrivateFieldLooseBase(this, _deferredStart)[_deferredStart].resolve();
console.debug('Access controller has started successfully');
} catch (err) {
console.error('Access controller failed to start', err);
const errorReason = new Error('Failed to get access state.');
_classPrivateFieldLooseBase(this, _deferredStart)[_deferredStart].reject(errorReason);
}
}
/**
* Update Zephr access controller parameters
*
* @description Update Zephr access controller when user state has changed
* e.g. logged in/out
* @async
* @param {object} args Access controller arguments
* @param {string=} args.jwt Secure JWT token
* @param {string=} args.path URL path of current page
* @returns {Promise<void|Error>} Promise resolves when update has finished
*/
async updateAccessController({
jwt,
path,
...inputs
}) {
console.debug('Updating access controller...');
if (typeof jwt === 'string') {
_classPrivateFieldLooseBase(this, _accessParams)[_accessParams].jwt = jwt;
}
if (typeof path === 'string') {
_classPrivateFieldLooseBase(this, _accessParams)[_accessParams].path = path;
}
try {
const accessState = await _classPrivateFieldLooseBase(this, _getCurrentAccessState)[_getCurrentAccessState](inputs);
_classPrivateFieldLooseBase(this, _accessState)[_accessState] = accessState;
console.debug('Access controller has updated successfully');
_classPrivateFieldLooseBase(this, _triggerOnUpdate)[_triggerOnUpdate]();
} catch (err) {
console.error('Access controller failed to update', err);
}
}
/**
* Access controller ready promise
*
* @returns {Promise<void|Error>} Ready to use promise
*/
onReady() {
return _classPrivateFieldLooseBase(this, _deferredStart)[_deferredStart].promise;
}
/**
* Access controller update listener
*
* @param {Function} callback Callback when the user access state is updated
*/
onUpdate(callback = () => {}) {
if (callback && typeof callback === 'function') {
_classPrivateFieldLooseBase(this, _updateCallbacks)[_updateCallbacks].push(callback);
}
}
/**
* Check if a feature is enabled for the current user
*
* @param {string} featureSlug The feature slug to check
* @param {object} inputs Inputs for Zephr decision making
* @param {boolean} [defaultEnabled=false] Is the feature enabled by default
* @returns {Promise<boolean>} Is the feature enabled?
*/
async isFeatureEnabled(featureSlug, inputs = {}, defaultEnabled = false) {
var _classPrivateFieldLoo, _classPrivateFieldLoo2;
const feature = ((_classPrivateFieldLoo = (_classPrivateFieldLoo2 = _classPrivateFieldLooseBase(this, _accessState)[_accessState]) == null ? void 0 : _classPrivateFieldLoo2.features) != null ? _classPrivateFieldLoo : {})[featureSlug];
if (!feature) {
return defaultEnabled;
}
switch (feature.type) {
case 'STATIC':
{
return feature.enabled;
}
case 'DYNAMIC':
{
let featureResult;
try {
featureResult = await _classPrivateFieldLooseBase(this, _runDynamicFeature)[_runDynamicFeature](featureSlug, inputs);
} catch (err) {
console.error('Decision engine feature failed to execute', err);
return defaultEnabled;
}
if (Object.values(featureResult.meters).length) {
_classPrivateFieldLooseBase(this, _accessState)[_accessState].meters = { ..._classPrivateFieldLooseBase(this, _accessState)[_accessState].meters,
...featureResult.meters
};
}
return featureResult.enabled;
}
default:
console.error(`Feature ${featureSlug} with type ${feature.type} unrecognised`);
return defaultEnabled;
}
}
/**
* Check if a feature is enabled for the current user
*
* @param {string} ruleSlug The rule slug to execute
* @param {object} inputs Inputs for Zephr decision making
* @returns {Promise<object>} Is the feature enabled?
*/
async getRuleDecision(ruleSlug, inputs = {}) {
let ruleResult;
try {
ruleResult = await _classPrivateFieldLooseBase(this, _runRuleDecision)[_runRuleDecision](ruleSlug, inputs);
} catch (err) {
console.error('Rule decision failed to execute', err);
return null;
}
return ruleResult;
}
/**
* Check if the current user is authenticated
*
* @returns {boolean} Is the current user authenticated?
*/
isAuthenticated() {
var _classPrivateFieldLoo3, _classPrivateFieldLoo4;
return (_classPrivateFieldLoo3 = (_classPrivateFieldLoo4 = _classPrivateFieldLooseBase(this, _accessState)[_accessState]) == null ? void 0 : _classPrivateFieldLoo4.authenticated) != null ? _classPrivateFieldLoo3 : false;
}
/**
* Get all of the current users features
*
* @returns {object[]} Array of the current users features
*/
getAllFeatures() {
var _classPrivateFieldLoo5, _classPrivateFieldLoo6;
return (_classPrivateFieldLoo5 = (_classPrivateFieldLoo6 = _classPrivateFieldLooseBase(this, _accessState)[_accessState]) == null ? void 0 : _classPrivateFieldLoo6.features) != null ? _classPrivateFieldLoo5 : [];
}
/**
* Get all of the current users meters
*
* @returns {object[]} Array of the current users meters
*/
getAllMeters() {
var _classPrivateFieldLoo7, _classPrivateFieldLoo8;
return (_classPrivateFieldLoo7 = (_classPrivateFieldLoo8 = _classPrivateFieldLooseBase(this, _accessState)[_accessState]) == null ? void 0 : _classPrivateFieldLoo8.meters) != null ? _classPrivateFieldLoo7 : [];
}
/**
* Run the current access state API
*
* @private
* @async
* @param {object} inputs Inputs for Zephr decision making
* @returns {Promise<object>} Access state response
*/
}
async function _getCurrentAccessState2(inputs = {}) {
return fetch__default["default"](`${this.apiEndpoint}/access/currentState`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${_classPrivateFieldLooseBase(this, _accessParams)[_accessParams].jwt}`
},
body: JSON.stringify({ ...inputs,
...{
path: _classPrivateFieldLooseBase(this, _accessParams)[_accessParams].path
}
})
}).then(response => response.json()).catch(error => {
console.error(error);
return Promise.reject(error);
});
}
async function _runDynamicFeature2(featureSlug, inputs = {}) {
return fetch__default["default"](`${this.apiEndpoint}/access/feature/${featureSlug}`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${_classPrivateFieldLooseBase(this, _accessParams)[_accessParams].jwt}`
},
body: JSON.stringify({ ...inputs,
...{
path: _classPrivateFieldLooseBase(this, _accessParams)[_accessParams].path
}
})
}).then(async response => {
if (!response.ok) {
console.error('Dynamic feature bad response', await response.text());
throw new Error('Failed response.');
}
return response.json();
});
}
async function _runRuleDecision2(ruleSlug, inputs = {}) {
return fetch__default["default"](`${this.apiEndpoint}/access/decision/${ruleSlug}`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
...(_classPrivateFieldLooseBase(this, _accessParams)[_accessParams].jwt && {
Authorization: `Bearer ${_classPrivateFieldLooseBase(this, _accessParams)[_accessParams].jwt}`
})
},
body: JSON.stringify({ ...inputs,
...{
path: _classPrivateFieldLooseBase(this, _accessParams)[_accessParams].path
}
})
}).then(async response => {
if (!response.ok) {
console.error('Rule decision bad response', await response.text());
throw new Error('Failed response.');
}
return response.json();
});
}
function _triggerOnUpdate2() {
_classPrivateFieldLooseBase(this, _updateCallbacks)[_updateCallbacks].forEach(callback => callback());
}
/**
* Zephr Server
*
* @public
* @class
* @property {string} orgId The Zephr Organisation ID
* @property {string} apiEndpoint The Zephr API endpoint url
*/
class ZephrServer {
/**
* Create server instance
*
* @param {object} args Constructor arguments
* @param {string} args.orgId The Zephr Organisation ID
* @param {string=} args.apiEndpoint Override the Zephr API endpoint url. (Default: https://${orgId}.saas-public-api.zephr.com)
*/
constructor({
orgId,
apiEndpoint
} = {}) {
if (!orgId) throw new Error('Organisation ID must be provided');
this.orgId = orgId;
this.apiEndpoint = apiEndpoint || `https://${orgId}.saas-public-api.zephr.com`;
}
/**
* Create Zephr access controller instance for request
*
* @description The Zephr access controller determines the current access state of a user
* @async
* @param {object} args Access controller arguments
* @param {string=} args.jwt Secure JWT token
* @param {string=} args.path URL path of initial page
* @returns {Promise<ZephrAccessController|Error>} Promise resolves a running access controller
*/
async createAccessController({
jwt,
path = '/'
} = {}) {
const accessController = new ZephrAccessController({
apiEndpoint: this.apiEndpoint
});
await accessController.startAccessController({
jwt,
path
});
return accessController;
}
}
/**
* Create an instance of Zephr Server SDK
*
* @param {string} orgId The Zephr Organisation ID
* @param {object=} args Optional arguments
* @param {string=} args.apiEndpoint Override the Zephr API endpoint url
* @returns {ZephrServer} A ZephrServer instance
*/
const createInstance = (orgId, {
apiEndpoint
} = {}) => {
const server = new ZephrServer({
orgId,
apiEndpoint
});
return server;
};
/**
* @constant {string} version The version of the Zephr SDK
*/
const version = process.env.npm_package_version;
exports.createInstance = createInstance;
exports.version = version;
//# sourceMappingURL=zephr-server-sdk.cjs.map