dialogflow-fulfillment
Version:
Dialogflow Fulfillment Library for Node.js
269 lines (261 loc) • 9.34 kB
JavaScript
/**
* Copyright 2017 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const debug = require('debug')('dialogflow:debug');
const _ = require('lodash');
// Configure logging for hosting platforms that only support console.log and console.error
debug.log = console.log.bind(console);
const DELETED_LIFESPAN_COUNT = 0; // Lifespan of a deleted context
/**
* This is the class that handles Dialogflow's contexts for the WebhookClient class
*/
class Context {
/**
* Constructor for Context object
* To be used in by Dialogflow's webhook client class
* context objects take are formatted as follows:
* { "context-name": {
* "lifespan": 5,
* "parameters": {
* "param": "value"
* }
* }
* }
*
* @example
* const context = new Context(inputContexts);
* context.get('name of context')
* context.set('another context name', 5, {param: 'value'})
* context.delete('name of context') // set context lifespan to 0
*
* @param {Object} inputContexts input contexts of a v1 or v2 webhook request
* @param {string} session for a v2 webhook request & response
*/
constructor(inputContexts, session) {
/**
* Dialogflow contexts included in the request or empty object if no value
* https://dialogflow.com/docs/contexts
* @type {object}
*/
this.contexts = {};
this.session = session;
if (inputContexts && session) {
this.inputContexts = this._processV2InputContexts(inputContexts);
this.contexts = this._processV2InputContexts(inputContexts);
} else if (inputContexts) {
this.contexts = this._processV1InputContexts(inputContexts);
this.inputContexts = this._processV1InputContexts(inputContexts);
}
}
// ---------------------------------------------------------------------------
// Public CRUD methods
// ---------------------------------------------------------------------------
/**
* Set a new Dialogflow outgoing context: https://dialogflow.com/docs/contexts
*
* @example
* const { WebhookClient } = require('dialogflow-webhook');
* const agent = new WebhookClient({request: request, response: response});
* agent.context.set('sample context name');
* const context = {'name': 'weather', 'lifespan': 2, 'parameters': {'city': 'Rome'}};
*
* @param {string|Object} name of context or an object representing a context
* @param {number} [lifespan=5] lifespan of context, number with a value of 0 or greater
* @param {Object} [params] parameters of context (can be arbitrary key-value pairs)
*/
set(name, lifespan, params) {
if (!name || (typeof name !== 'string' && typeof name['name'] !== 'string')) {
throw new Error('Required "name" argument must be a string or an object with a string attribute "name"');
}
if (typeof name !== 'string') {
params = name['parameters'];
lifespan = name['lifespan'];
name = name['name'];
}
if (!this.contexts[name]) {
this.contexts[name] = {name: name};
}
if (lifespan !== undefined && lifespan !== null) {
this.contexts[name].lifespan = lifespan;
}
if (params !== undefined) {
this.contexts[name].parameters = params;
}
}
/**
* Get an context from the Dialogflow webhook request: https://dialogflow.com/docs/contexts
*
* @example
* const { WebhookClient } = require('dialogflow-webhook');
* const agent = new WebhookClient({request: request, response: response});
* let context = agent.context.get('sample context name');
*
* @param {string} name of an context present in the Dialogflow webhook request
* @return {Object|null} context object with lifespan and parameters (if defined) or null
*/
get(name) {
return this.contexts[name];
}
/**
* Delete an context a Dialogflow session (set the lifespan to 0)
*
* @example
* const { WebhookClient } = require('dialogflow-webhook');
* const agent = new WebhookClient({request: request, response: response});
* agent.context.delete('no-longer-relevant-context-name');
*
* @param {string} name of context to be deleted
*
* @public
*/
delete(name) {
this.set(name, DELETED_LIFESPAN_COUNT);
}
/**
* Returns contexts as an iterator.
*
* @example
* const { WebhookClient } = require('dialogflow-webhook');
* const agent = new WebhookClient({request: request, response: response});
* for (const context of agent.context) {
* // do something with the contexts
* }
*
* @return {iterator} iterator of all context objects
* @public
*/
[Symbol.iterator]() {
let contextArray = [];
for (const contextName of Object.keys(this.contexts)) {
contextArray.push(this.contexts[contextName]);
}
return contextArray[Symbol.iterator]();
// suppose to be Array.prototype.values(), but can't use because of bug:
// https://bugs.chromium.org/p/chromium/issues/detail?id=615873
}
// ---------------------------------------------------------------------------
// Private methods
// ---------------------------------------------------------------------------
/**
* Remove an context from Dialogflow's outgoing context webhook response
* used to maintain compatibility with legacy clearContext methods
*
* @example
* const { WebhookClient } = require('dialogflow-webhook');
* const agent = new WebhookClient({request: request, response: response});
* agent.context._removeOutgoingContext('no-longer-sent-context-name');
*
* @param {string} name of context to be removed from outgoing contexts
*
* @private
*/
_removeOutgoingContext(name) {
delete this.contexts[name];
}
// ---------------------------------------------------------------------------
// Private v2 <--> v1 translation methods
// ---------------------------------------------------------------------------
/**
* Translate context object from v1 webhook request format to class format
*
* @param {Array} v1InputContexts to be used by the Contexts class
*
* @return {Object} internal representation of contexts
* @private
*/
_processV1InputContexts(v1InputContexts) {
let contexts = {};
for (let index = 0; index<v1InputContexts.length; index++) {
const context = v1InputContexts[index];
contexts[context['name']] = {
name: context['name'],
parameters: context['parameters'],
lifespan: context['lifespan'],
};
}
return contexts;
}
/**
* Translate context object from v2 webhook request format to class format
*
* @param {Array} v2InputContexts to be used by the Contexts class
*
* @return {Object} internal representation of contexts
* @private
*/
_processV2InputContexts(v2InputContexts) {
let contexts = {};
for (let index = 0; index<v2InputContexts.length; index++) {
let context = v2InputContexts[index];
const name = context['name'].split('/')[6];
contexts[name] = {
name: name,
lifespan: context['lifespanCount'],
parameters: context['parameters']};
}
return contexts;
}
/**
* Get array of context objects formatted for v1 webhook response
*
* @return {Object[]} array of v1 context objects for webhook response
*/
getV1OutputContextsArray() {
let v1OutputContexts = [];
for (const ctx of this) {
// Skip context if it is the same as the input context
if (this.inputContexts &&
this.inputContexts[ctx.name] &&
_.isEqual(ctx, this.inputContexts[ctx.name])) {
continue;
}
let v1Context = {name: ctx.name};
if (ctx.lifespan !== undefined) {
v1Context['lifespan'] = ctx.lifespan;
}
if (ctx.parameters) {
v1Context['parameters'] = ctx.parameters;
}
v1OutputContexts.push(v1Context);
}
return v1OutputContexts;
}
/**
* Get array of context objects formatted for v2 webhook response
*
* @return {Object[]} array of v2 context objects for webhook response
*/
getV2OutputContextsArray() {
let v2OutputContexts = [];
for (const ctx of this) {
// Skip context if it is the same as the input context
if (this.inputContexts &&
this.inputContexts[ctx.name] &&
_.isEqual(ctx, this.inputContexts[ctx.name])) {
continue;
}
let v2Context = {name: `${this.session}/contexts/${ctx.name}`};
if (ctx.lifespan !== undefined) {
v2Context['lifespanCount'] = ctx.lifespan;
}
if (ctx.parameters) {
v2Context['parameters'] = ctx.parameters;
}
v2OutputContexts.push(v2Context);
}
return v2OutputContexts;
}
}
module.exports = Context;