VoxaAI/voxa-cli

View on GitHub
src/commands/init.ts

Summary

Maintainability
C
1 day
Test Coverage
/*
 * 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-submodule-imports no-console */

import colors from "colors";
import fs from "fs-extra";
import inquirer from "inquirer";
import _ from "lodash";
import pad from "pad";
import path from "path";
import { Observable } from "rxjs";
import { DEFAULT_INTERACTION_OPTIONS } from "../InteractionBuilder";

export const name = "init";
export const alias = "";
export const description = "create a new interaction.json";

const ALLOWED_ATTRIBUTES = [
  "platforms",
  "spreadsheets",
  "dialogflowSpreadsheets",
  "alexaSpreadsheets",
  "speechPath",
  "content",
  "contentPath",
  "viewsPath"
];

export async function action() {
  const interactionFile = path.join(process.cwd(), "interaction.json");
  let hasAnInteractionFile = false;
  try {
    // a path we KNOW is totally bogus and not a module
    hasAnInteractionFile = require(interactionFile);
  } catch (e) {
    if (e.code === "MODULE_NOT_FOUND") {
      hasAnInteractionFile = false;
    }
  }

  const observe = Observable.create((obs: any) => {
    const override = (answers: any) => {
      return answers.interactionFile;
    };
    const onlyCommaAnswer = (input: string) => {
      return _.chain(input)
        .split(",")
        .map(_.trim)
        .compact()
        .value();
    };
    const noEmptyAnswer = (input: string) => !_.isEmpty(input);
    const mustBeSpreadsheetURLOrLength30 = async (input: string) => {
      let result: any = _.chain(input)
        .split(",")
        .map(_.trim)
        .compact()
        .map((i: string) => {
          if (i.includes("docs.google.com/spreadsheets")) {
            const matched = i.match(/docs\.google\.com\/spreadsheets\/d\/(.*)\/.*/);
            return matched && _.isString(matched[1]) && matched[1].length > 30;
          }

          if (i.includes(".sharepoint.com") && i.length > 30) {
            return true;
          }

          const customPath = i.indexOf("/") === 0 ? i : path.join(process.cwd(), i);

          return fs.pathExists(customPath);
        })
        .value();

      result = await Promise.all(result);
      result = result.every((r: boolean) => r);

      return noEmptyAnswer(input) && result
        ? true
        : "Insert a proper google spreadsheet url, Office 365 sharepoint url, local file or local folder path. e.g. https://docs.google.com/spreadsheets/d/XXXXXXX/edit#gid=0, https://XXXXXX.sharepoint.com/:x:/g/personal/YYYYYY/ZZZZZZZ, /Users/local/file.xlsx or /Users/local/sheets/";
    };

    const questions = [
      {
        type: "confirm",
        name: "interactionFile",
        message: `Do you want to override ${interactionFile}?`,
        when: () => hasAnInteractionFile,
        default: true
      },
      {
        type: "checkbox",
        name: "platforms",
        message: "Choose platforms",
        choices: ["alexa", "dialogflow"],
        when: override
      },
      {
        type: "input",
        name: "spreadsheets",
        message: "Specify all spreadsheets separated by comma (*)",
        validate: mustBeSpreadsheetURLOrLength30,
        filter: onlyCommaAnswer,
        when: override
      },
      {
        type: "input",
        name: "speechPath",
        message: "Specify folder path to save interaction model and manifest",
        default: DEFAULT_INTERACTION_OPTIONS.speechPath,
        when: override
      },
      {
        type: "input",
        name: "contentPath",
        message: "Specify folder path to save all downloable content",
        when: override,
        default: DEFAULT_INTERACTION_OPTIONS.contentPath
      },
      {
        type: "input",
        name: "viewsPath",
        message: "Specify folder path to save views file",
        default: DEFAULT_INTERACTION_OPTIONS.viewsPath,
        when: override
      },
      {
        type: "confirm",
        name: "isCorrect",
        message: `is Correct?`,
        when: (answers: any) => {
          if (override(answers)) {
            printVariables(_.cloneDeep(answers));
          }
          return override(answers);
        },
        default: true
      }
    ];
    questions.map(q => obs.next(q));

    obs.complete();
  });

  function printVariables(answers: any) {
    return _.chain(answers)
      .toPairs()
      .filter(item => !!ALLOWED_ATTRIBUTES.find(i => i === item[0]))
      .map(item => {
        const key = item[0];
        const value = item[1];
        console.log(pad(colors.blue(key), 20), colors.grey(value));
        return item;
      })
      .compact()
      .fromPairs()
      .value();
  }
  async function executePrompt(): Promise<any> {
    return inquirer.prompt(observe).then((answers: any) => {
      if (answers.interactionFile && !answers.isCorrect) {
        return executePrompt();
      }

      if (hasAnInteractionFile && !answers.interactionFile) {
        answers = hasAnInteractionFile;
      }
      if (!answers.interactionFile && !answers.isCorrect) {
        printVariables(answers);
      }
      answers = _.pick(answers, ALLOWED_ATTRIBUTES);
      return fs.outputFile(
        path.join(process.cwd(), "interaction.json"),
        JSON.stringify(answers, null, 2),
        { flag: "w" }
      );
    });
  }

  await executePrompt();
}