msbot
Version:
MSBot command line tool for manipulating Microsoft Bot Framework .bot files
283 lines • 12.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Copyright(c) Microsoft Corporation.All rights reserved.
* Licensed under the MIT License.
*/
// tslint:disable:no-console
// tslint:disable:no-object-literal-type-assertion
const botframework_config_1 = require("botframework-config");
const chalk = require("chalk");
const program = require("commander");
const fsx = require("fs-extra");
const process = require("process");
const processUtils_1 = require("./processUtils");
const utils_1 = require("./utils");
program.Command.prototype.unknownOption = (flag) => {
console.error(chalk.default.redBright(`Unknown arguments: ${flag}`));
program.help();
};
program
.name('msbot export services')
.description('export all of the connected services to local folder with .bot.recipe file to support cloning')
.option('-f, --folder <folder>', 'path to folder to place exported resources')
.option('--verbose', 'show verbose export information')
.option('-q, --quiet', 'disable output')
.option('-b, --bot <path>', 'path to bot file. If omitted, local folder will look for a .bot file')
.option('--secret <secret>', 'bot file secret password for encrypting service secrets')
.option('--prefix', 'Append [msbot] prefix to all messages')
.action((cmd, actions) => undefined);
const command = program.parse(process.argv);
const args = {};
Object.assign(args, command);
args.verbose = process.env.VERBOSE === 'verbose';
if (!args.bot) {
botframework_config_1.BotConfiguration.loadBotFromFolder(process.cwd(), args.secret)
.then(processConfiguration)
.catch((reason) => {
console.error(chalk.default.redBright(reason.toString().split('\n')[0]));
showErrorHelp();
});
}
else {
botframework_config_1.BotConfiguration.load(args.bot, args.secret)
.then(processConfiguration)
.catch((reason) => {
console.error(chalk.default.redBright(reason.toString().split('\n')[0]));
showErrorHelp();
});
}
async function processConfiguration(config) {
if (!args.folder) {
throw new Error('missing --folder argument');
}
try {
const recipe = await exportBot(config, args.folder, {
progress: (service, newCommand, index, total) => {
if (!args.quiet) {
const output = `exporting ${chalk.default.bold(service.name)} [${service.type}] (${index}/${total})`;
if (args.verbose) {
console.warn(chalk.default.bold(output));
console.log(chalk.default.italic(`${newCommand}\n`));
}
else {
console.warn(output);
}
}
}
});
}
catch (error) {
const lines = error.message.split('\n');
for (const line of lines) {
// trim to copywrite symbol, help from inner process command line args is inappropriate
if (line.indexOf('©') > 0) {
process.exit(1);
}
console.error(chalk.default.redBright(line));
}
}
}
// export the services from the bot file as resource files and recipe file
async function exportBot(config, folder, exportOptions) {
let options = Object.assign({ download: true }, exportOptions);
let recipe = new botframework_config_1.BotRecipe();
await fsx.ensureDir(folder);
let index = 0;
for (let service of config.services) {
index++;
switch (service.type) {
case botframework_config_1.ServiceTypes.Dispatch:
{
await exportLuisService(service);
let dispatchResource = {
type: service.type,
id: service.id,
name: service.name,
serviceIds: service.serviceIds
};
recipe.resources.push(dispatchResource);
}
break;
case botframework_config_1.ServiceTypes.Luis:
{
await exportLuisService(service);
let resource = {
type: service.type,
id: service.id,
name: service.name
};
recipe.resources.push(resource);
}
break;
case botframework_config_1.ServiceTypes.QnA:
{
let qnaService = service;
if (options.download) {
let command = `qnamaker export kb --kbId ${qnaService.kbId} --environment prod --subscriptionKey ${qnaService.subscriptionKey} --hostname ${qnaService.hostname} --endpointKey ${qnaService.endpointKey}`;
if (options.progress) {
options.progress(service, command, index, config.services.length);
}
let json = '';
await processUtils_1.spawnAsync(command, (stdout) => json += stdout, (stderr) => console.error(stderr));
// make sure it's json
JSON.parse(json);
await fsx.writeFile(folder + `/${qnaService.id}.qna`, json, { encoding: 'utf8' });
}
else {
if (options.progress) {
options.progress(service, '', index, config.services.length);
}
}
let resource = {
type: service.type,
id: service.id,
name: service.name
};
recipe.resources.push(resource);
}
break;
case botframework_config_1.ServiceTypes.Endpoint:
{
if (options.progress) {
options.progress(service, '', index, config.services.length);
}
let endpointResource = {
type: botframework_config_1.ServiceTypes.Endpoint,
id: service.id,
name: service.name,
url: service.endpoint
};
recipe.resources.push(endpointResource);
}
break;
case botframework_config_1.ServiceTypes.BlobStorage:
{
if (options.progress) {
options.progress(service, '', index, config.services.length);
}
let blobResource = {
type: botframework_config_1.ServiceTypes.BlobStorage,
id: service.id,
name: service.name,
container: service.container || ''
};
recipe.resources.push(blobResource);
}
break;
case botframework_config_1.ServiceTypes.CosmosDB:
{
if (options.progress) {
options.progress(service, '', index, config.services.length);
}
let cosmosDBResource = {
type: botframework_config_1.ServiceTypes.CosmosDB,
id: service.id,
name: service.name,
database: service.database,
collection: service.collection,
};
recipe.resources.push(cosmosDBResource);
}
break;
case botframework_config_1.ServiceTypes.File:
{
if (options.progress) {
options.progress(service, '', index, config.services.length);
}
let fileResource = {
type: botframework_config_1.ServiceTypes.File,
id: service.id,
name: service.name,
path: service.path,
};
recipe.resources.push(fileResource);
}
break;
case botframework_config_1.ServiceTypes.Generic:
{
if (options.progress) {
options.progress(service, '', index, config.services.length);
}
console.warn(`WARNING: Generic services cannot be cloned and all configuration data will be passed unchanged and unencrypted `);
let genericService = service;
let genericResource = {
type: botframework_config_1.ServiceTypes.Generic,
id: service.id,
name: service.name,
url: genericService.url,
configuration: genericService.configuration,
};
recipe.resources.push(genericResource);
}
break;
case botframework_config_1.ServiceTypes.Bot:
{
if (options.progress) {
options.progress(service, '', index, config.services.length);
}
let resource = {
type: service.type,
id: service.id,
name: service.name
};
recipe.resources.push(resource);
}
break;
case botframework_config_1.ServiceTypes.AppInsights:
{
if (options.progress) {
options.progress(service, '', index, config.services.length);
}
let resource = {
type: service.type,
id: service.id,
name: service.name
};
recipe.resources.push(resource);
}
break;
default:
if (options.progress) {
options.progress(service, '', index, config.services.length);
}
console.warn(`WARNING: Unknown service type [${service.type}]. This service will not be exported.`);
break;
}
}
await fsx.writeFile(folder + `/bot.recipe`, JSON.stringify(recipe, null, 2), { encoding: 'utf8' });
return recipe;
async function exportLuisService(service) {
let luisService = service;
if (options.download) {
const luisAuthoringRegion = utils_1.regionToLuisAuthoringRegionMap[luisService.region];
let command = `luis export version --region ${luisAuthoringRegion} --appId ${luisService.appId} --authoringKey ${luisService.authoringKey} --versionId "${luisService.version}"`;
if (options.progress) {
options.progress(service, command, index, config.services.length);
}
let json = '';
await processUtils_1.spawnAsync(command, (stdout) => json += stdout, (stderr) => console.error(stderr));
// make sure it's json
try {
JSON.parse(json);
}
catch (err) {
throw new Error(`${err.message || err}\n${json}`);
}
await fsx.writeFile(folder + `/${luisService.id}.luis`, json, { encoding: 'utf8' });
}
else {
if (options.progress) {
options.progress(service, '', index, config.services.length);
}
}
}
}
function showErrorHelp() {
program.outputHelp((str) => {
console.error(str);
return '';
});
process.exit(1);
}
//# sourceMappingURL=msbot-export-services.js.map