@cognigy/rest-api-client
Version:
Cognigy REST-Client
303 lines • 12.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseContext = void 0;
/* Node modules */
const nlp = require('nlp_compromise');
/* Custom modules */
const logger_1 = require("./logger");
const objectSizeValidator_1 = require("./objectSizeValidator");
/**
* Cognigy Base Context Object
*/
class BaseContext {
/**
* Creates a new instance of a Context object.
*
* @param {boolean} injectSystemDefault - Flag that describes whether the context
* should be initialized with a default structure.
* @param {string} organisationId - The id of the organisation.
* @memberOf Context
*/
constructor(options = {}, traceId) {
/**
* We need to ignore proxy object trap handlers to keep context object reference immutable.
* This flag is used to ignore unwanted invocation of proxy settes/getters/deleters/etc. when context is reset
* and new fields are added to the context object by cleaning/emptying up of old context fields.
*/
this.ignoreTrapHandler = false;
const { injectSystemDefault, organisationId } = options;
this.traceId = traceId;
// we validate set context with a proxy, not for the default context
this.context = injectSystemDefault ? {} : this.createProxy();
this.organisationId = organisationId;
}
createProxy(initial = {}) {
return new Proxy(initial, {
set: (obj, prop, value) => {
if (!prop) {
logger_1.logger.log("debug", { traceId: this.traceId, disableSensitiveLogging: true }, `Trying to set context with an invalid key '${prop === null || prop === void 0 ? void 0 : prop.toString()}' (context with an invalid key cannot be set).`, {
key: prop,
module: "BaseContext.ts",
function: "set:createProxy"
});
return true;
}
if (this.ignoreTrapHandler) {
obj[prop] = value;
return true;
}
// check if maximum context size is not reached
const merged = Object.assign(obj, { [prop]: value });
const validObjectSize = (0, objectSizeValidator_1.validateObjectSize)(merged, (process.env.MAX_MEMORY_OBJECT_SIZE && parseInt(process.env.MAX_MEMORY_OBJECT_SIZE)) || 64000);
if (validObjectSize.valid === false) {
const errorMessage = `Cannot add value to context for key ${prop.toString()}: exceeded maximum context size.`;
logger_1.logger.log('error', { traceId: `proxy-${this.traceId}` }, errorMessage, {
organisationId: this.organisationId,
projectId: this.projectId
});
obj[prop] = {
error: errorMessage
};
return true;
}
else {
obj[prop] = value;
return true;
}
}
});
}
;
/**
* Imports data into the context. Creates a deep copy of the provided data to
* get rid of references.
*
* @param {any} data - The data which should be imported.
* @param {boolean} replace - Optional flag that defines whether the whole object should
* be overwritten or whether we should copy individual keys from the 'data' object and
* merge them into the current context.
* @memberOf Context
*/
importContext(data, traceId, replace) {
if (replace) {
if (typeof data !== 'object' && data !== null) {
logger_1.logger.log("debug", { traceId }, `[importContext] Trying to replace context with data that is not an object`, {
organisationId: this.organisationId,
projectId: this.projectId,
});
}
// make a deep copy of the provided 'data' to get rid of references.
// we also do not allow 'null' as an input
const deepCopy = JSON.parse(JSON.stringify(data));
if (deepCopy) {
try {
this.ignoreTrapHandler = true;
for (var variableKey in this.context) {
if (this.context.hasOwnProperty(variableKey)) {
delete this.context[variableKey];
}
}
// we validate setting context with a proxy, if not injectSystemDefault
const sourceContext = this.createProxy(deepCopy);
/**
* Object.assign(@target, @source) function copies the values of all of the enumerable own properties from the source object
* to a target object and returns the target object.
* Below code will preserve the `this.context` (as target object) reference after copying props from deep copy data object.
*/
this.context = Object.assign(this.context, sourceContext);
}
finally {
this.ignoreTrapHandler = false;
}
}
}
else {
if (typeof data !== 'object' && data !== null) {
logger_1.logger.log("debug", { traceId }, `[importContext] Trying to set context for data that is not an object`, {
organisationId: this.organisationId,
projectId: this.projectId,
});
}
for (let c in data) {
this.setContext(c, data[c], traceId);
}
}
}
/**
* Retrieves a context variable.
*
* @param {string} key - The context variable to retrieve
* @returns {any} The part of the context you requested using the provided key.
* @memberOf Context
*/
getContext(key) {
if (!key) {
logger_1.logger.log("debug", { traceId: this.traceId, disableSensitiveLogging: true }, `Trying to get context with an invalid key '${key}' (context with an invalid key cannot be accessed).`, {
key,
module: "BaseContext.ts",
function: "getContext"
});
return undefined;
}
let s = [];
if (key.indexOf(".") > -1)
s = key.split(".");
else
s[0] = key;
if (s.length > 1) { // there is a dot, access deep object
let c = this.context;
for (let i of s) {
if (c)
c = (c[i]) ? c[i] : undefined;
}
return (c) ? c : undefined;
}
else { // there is none
return this.context[key] !== null && this.context[key] !== undefined
? this.context[key]
: undefined;
}
}
/**
* Retrieves the full context object. Does not create a copy so we return the reference
* to the internal 'context' object.
*
* @returns {any} The complete Context object.
* @memberOf Context
*/
getFullContext() {
return this.context;
}
/**
* Adds a new key/value pair into the context. Will overwrite the 'key' if its
* already there.
*
* @param {string} key - The key for your new key/value pair within the context.
* @param {any} value - The new value within the context.
* @memberOf Context
*/
setContext(key, value, traceId) {
if (!key) {
logger_1.logger.log("debug", { traceId, disableSensitiveLogging: true }, `Trying to set context with an invalid key '${key}' (context with an invalid key cannot be set).`, {
key,
module: "BaseContext.ts",
function: "setContext"
});
return;
}
this.context[key] = value;
}
/**
* Wrapper around the static function which actually implements
* the logic. We use this wrapper to have the correct 'this' context
* bound.
*/
findContextPath(splits, createPath) {
return BaseContext.findLocationPathStateless(splits, createPath, this.context);
}
/**
* Returns a specific path into the given 'contextObject' and returns the most
* inner object/value you have requested.
*/
static findLocationPathStateless(splits, createPath, locationObject) {
let location = locationObject;
for (let i = 0; i < splits.length; i++) {
if (i < splits.length - 1) {
let index = null;
if (splits[i].match(/.+\[.*\]/g)) {
// there is an array indicator. find it and use it below
let tempsplits = splits[i].split("[");
splits[i] = tempsplits[0];
try {
index = Number(tempsplits[1].replace("]", ""));
}
catch (err) { }
}
if (typeof location[splits[i]] === 'object') {
location = location[splits[i]];
if (index !== null && location[index])
location = location[index];
}
else {
if (createPath) {
location[splits[i]] = {};
location = location[splits[i]];
}
else {
location = null;
break;
}
}
}
;
}
return location;
}
/**
* Removes a certain 'key' from the context object.
*
* @param {string} key - The key that should be removed from the context object.
* @memberOf Context
*/
delete(key) {
if (!key) {
logger_1.logger.log("debug", { traceId: this.traceId, disableSensitiveLogging: true }, `Trying to delete key from the context with an invalid key '${key}' (context with an invalid key cannot be accessed).`, {
key,
module: "BaseContext.ts",
function: "delete"
});
}
delete this.context[key];
}
/**
* Sets last topics in context, identifies correct gender
*
* @param object
* @param age when was object created
*/
setLastTopic(object, age) {
this.context["lastTopic"] = { "text": object.text, "age": age };
if (!object.type || !object.type.gender) {
try {
object.type = { gender: null };
let gender = nlp.text(object.text.split(" ")[0]).sentences[0].people[0].gender();
if (gender)
object.type.gender = gender.toLowerCase();
}
catch (err) { }
}
if (object.type && object.type.gender) {
if (object.type.gender.toLocaleLowerCase() === "male")
this.context["lastMaleTopic"] = { "text": object.text, "age": age };
if (object.type.gender.toLocaleLowerCase() === "female")
this.context["lastFemaleTopic"] = { "text": object.text, "age": age };
}
}
/**
* Retrieves the last topic from the context, based on gender
*
* @param type
* @param curAge
*/
getLastTopic(type, curAge) {
switch (type) {
case "male":
if (this.context["lastMaleTopic"].text !== null) // male topic exists
return this.context["lastMaleTopic"].text;
else
return this.context["lastTopic"].text;
case "female":
if (this.context["lastFemaleTopic"].text !== null) // male topic exists
return this.context["lastFemaleTopic"].text;
else
return null;
default:
return this.context["lastTopic"].text;
}
}
setProjectId(projectId) {
this.projectId = projectId;
}
}
exports.BaseContext = BaseContext;
//# sourceMappingURL=BaseContext.js.map