Repository URL to install this package:
|
Version:
0.6.5 ▾
|
const process = require('node:process');
const consts = require('@supertenant/superconsts');
/**
* @typedef {import('./binding').SpanID} SpanID
* @typedef {import('./binding').SpanLabels} SpanLabels
* @typedef {{
* PollInterval: number
* }} WaitActionDefinition
*
* @typedef {{
* HttpReject: {
* http_rc: number
* }
* }} HttpRejectActionDefinition
*
* @typedef {{
* SqlReject: {
* sql_rc: number,
* error_message: string,
* sql_state: string
* }
* }} SqlRejectActionDefinition
*
* @typedef {{} | WaitActionDefinition | HttpRejectActionDefinition | SqlRejectActionDefinition} ActionDefinition
* @typedef {{
* Action: number
* Definition: ActionDefinition
* }} Action
*/
/** @type {Action} */
const executeAction = {
Action: consts.Action.Execute,
Definition: {}
};
class SuperBrain {
constructor() {
this._initialized = false;
this._lib = null;
this._config = null;
this._serverless = ['y', 'yes', 'true', 't', '1'].indexOf(
(process.env.SUPERTENANT_SUPERBRAIN_SERVERLESS ?? '').toLowerCase()) !== -1;
this._integration_circuit_breakers =
(process.env.SUPERTENANT_SUPERMETER_INTEGRATION_CIRCUIT_BREAKERS ?? '').split(',').map((s) => s.trim());
}
/**
* @param {string?} [configPath]
* @returns {boolean}
*/
init(configPath) {
if (this._initialized) {
return this._lib != null;
}
this._initialized = true;
configPath = configPath ?? '';
if (typeof (configPath) !== 'string') {
throw 'configPath must be a string (or null/undefined)';
}
this._lib = require('./binding.js');
let version = 'unknown';
try {
version = process.env.npm_package_version;
version ||= 'unknown';
} catch (e) {
}
// copy environment variables from node to Go Brain so they can be used when initializing it.
for (const key in process.env) {
if (typeof key === 'string') {
if (key.startsWith('SUPERTENANT') || key.startsWith('SUPERMETER')) {
const val = process.env[key];
if (typeof val === 'string') {
this._lib.setenv(key, val);
}
}
}
}
try {
if (!this._lib.init(version, configPath, 'nodejs', process.version)) {
this._lib = null;
return false;
}
} catch (e) {
this._lib = null;
return false;
}
this._updateConfig();
return true;
}
/**
* @param {number?} [timeout]
* @returns {boolean}
*/
shutdown(timeout) {
timeout = timeout ?? 10000;
if (typeof timeout != 'number' || timeout < 0) {
throw 'timeout must be a non-negative number (or null/undefined)';
}
if (!this._initialized) {
return true;
}
return this._lib.shutdown(timeout);
}
/**
* @returns {boolean}
*/
isServerless() {
if (!this._initialized || this._lib == null) {
return this._serverless;
}
return this._lib.is_serverless();
}
/**
* @returns {boolean}
*/
isCircuitBreakerEnabled() {
if (!this._initialized || this._lib == null) {
return false;
}
return this._lib.is_circuit_breaker_enabled();
}
/**
* @param {string} [integrationModule]
* @returns {boolean}
*/
isIntegrationCircuitBreakerEnabled(integrationModule) {
if (typeof integrationModule !== 'string' || integrationModule === '') {
throw 'empty/non-string integrationModule';
}
if (this._integration_circuit_breakers.indexOf(integrationModule) !== -1) {
return true;
}
if (!this._initialized || this._lib == null) {
return false;
}
return this._lib.is_integration_circuit_breaker_enabled(integrationModule);
}
/**
* @returns {boolean}
*/
enableCircuitBreaker() {
if (!this._initialized || this._lib == null) {
return false;
}
return this._lib.enable_circuit_breaker();
}
/**
* @param {import('@supertenant/superconsts').BrainLogLevel} [level]
* @param {string} [msg]
* @returns {boolean}
*/
log(level, msg) {
if (!this._initialized || this._lib == null) {
return;
}
if (typeof level !== 'number' || typeof msg !== 'string') {
throw 'level must be a number and msg must be a string';
}
this._lib.log(level, msg);
}
/**
* @param {number} [taskId]
* @param {import('@supertenant/superconsts').SpanType} [spanType]
* @param {import('./binding.js').SpanLabels} [labels]
* @returns {import('./binding.js').JSOpenSpanResult?}
*/
openSpan(taskId, spanType, labels) {
if (!this._initialized || this._lib == null) {
return null;
}
return this._lib.open_span(taskId, spanType, labels);
}
/**
* @param {import('./binding.js').OpenSpanResult} [openSpanResult]
* @returns {Action}
*/
pollSpanAction(openSpanResult) {
if (!this._initialized || this._lib == null) {
return executeAction;
}
const actionRef = this._lib.poll_span_action(openSpanResult);
// short circuit: execute action always have the same ID.
if (actionRef == consts.Action.Execute) {
return executeAction;
}
return this._getAction(actionRef);
}
/**
* @param {SpanID} [spanId]
* @param {SpanLabels} [spanLabels]
* @returns {boolean}
*/
closeSpan(spanId, spanLabels) {
if (!this._initialized || this._lib == null) {
return false;
}
return this._lib.close_span(spanId, spanLabels);
}
/**
* @param {SpanID} [spanId]
* @returns {boolean}
*/
_debugHasSpan(spanId) {
if (!this._initialized || this._lib == null) {
return false;
}
return this._lib._debug_has_span(spanId);
}
/**
* @param {number} parentTaskId
* @param {number} taskId
* @param {import('@supertenant/superconsts').Task} [taskType]
* @returns {boolean}
*/
createTask(parentTaskId, taskId, taskType) {
if (!this._initialized || this._lib == null) {
return false;
}
return this._lib.create_task(parentTaskId, taskId, taskType);
}
/**
* @param {number} parentTaskId
* @param {import('@supertenant/superconsts').Task} [taskType]
* @returns {number}
*/
createTaskAutoInc(parentTaskId, taskType) {
if (!this._initialized || this._lib == null) {
return 0;
}
return this._lib.create_task_auto_inc(parentTaskId, taskType);
}
/**
* @param {number} taskId
* @returns {boolean}
*/
finishTask(taskId) {
if (!this._initialized || this._lib == null) {
return false;
}
return this._lib.finish_task(taskId);
}
/**
* @returns {void}
*/
printVersion() {
if (!this._initialized || this._lib == null) {
console.log('@supertenant/superbrain printVersion: not initialized');
return;
}
return this._lib.print_version();
}
/**
* @param {number} actionId
* @returns {Action}
*/
_getAction(actionId) {
if (actionId == consts.Action.Execute) {
return executeAction;
}
if (this._config != null && this._config.hasOwnProperty(actionId)) {
return this._config[actionId];
}
// TODO don't try to update config if updated it very recently.
this._updateConfig(); // action ID wasn't found - need to refresh config to see if now we find it.
if (this._config != null && this._config.hasOwnProperty(actionId)) {
return this._config[actionId];
}
// TODO log this only every X seconds.
this.log(
consts.BrainLogLevel.Error,
'actionId ' + actionId + ' not found in ' + this._config + ', defaulting to Execute');
return executeAction;
}
/**
* @returns {boolean}
*/
_updateConfig() {
if (!this._initialized || this._lib == null) {
return false;
}
let configStr = this._lib.get_config();
try {
this._config = JSON.parse(configStr);
return true;
} catch (err) {
this.log(consts.BrainLogLevel.Error, 'failed to parse config: ' + err);
return false;
}
}
}
/** @type {SuperBrain} */
module.exports = new SuperBrain();