UNPKG

@cognigy/rest-api-client

Version:

Cognigy REST-Client

303 lines 12.3 kB
"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