UNPKG

botbuilder-dialogs

Version:

A dialog stack based conversation manager for Microsoft BotBuilder.

242 lines 9.5 kB
"use strict"; /** * @module botbuilder-dialogs */ /** * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SettingsMemoryScope = void 0; const dialogTurnStateConstants_1 = require("../../dialogTurnStateConstants"); const memoryScope_1 = require("./memoryScope"); const scopePath_1 = require("../scopePath"); /** * The setting node. */ class Node { /** * Initializes a new instance of `Node`. * * @param {string} value Value of the node. If the node is not leaf, value represents the current path. */ constructor(value) { this.value = value; /** * The child nodes of the node. */ this.children = []; } /** * Indicates if the node is leaf node. * * @returns {boolean} If the node is leaf node or not. */ isLeaf() { return this.children.length === 0; } } /** * SettingsMemoryScope maps "settings" -> dc.context.turnState['settings'] */ class SettingsMemoryScope extends memoryScope_1.MemoryScope { /** * Initializes a new instance of the [SettingsMemoryScope](xref:botbuilder-dialogs.SettingsMemoryScope) class. * * @param initialSettings initial set of settings to supply */ constructor(initialSettings) { super(scopePath_1.ScopePath.settings, false); this.initialSettings = initialSettings; } /** * Gets the backing memory for this scope. * * @param {DialogContext} dc The [DialogContext](xref:botbuilder-dialogs.DialogContext) object for this turn. * @returns {Record<string, ?>} The memory for the scope. */ getMemory(dc) { var _a, _b; if (dc.context.turnState.has(scopePath_1.ScopePath.settings)) { return (_a = dc.context.turnState.get(scopePath_1.ScopePath.settings)) !== null && _a !== void 0 ? _a : {}; } const configuration = (_b = dc.context.turnState.get(dialogTurnStateConstants_1.DialogTurnStateConstants.configuration)) !== null && _b !== void 0 ? _b : {}; Object.entries(process.env).reduce((result, [key, value]) => { result[`${key}`] = value; return result; }, configuration); const settings = SettingsMemoryScope.loadSettings(configuration); dc.context.turnState.set(scopePath_1.ScopePath.settings, settings); return settings; } /** * @param dc Current dialog context. */ load(dc) { const _super = Object.create(null, { load: { get: () => super.load } }); return __awaiter(this, void 0, void 0, function* () { if (this.initialSettings) { // filter initialSettings const filteredSettings = SettingsMemoryScope.filterSettings(this.initialSettings); dc.context.turnState.set(scopePath_1.ScopePath.settings, filteredSettings); } yield _super.load.call(this, dc); }); } /** * Build a dictionary view of configuration providers. * * @param {Record<string, string>} configuration The configuration that we are running with. * @returns {Record<string, ?>} Projected dictionary for settings. */ static loadSettings(configuration) { let settings = {}; if (configuration) { // load configuration into settings const root = this.convertFlattenSettingToNode(Object.entries(configuration)); settings = root.children.reduce((acc, child) => (Object.assign(Object.assign({}, acc), { [child.value]: this.convertNodeToObject(child) })), settings); } // filter env configuration settings return this.filterSettings(settings); } /** * Generate a node tree with the flatten settings. * For example: * { * "array":["item1", "item2"], * "object":{"array":["item1"], "2":"numberkey"} * } * * Would generate a flatten settings like: * array:0 item1 * array:1 item2 * object:array:0 item1 * object:2 numberkey * * After Converting it from flatten settings into node tree, would get: * * null * | | * array object * | | | | * 0 1 array 2 * | | | | * item1 item2 0 numberkey * | * item1 * The result is a Tree. * * @param {Array<[string, string]>} kvs Configurations with key value pairs. * @returns {Node} The root node of the tree. */ static convertFlattenSettingToNode(kvs) { const root = new Node(); kvs.forEach(([key, value]) => { const keyChain = key.split(':'); let currentNode = root; keyChain.forEach((item) => { const matchItem = currentNode.children.find((u) => (u === null || u === void 0 ? void 0 : u.value) === item); if (!matchItem) { // Remove all the leaf children currentNode.children = currentNode.children.filter((u) => u.children.length !== 0); // Append new child into current node const node = new Node(item); currentNode.children.push(node); currentNode = node; } else { currentNode = matchItem; } }); currentNode.children.push(new Node(value)); }); return root; } static convertNodeToObject(node) { if (!node.children.length) { return {}; } // If the child is leaf node, return its value directly. if (node.children.length === 1 && node.children[0].isLeaf()) { return node.children[0].value; } // check if all the children are number format. let pureNumberIndex = true; const indexArray = []; let indexMax = -1; for (let i = 0; i < node.children.length; i++) { const child = node.children[Number(i)]; if (/^-?\d+$/.test(child.value)) { const num = parseInt(child.value, 10); if (!isNaN(num) && num >= 0) { indexArray.push(num); if (num > indexMax) { indexMax = num; } continue; } } pureNumberIndex = false; break; } if (pureNumberIndex) { // all children are int numbers, treat it as array. const listResult = new Array(indexMax + 1); node.children.forEach((child, index) => { listResult[indexArray[Number(index)]] = this.convertNodeToObject(child); }); return listResult; } // Convert all child into dictionary return node.children.reduce((result, child) => { result[child.value] = this.convertNodeToObject(child); return result; }, {}); } static filterSettings(settings) { const result = Object.assign({}, settings); this.blockingList.forEach((path) => this.deletePropertyPath(result, path)); return result; } static deletePropertyPath(obj, path) { if (!obj || !(path === null || path === void 0 ? void 0 : path.length)) { return; } const pathArray = path.split(':'); for (let i = 0; i < pathArray.length - 1; i++) { const realKey = Object.keys(obj).find((key) => key.toLowerCase() === pathArray[i].toLowerCase()); obj = obj[realKey]; if (typeof obj === 'undefined') { return; } } const lastPath = pathArray.pop().toLowerCase(); const lastKey = Object.keys(obj).find((key) => key.toLowerCase() === lastPath); delete obj[lastKey]; } } exports.SettingsMemoryScope = SettingsMemoryScope; SettingsMemoryScope.blockingList = [ 'MicrosoftAppPassword', 'cosmosDb:authKey', 'blobStorage:connectionString', 'BlobsStorage:connectionString', 'CosmosDbPartitionedStorage:authKey', 'applicationInsights:connectionString', 'applicationInsights:InstrumentationKey', 'runtimeSettings:telemetry:options:connectionString', 'runtimeSettings:telemetry:options:instrumentationKey', 'runtimeSettings:features:blobTranscript:connectionString', ]; //# sourceMappingURL=settingsMemoryScope.js.map