~tcarrio/dialogflow-as-code

7a12d824d857b3e571124c085c525a5af1504276 — Tom Carrio 3 years ago 38fff65
Fixes for code smells, bugs, and other Sonar flags
M .gitignore => .gitignore +2 -1
@@ 9,4 9,5 @@ input-example/
!input/.gitkeep
test-results.xml
test/resources/testOutput
test/resources/test-dac/dist/
\ No newline at end of file
test/resources/test-dac/dist/
.scannerwork

M src/builder/entity-type.ts => src/builder/entity-type.ts +2 -2
@@ 34,8 34,8 @@ export class EntityTypeBuilder implements IBuilder<EntityType> {
  }
  public entities(entities: (SynonymsBuilder | Entity)[]): EntityTypeBuilder {
    this._entityType.entities.push(
      ...entities.map((et) =>
        et instanceof SynonymsBuilder ? et.build() : et,
      ...entities.map((entity) =>
        entity instanceof SynonymsBuilder ? entity.build() : entity,
      ),
    );
    this._valid = this._valid | 8;

M src/builder/intent.ts => src/builder/intent.ts +6 -6
@@ 95,11 95,11 @@ export class IntentBuilder {
    );
    return this;
  }
  public followUpOf(intent: string | Intent) {
    if (typeof intent === "string") {
      this._intent.parentFollowupIntentName = intent;
  public followUpOf(parentIntent: string | Intent) {
    if (typeof parentIntent === "string") {
      this._intent.parentFollowupIntentName = parentIntent;
    } else {
      this._intent.parentFollowupIntentName = intent.displayName;
      this._intent.parentFollowupIntentName = parentIntent.displayName;
    }
    return this;
  }


@@ 141,8 141,8 @@ export class IntentBuilder {
  public ms(ms: (Message | MessageBuilder)[]) {
    return this.messages(ms);
  }
  public fo(intent: string | Intent) {
    return this.followUpOf(intent);
  public fo(parentIntent: string | Intent) {
    return this.followUpOf(parentIntent);
  }
}


M src/builder/message.ts => src/builder/message.ts +2 -2
@@ 76,12 76,12 @@ export class MessageBuilder implements IBuilder<Message> {
        `The '${this._type}' message type was not built successfully`,
      );
    }
    const msg = {
    
    return {
      message: this._type,
      platform: this.platform,
      [this._type]: this._msg,
    };
    return msg;
  }

  public pf(platform: Platform): MessageBuilder {

M src/builder/training-phrases.ts => src/builder/training-phrases.ts +15 -15
@@ 60,31 60,31 @@ export class TrainingPhraseBuilder implements IBuilder<TrainingPhrase> {

const punctuation: Set<string> = new Set<string>(["?", ",", "."]);

function fixSpacing(phrases: TrainingPhraseInput[]): TrainingPhraseInput[] {
  if (phrases.length <= 1) {
    return phrases;
function fixSpacing(inputPhrases: TrainingPhraseInput[]): TrainingPhraseInput[] {
  if (inputPhrases.length <= 1) {
    return inputPhrases;
  }

  const space = " ";
  const newPhrases: TrainingPhraseInput[] = [];
  for (let index = 0; index < phrases.length - 1; index++) {
    const phrase = phrases[index];
    const next = phrases[index + 1];
  for (let index = 0; index < inputPhrases.length - 1; index++) {
    const existingPhrase = inputPhrases[index];
    const next = inputPhrases[index + 1];

    if (typeof phrase === "string" && typeof next === "string") {
      newPhrases.push(phrase.trimRight() + space + next.trimLeft());
    if (typeof existingPhrase === "string" && typeof next === "string") {
      newPhrases.push(existingPhrase.trimRight() + space + next.trimLeft());
      index++;
    } else if (typeof phrase === "string" && typeof next !== "string") {
      newPhrases.push(phrase.trimRight() + space);
    } else if (typeof phrase !== "string" && typeof next === "string") {
      phrases[index + 1] = space + next.trimLeft();
      newPhrases.push(phrase);
    } else if (typeof existingPhrase === "string" && typeof next !== "string") {
      newPhrases.push(existingPhrase.trimRight() + space);
    } else if (typeof existingPhrase !== "string" && typeof next === "string") {
      inputPhrases[index + 1] = space + next.trimLeft();
      newPhrases.push(existingPhrase);
    } else {
      newPhrases.push(phrase, space);
      newPhrases.push(existingPhrase, space);
    }
  }

  let phrase = phrases[phrases.length - 1];
  let phrase = inputPhrases[inputPhrases.length - 1];
  if (typeof phrase === "string") {
    const trimmed = phrase.trim();
    if (punctuation.has(trimmed)) {

M src/converter/cli.ts => src/converter/cli.ts +5 -5
@@ 33,10 33,10 @@ function buildProgram() {

const CLI = buildProgram();

let outputDirectory = "./output";
if (CLI.output) outputDirectory = CLI.output;
let inDir = "./input";
if (CLI.input) inDir = CLI.input;

let inputDirectory = "./input";
if (CLI.input) inputDirectory = CLI.input;
let outDir = "./output";
if (CLI.output) outDir = CLI.output;

main(inputDirectory, outputDirectory);
main(inDir, outDir);

M src/converter/common/formatter.ts => src/converter/common/formatter.ts +4 -4
@@ 9,12 9,12 @@ let _indent = 2;
export function indent(input: string, tabs: number): string {
  if (!input) return input;

  const indent = new Array(tabs * _indent + 1).join(" ");
  return indent + input.replace(/\n/g, "\n" + indent);
  const newIndent = new Array(tabs * _indent + 1).join(" ");
  return newIndent + input.replace(/\n/g, "\n" + newIndent);
}

export function setIndent(indent: number) {
  _indent = indent;
export function setIndent(newIndent: number) {
  _indent = newIndent;
}

export function format(input: string): string {

M src/converter/converter/templates/intent-template.ts => src/converter/converter/templates/intent-template.ts +12 -13
@@ 146,9 146,9 @@ export { ${this.templateVariableName()} };
        return `det("${part.meta.substr(5)}")`;
      case PartType.UserGenerated:
        const name = part.meta.substr(1);
        const varName = getVar(`${ENTITY_PREFIX}_${name}`);
        if (varName !== null) {
          return `pb(${varName})`;
        const entityVarName = getVar(`${ENTITY_PREFIX}_${name}`);
        if (entityVarName !== null) {
          return `pb(${entityVarName})`;
        }
        return `pb("${name}")`;
      default:


@@ 205,17 205,16 @@ export { ${this.templateVariableName()} };
  }

  public static processSpeech(speech: string | string[]) {
    const val = isStringArray ? speech : [speech];
    const val = isStringArray(speech) ? speech : [speech];
    return JSON.stringify(val);
  }

  private messageTypeFrom(msgType: number = 0): string {
    switch (msgType) {
      case 999:
        return "the other one? ¯_(ツ)_/¯";
      default:
        return "text";
    if (msgType === 999) {
      return "the other one? ¯_(ツ)_/¯";
    }

    return "text";
  }

  private templateOutputContexts(): string {


@@ 229,7 228,7 @@ export { ${this.templateVariableName()} };
    }
    return [...this._outputContexts]
      .filter(x => x.name !== undefined)
      .map(x => `${varName(x.name!, this.cxPre)},`)
      .map(x => `${varName(x.name, this.cxPre)},`)
      .join("\n");
  }



@@ 247,9 246,9 @@ export { ${this.templateVariableName()} };
    for (let phrase of this.intent.trainingPhrases) {
      for (let part of phrase.data) {
        if (IntentTemplate.typeOfPart(part) === PartType.UserGenerated) {
          const varName = getVar(`${ENTITY_PREFIX}_${part.meta.substr(1)}`);
          if (varName !== null) {
            this.addImport(varName, `../${ENTITY_TYPE_DIR}`);
          const entityVarName = getVar(`${ENTITY_PREFIX}_${part.meta.substr(1)}`);
          if (entityVarName !== null) {
            this.addImport(entityVarName, `../${ENTITY_TYPE_DIR}`);
          }
        }
      }

M src/sample/index.ts => src/sample/index.ts +1 -1
@@ 33,7 33,7 @@ export async function sample(logLevel: LogLevel = 0) {
  await dfc.sync(resources);

  if (logger) {
    (await Container.get(IntentsService))
    await Container.get(IntentsService)
      .getIntents()
      .then((intents) =>
        logger.verbose(`Intents: ${JSON.stringify(intents, null, 2)}`),

M src/sync/index.ts => src/sync/index.ts +1 -1
@@ 207,7 207,7 @@ export class DialogflowCreator {
      return {
        ...intent,
        outputContexts: intent.outputContexts!.map((ctx) => {
          return { ...ctx, name: this.contextInPath(ctx.name!) };
          return { ...ctx, name: this.contextInPath(ctx.name) };
        }),
        inputContextNames: intent.inputContextNames!.map((name) =>
          this.contextInPath(name),

M src/test-maker/cli.ts => src/test-maker/cli.ts +14 -17
@@ 1,36 1,33 @@
#!/usr/bin/env node

import _ from "lodash";
import { LogLevel } from "../services";
import { ProgramBuilder } from "./common/build-program";
import { Options } from "./common-interfaces/cli-interfaces";
import { DACTestError } from "./common/dac-test-error";
import { exit } from "./common/exit";
import { Logger } from "./common/logger";
import { StatusCode } from "./common/status-code";
import { testTemplate } from "./templates/test-template";
import { writeFile } from "./common/write-file";
import _ from "lodash";
import { Logger } from "./common/logger";
import { LogLevel } from "../services";
import { testTemplate } from "./templates/test-template";

async function main(options: Promise<Options>) {
async function main() {
  const options = await ProgramBuilder.buildProgram();
  const testFile = testTemplate({
    file: (await options).file,
    filter: (await options).filter,
    intents: _.flatten(await Promise.all((await options).intents)),
    entities: _.flatten(await Promise.all((await options).entities)),
    logger: (await options).logger,
    file: options.file,
    filter: options.filter,
    intents: _.flatten(options.intents),
    entities: _.flatten(options.entities),
    logger: options.logger,
  });
  writeFile((await options).file, testFile);
  writeFile(options.file, testFile);
}

//TOP-LEVEL
try {
  const options = ProgramBuilder.buildProgram();
  main(options);
} catch (e) {
main().catch((e: Error) => {
  const logger = new Logger(LogLevel.NORMAL);
  logger.error(e);
  if (e instanceof DACTestError) {
    exit(e.code);
  }
  exit(StatusCode.INVALID_USAGE);
}
});
\ No newline at end of file

M src/test-maker/common/dac-test-error.ts => src/test-maker/common/dac-test-error.ts +2 -1
@@ 1,4 1,5 @@
import { StatusCode } from "./status-code";

export class DACTestError extends Error {
  public readonly code: StatusCode;
  public constructor(message: string, code: StatusCode) {


@@ 6,4 7,4 @@ export class DACTestError extends Error {
    Object.setPrototypeOf(this, DACTestError.prototype);
    this.code = code;
  }
};
\ No newline at end of file
}
\ No newline at end of file

M src/test-maker/common/entity-converter.ts => src/test-maker/common/entity-converter.ts +1 -1
@@ 16,7 16,7 @@ export function entityConverter(entity: Part, entities: EntityType[]): string {
      Math.floor(Math.random() * Math.floor(matched.entities.length - 0.01))
    ].value;
  }
  if (entity.entityType === "@sys.number") {;
  if (entity.entityType === "@sys.number") {
    const item = Math.floor(Math.random() * (NumberSynonym.length - 0.01)) + 0;
    return NumberSynonym[item];
  }

M src/test-suite/common/matrix-builder.ts => src/test-suite/common/matrix-builder.ts +1 -1
@@ 38,7 38,7 @@ export class MatrixBuilder {
      .fill(0)
      .map(() => new Array(labelMap.labels.length).fill(0));
    for (const xAxisIntentName in matrix) {
      matrix[xAxisIntentName].map((intent, yAxisIntentIndex) => {
      matrix[xAxisIntentName].forEach((_intent, yAxisIntentIndex) => {
        const yAxisIntentName =
          matrix[xAxisIntentName][yAxisIntentIndex].actual;
        const xAxisValue = labelMap.numbers[xAxisIntentName];

M src/test-suite/reporters/cli-reporter.ts => src/test-suite/reporters/cli-reporter.ts +5 -4
@@ 3,6 3,7 @@ import figlet from "figlet";
import { Reporter } from "../common-interfaces/cli-interfaces";
import { TestResults } from "../common-interfaces/data-interfaces";
import { Logger } from "../common/logger";

export class CLIReporter implements Reporter {
  private data: TestResults[] = [];
  public formatData(data: TestResults[]) {


@@ 11,16 12,16 @@ export class CLIReporter implements Reporter {
  public activateReport(logger: Logger): void {
    logger.log(chalk.magenta(figlet.textSync("DAC Testing Suite", "Big")));
    let [testsRan, failures] = [0, 0];
    this.data.map(testFileResult => {
    this.data.forEach(testFileResult => {
      testsRan += testFileResult.testResults.length;
      failures += testFileResult.errorStack.length;
    });
    logger.log(chalk.blue("Tests ran: ") + testsRan);
    logger.log(chalk.green("Succeeded: ") + (testsRan - failures));
    logger.log(chalk.red("Failures: ") + failures);
    this.data.map(testFileResult => {
      testFileResult.errorStack.map(error => {
        error.messages.map(message => {
    this.data.forEach(testFileResult => {
      testFileResult.errorStack.forEach(error => {
        error.messages.forEach(message => {
          logger.log(`${message}`);
        });
      });

M src/test-suite/testers/test-runner.ts => src/test-suite/testers/test-runner.ts +2 -2
@@ 35,9 35,9 @@ export class TestRunner {
  }
  public getExitStatus(results: TestResults[]): StatusCode {
    let [strictfails, laxfails, tests] = [0, 0, 0];
    results.map(result => {
    results.forEach(result => {
      tests += result.testResults.length;
      result.errorStack.map(error => {
      result.errorStack.forEach(error => {
        if (error.rantest.strict !== undefined && !error.rantest.strict) {
          laxfails++;
        } else {

M src/util/index.ts => src/util/index.ts +1 -1
@@ 25,7 25,7 @@ export function processGrpc<T>(p: Promise<[T]>, stage?: string): Promise<T> {
      throw new Error("Null value in gRPC response");
    })
    .catch((err) => {
      (Container.get(LoggerService) as LoggerService).error(
      Container.get(LoggerService).error(
        `Error encountered: ${stage}`,
      );
      throw err;