UNPKG

voxa-cli

Version:
246 lines (214 loc) 8.07 kB
/* * Copyright (c) 2018 Rain Agency <contact@rain.agency> * Author: Rain Agency <contact@rain.agency> * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* tslint:disable:no-console no-submodule-imports */ import fsExtra from "fs-extra"; import _ from "lodash"; import path from "path"; import { AlexaSchema } from "./AlexaSchema"; import { transform } from "./connectors/Spreadsheet"; import { DialogflowSchema } from "./DialogflowSchema"; import { downloadDirs } from "./Drive"; import { IFileContent, IInvocation, Schema } from "./Schema"; import { IPlatformSheets, IVoxaSheet } from "./VoxaSheet"; const fs = Promise.promisifyAll(fsExtra); export type ISupportedPlatforms = "alexa" | "dialogflow"; export interface IInteractionOptions { rootPath?: string; speechPath?: string; platforms?: ISupportedPlatforms[] | ISupportedPlatforms; contentPath?: string; viewsPath?: string; synonymPath?: string; spreadsheets: string | string[]; alexaSpreadsheets: string | string[]; dialogflowSpreadsheets: string | string[]; assets?: string[]; assetsPath?: string; } export interface IDefinedInteractionOptions { rootPath: string; speechPath: string; platforms: ISupportedPlatforms[]; contentPath: string; viewsPath: string; synonymPath: string; spreadsheets: string[]; alexaSpreadsheets: string | string[]; dialogflowSpreadsheets: string | string[]; assets: string[]; assetsPath: string; } export const DEFAULT_INTERACTION_OPTIONS = { speechPath: "speech-assets", platforms: ["alexa"] as ISupportedPlatforms[], contentPath: "content", viewsPath: "/", synonymPath: "synonyms", assets: [], assetsPath: "assets" }; function defaultOptions(interactionOptions: IInteractionOptions): IDefinedInteractionOptions { const rootPath: string = interactionOptions.rootPath || ""; const speechPath: string = interactionOptions.speechPath || DEFAULT_INTERACTION_OPTIONS.speechPath; const synonymPath: string = interactionOptions.synonymPath || DEFAULT_INTERACTION_OPTIONS.synonymPath; const viewsPath: string = interactionOptions.viewsPath || DEFAULT_INTERACTION_OPTIONS.viewsPath; const contentPath: string = interactionOptions.contentPath || DEFAULT_INTERACTION_OPTIONS.contentPath; const assetsPath: string = interactionOptions.assetsPath || DEFAULT_INTERACTION_OPTIONS.assetsPath; const assets: string[] = interactionOptions.assets || DEFAULT_INTERACTION_OPTIONS.assets; const spreadsheets: string[] = arrayify(interactionOptions.spreadsheets) as string[]; const alexaSpreadsheets: string[] = arrayify(interactionOptions.alexaSpreadsheets) as string[]; const dialogflowSpreadsheets: string[] = arrayify( interactionOptions.dialogflowSpreadsheets ) as string[]; let platforms: ISupportedPlatforms[] = arrayify( interactionOptions.platforms ) as ISupportedPlatforms[]; const spreadsheetMapping = { alexa: alexaSpreadsheets, dialogflow: dialogflowSpreadsheets }; // If the user didn't declare anything in the platorm key, // then we should inspect the spreadshseet in alexaSpreadsheets and dialogflowSpreadsheets // to find out what platform the user wants to use. platforms = _.chain(spreadsheetMapping) .toPairs() .reduce( (acc: string[], next: any[]) => { const key = next[0]; const value = next[1]; if (value) { acc.push(key); } return acc; }, [] as ISupportedPlatforms[] ) .concat(platforms) .filter() .uniq() .value() as ISupportedPlatforms[]; if (_.isEmpty(spreadsheets) && _.isEmpty(platforms)) { throw Error("Spreadsheet were not specified in the right format"); } platforms = _.isEmpty(platforms) ? DEFAULT_INTERACTION_OPTIONS.platforms : platforms; return { rootPath, spreadsheets, alexaSpreadsheets, dialogflowSpreadsheets, speechPath, synonymPath, viewsPath, assetsPath, contentPath, platforms, assets }; } function arrayify(value: string | any[] | undefined): any[] | undefined { return _.isString(value) ? [value] : value; } async function getSheetsByPlatform( interactionOptions: IInteractionOptions, authKeys: any ): Promise<IPlatformSheets> { const sheets = await transform(interactionOptions, authKeys); const AVAILABLE_PLATFORM_KEYS = ["alexaSpreadsheets", "dialogflowSpreadsheets"]; const transformByPlatfromPromises = AVAILABLE_PLATFORM_KEYS.map(spreadsheetKey => transform(interactionOptions, authKeys, spreadsheetKey) ); const sheetsByPlatformPromise: IVoxaSheet[][] = await Promise.all(transformByPlatfromPromises); const sheetsByPlatform: IPlatformSheets = AVAILABLE_PLATFORM_KEYS.reduce((acc, next, index) => { const sheetByPlatform = _.chain(sheets) .cloneDeep() .concat(sheetsByPlatformPromise[index]) .uniq() .value(); _.set(acc, next, sheetByPlatform); return acc; }, {}) as IPlatformSheets; // concat all the sheets from the `spreadsheets` key with platform specific sheets return sheetsByPlatform; } export const buildInteraction = async (interactionOptions: IInteractionOptions, authKeys: any) => { const definedInteractionOptions = defaultOptions(interactionOptions); console.time("all"); console.time("timeframe"); const { alexaSpreadsheets, dialogflowSpreadsheets } = await getSheetsByPlatform( definedInteractionOptions, authKeys ); console.timeEnd("timeframe"); const platforms = definedInteractionOptions.platforms; const schemas = []; if (platforms.includes("alexa")) { const schema = new AlexaSchema(alexaSpreadsheets, definedInteractionOptions); schemas.push(schema); } if (platforms.includes("dialogflow")) { const schema = new DialogflowSchema(dialogflowSpreadsheets, definedInteractionOptions); schemas.push(schema); } await Promise.all( schemas.map(schema => fs.remove( path.join( definedInteractionOptions.rootPath, definedInteractionOptions.speechPath, schema.NAMESPACE ) ) ) ); const fileContentsProcess = _.chain(schemas) .reduce( (acc, schema, index) => { if (index === 0) { // We only want to execute this file once schema.buildDownloads(); schema.buildViews(); schema.buildViewsMapping(); schema.buildSynonyms(); } schema.invocations.map((invoc: IInvocation) => { schema.build(invoc.locale, invoc.environment); }); acc = acc.concat(schema.fileContent); return acc; }, [] as IFileContent[] ) .uniqBy("path") .map((file: IFileContent) => fs.outputFile(file.path, JSON.stringify(file.content, null, 2), { flag: "w" }) ).value(); await Promise.all(fileContentsProcess); await downloadDirs( definedInteractionOptions.assets, path.join(definedInteractionOptions.rootPath, definedInteractionOptions.assetsPath), authKeys ); console.timeEnd("all"); };