conv-context
Version:
Conversation context manager
194 lines (157 loc) • 4.96 kB
JavaScript
const _ = require('lodash');
const timestring = require('timestring');
const ONE_DAY = 24 * 60 * 60 * 1000;
class Context {
constructor (contextData = {}, thread) {
this.contextKey = contextData.contextKey || '__convContext';
this.store = contextData.store || {};
this.output = contextData.output || {};
this.resolver = contextData.resolve;
this.thread = thread;
this.ttlBuffer = this.getTtlBuffer(contextData.ttlBuffer);
}
static init (contextData, thread) {
return new Context(contextData, thread);
}
get selectStyle () {
return _.get(this.output, 'selectStyle');
}
get (key, defaultValue) {
if (key === undefined || key === null || key === '') {
return this.thread.get(this.contextKey, defaultValue);
}
if (_.isString(key)) {
return this.thread.get(`${this.contextKey}.${key}`, defaultValue);
}
throw new Error('Invalid key');
}
set (key, value) {
if (key === undefined || key === null || key === '') {
_.assign(this.store, value); // do not override an old value
return this;
}
if (_.isString(key)) {
_.set(this.store, key, value);
return this;
}
if (_.isObject(key)) {
_.forEach(key, (value, prop) => {
this.set(prop, value);
});
return this;
}
throw new Error('Invalid key');
}
unset (key) {
if (key === undefined || key === null || key === '') {
this.store = undefined;
return this.thread.unset(this.contextKey);
}
if (_.isString(key)) {
_.set(this.store, key, undefined);
return this.thread.unset(`${this.contextKey}.${key}`);
}
throw new Error('Invalid key');
}
setStepContext (data, stepId=null) {
if (!stepId) {
stepId = this.thread.currentStepId;
}
this.set(`__steps.${stepId}`, data);
return this;
}
getStepContext (stepId) {
if (!stepId) throw new Error('Invalid step ID');
return this.get(`__steps.${stepId}`)
}
deleteStepContext (stepId) {
if (!stepId) throw new Error('Invalid step ID');
this.unset(`__steps.${stepId}`);
}
filterStepContext (query={}) {
return _.filter(this.get(`__steps`), query);
}
findStepContext (query={}) {
return _.find(this.get(`__steps`), query);
}
findStepId (query={}) {
return _.findKey(this.get(`__steps`), query);
}
async defaultResolver (output) {
// helper resolve functions for each style
const fromInherited = value => {
const from = _.get(value, 'from', 'last');
return this.get(from);
}
const fromStepId = value => {
const stepId = value.stepId
|| value.targetStepId
|| _.get(_.keys(value), '[0]');
if (!stepId || !_.isString(stepId)) throw new Error('Cannot get selected step ID');
return this.getStepContext(stepId);
}
const fromMergeFieldName = value => {
const mfName = value.name || value.dataOutName
|| value.mergeFieldName || _.get(_.keys(value), '[0]');
if (!mfName || !_.isString(mfName)) throw new Error('No merge field name property found');
return this.thread.mergeFields[mfName].get();
}
switch (this.selectStyle) {
case 'inherited':
return fromInherited(output.value);
case 'step':
return fromStepId(output.value);
case 'dataOut':
case 'mergeField':
return fromMergeFieldName(output.value);
case 'pick':
return output.value.pickValue;
default:
return output.value;
}
}
async resolve(resolver, data) {
if (_.isFunction(resolver)) {
return resolver.call(this.thread, this.output, this.store);
}
if (_.isUndefined(data) && !_.isUndefined(resolver)) {
data = resolver;
}
if (_.isFunction(this.resolver)) {
return this.resolver(this.thread, data);
}
return this.defaultResolver(this.output);
}
setSession () {
this.thread.set(this.contextKey, this.store);
}
getTtlBuffer (ctxTtlBuffer) {
if (_.isNumber(ctxTtlBuffer)) return ctxTtlBuffer;
const bufferInput = _.get(ctxTtlBuffer, 'input');
if (!_.isString(bufferInput)) {
return ONE_DAY;
}
try {
return timestring(bufferInput, 'ms');
} catch {
return ONE_DAY;
}
}
async setShared (duration) {
const remainingSession = new Date(this.thread.session.expirationDate) - Date.now();
const defaultTtl = remainingSession + this.ttlBuffer;
const ttl = duration || defaultTtl;
await this.thread.setShared(this.contextKey, this.store, ttl);
}
async save (shared=true, ttl=null) {
this.setSession();
if (shared) {
if (_.isNumber(shared) && ttl === null) ttl = shared;
await this.setShared(ttl);
}
}
}
module.exports = Context;
module.exports.default = Context;
module.exports.Context = Context;
Object.defineProperty(module.exports, "__esModule", { value: true });