mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-20 08:40:38 +00:00
major revamp of http client functionalit
This commit is contained in:
@@ -2,25 +2,39 @@ const Emitter = require('events');
|
||||
const debug = require('debug')('jambonz:feature-server');
|
||||
const assert = require('assert');
|
||||
const {TaskPreconditions} = require('../utils/constants');
|
||||
const normalizeJamones = require('../utils/normalize-jamones');
|
||||
const makeTask = require('./make_task');
|
||||
const specs = new Map();
|
||||
const _specData = require('./specs');
|
||||
for (const key in _specData) {specs.set(key, _specData[key]);}
|
||||
|
||||
/**
|
||||
* @classdesc Represents a jambonz verb. This is a superclass that is extended
|
||||
* by a subclass for each verb.
|
||||
* @extends Emitter
|
||||
*/
|
||||
class Task extends Emitter {
|
||||
constructor(logger, data) {
|
||||
super();
|
||||
this.preconditions = TaskPreconditions.None;
|
||||
this.logger = logger;
|
||||
this.data = data;
|
||||
this.actionHook = this.data.actionHook;
|
||||
|
||||
this._killInProgress = false;
|
||||
this._completionPromise = new Promise((resolve) => this._completionResolver = resolve);
|
||||
}
|
||||
|
||||
/**
|
||||
* @property {boolean} killed - true if the task has been killed
|
||||
*/
|
||||
get killed() {
|
||||
return this._killInProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @property {CallSession} callSession - the CallSession this task is executing within
|
||||
*/
|
||||
get callSession() {
|
||||
return this.cs;
|
||||
}
|
||||
@@ -29,13 +43,13 @@ class Task extends Emitter {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the task. Subclasses must implement this method, but should always call
|
||||
* the superclass implementation first.
|
||||
* @param {CallSession} cs - the CallSession that the Task will be executing within.
|
||||
*/
|
||||
async exec(cs) {
|
||||
this.cs = cs;
|
||||
|
||||
// N.B. need to require it down here rather than at top to avoid recursion in require of this module
|
||||
const {actionHook, notifyHook} = require('../utils/notifiers')(this.logger, cs.callInfo);
|
||||
this.actionHook = actionHook;
|
||||
this.notifyHook = notifyHook;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,29 +62,47 @@ class Task extends Emitter {
|
||||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* when a subclass Task has completed its work, it should call this method
|
||||
*/
|
||||
notifyTaskDone() {
|
||||
this._completionResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* when a subclass task has launched various async activities and is now simply waiting
|
||||
* for them to complete it should call this method to block until that happens
|
||||
*/
|
||||
awaitTaskDone() {
|
||||
return this._completionPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* provided as a convenience for tasks, this simply calls CallSession#normalizeUrl
|
||||
*/
|
||||
normalizeUrl(url, method, auth) {
|
||||
return this.callSession.normalizeUrl(url, method, auth);
|
||||
}
|
||||
|
||||
async performAction(method, auth, results, expectResponse = true) {
|
||||
if (this.action) {
|
||||
const hook = this.normalizeUrl(this.action, method, auth);
|
||||
const tasks = await this.actionHook(hook, results, expectResponse);
|
||||
if (expectResponse && tasks && Array.isArray(tasks)) {
|
||||
this.logger.debug({tasks: tasks}, `${this.name} replacing application with ${tasks.length} tasks`);
|
||||
this.callSession.replaceApplication(tasks);
|
||||
async performAction(results, expectResponse = true) {
|
||||
if (this.actionHook) {
|
||||
const params = results ? Object.assign(results, this.cs.callInfo) : this.cs.callInfo;
|
||||
const json = await this.cs.requestor.request(this.actionHook, params);
|
||||
if (expectResponse && json && Array.isArray(json)) {
|
||||
const tasks = normalizeJamones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
|
||||
if (tasks && tasks.length > 0) {
|
||||
this.logger.info({tasks: tasks}, `${this.name} replacing application with ${tasks.length} tasks`);
|
||||
this.callSession.replaceApplication(tasks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* validate that the JSON task description is valid
|
||||
* @param {string} name - verb name
|
||||
* @param {object} data - verb properties
|
||||
*/
|
||||
static validate(name, data) {
|
||||
debug(`validating ${name} with data ${JSON.stringify(data)}`);
|
||||
// validate the instruction is supported
|
||||
@@ -94,6 +126,12 @@ class Task extends Emitter {
|
||||
else if (typeof dSpec === 'string' && dSpec === 'array') {
|
||||
if (!Array.isArray(dVal)) throw new Error(`${name}: property ${dKey} is not an array`);
|
||||
}
|
||||
else if (typeof dSpec === 'string' && dSpec.includes('|')) {
|
||||
const types = dSpec.split('|').map((t) => t.trim());
|
||||
if (!types.includes(typeof dVal)) {
|
||||
throw new Error(`${name}: property ${dKey} has invalid data type, must be one of ${types}`);
|
||||
}
|
||||
}
|
||||
else if (Array.isArray(dSpec) && dSpec[0].startsWith('#')) {
|
||||
const name = dSpec[0].slice(1);
|
||||
for (const item of dVal) {
|
||||
|
||||
Reference in New Issue
Block a user