From 91ba5c62d8eb011e4b37865c23ce9c917abc9248 Mon Sep 17 00:00:00 2001 From: Souvik Date: Thu, 12 Aug 2021 10:49:37 +0530 Subject: [PATCH 1/5] fix: issue with not reading spec file in working directory when no global context is set. --- src/hooks/context/hooks.tsx | 26 +++++++++++++++++++------- src/hooks/context/models.ts | 7 +++++++ src/messages.tsx | 2 ++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/hooks/context/hooks.tsx b/src/hooks/context/hooks.tsx index 1fa8f0b0962..d7cab4084c4 100644 --- a/src/hooks/context/hooks.tsx +++ b/src/hooks/context/hooks.tsx @@ -1,4 +1,4 @@ -import { Context, ContextFileNotFoundError, ContextNotFoundError, MissingCurrentContextError } from './models'; +import { Context, ContextFileNotFoundError, ContextNotFoundError, MissingCurrentContextError, NoSpecPathFoundError } from './models'; import { ContextService } from './contextService'; import { container } from 'tsyringe'; import { SpecificationFile } from '../validation'; @@ -125,10 +125,12 @@ export interface useSpecFileOutput { export const useSpecfile = (flags: useSpecFileInput): useSpecFileOutput => { const contextService: ContextService = container.resolve(ContextService); + let autoDetectedSpecFile: string | undefined; + let specFile: SpecificationFile; try { if (flags.file) { - const specFile: SpecificationFile = new SpecificationFile(flags.file); + specFile = new SpecificationFile(flags.file); if (specFile.isNotValid()) { throw new Error('Invalid spec path'); } return { specFile }; } @@ -138,25 +140,35 @@ export const useSpecfile = (flags: useSpecFileInput): useSpecFileOutput => { if (flags.context) { const ctxFile = ctx.store[flags.context]; if (!ctxFile) { throw new ContextNotFoundError(flags.context); } - const specFile = new SpecificationFile(ctxFile); + specFile = new SpecificationFile(ctxFile); return { specFile }; } if (ctx.current) { const currentFile = ctx.store[ctx.current]; if (!currentFile) { throw new MissingCurrentContextError(); } - const specFile = new SpecificationFile(currentFile); + specFile = new SpecificationFile(currentFile); return { specFile }; } - const autoDetectedSpecPath = contextService.autoDetectSpecFile(); + autoDetectedSpecFile = contextService.autoDetectSpecFile(); - if (typeof autoDetectedSpecPath === 'undefined') { throw new Error('No spec path found in your working directory, please use flags or store a context'); } + if (typeof autoDetectedSpecFile === 'undefined') { throw new NoSpecPathFoundError(); } - const specFile = new SpecificationFile(autoDetectedSpecPath); + specFile = new SpecificationFile(autoDetectedSpecFile); return { specFile }; } catch (error) { + if (error instanceof ContextFileNotFoundError) { + try { + autoDetectedSpecFile = contextService.autoDetectSpecFile(); + if (typeof autoDetectedSpecFile === 'undefined') { throw new NoSpecPathFoundError(); } + specFile = new SpecificationFile(autoDetectedSpecFile); + return { specFile }; + } catch (error) { + return { error }; + } + } return { error }; } }; diff --git a/src/hooks/context/models.ts b/src/hooks/context/models.ts index 46f1a462122..b04c40db783 100644 --- a/src/hooks/context/models.ts +++ b/src/hooks/context/models.ts @@ -40,3 +40,10 @@ export class MissingArgumentstError extends Error { this.message = messages.MISSING_ARGUMENTS; } } + +export class NoSpecPathFoundError extends Error { + constructor() { + super(); + this.message = messages.NO_SPEC_PATH_FOUND; + } +} diff --git a/src/messages.tsx b/src/messages.tsx index 199689ae98c..c969df8227e 100644 --- a/src/messages.tsx +++ b/src/messages.tsx @@ -14,3 +14,5 @@ export const ValidationMessage = (filePath: string) => ({ error: () => `File: ${filePath} does not exists or is not a file!`, message: () => `File: ${filePath} successfully validated!` }); + +export const NO_SPEC_PATH_FOUND = 'No spec path found in your working directory, please use flags or store a context'; From 66189932f9baaafb3d90457fe41d65218e2540ec Mon Sep 17 00:00:00 2001 From: Souvik Date: Thu, 12 Aug 2021 11:04:22 +0530 Subject: [PATCH 2/5] fix: lint fixes --- src/hooks/context/hooks.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/hooks/context/hooks.tsx b/src/hooks/context/hooks.tsx index d7cab4084c4..07c29670a0b 100644 --- a/src/hooks/context/hooks.tsx +++ b/src/hooks/context/hooks.tsx @@ -125,7 +125,6 @@ export interface useSpecFileOutput { export const useSpecfile = (flags: useSpecFileInput): useSpecFileOutput => { const contextService: ContextService = container.resolve(ContextService); - let autoDetectedSpecFile: string | undefined; let specFile: SpecificationFile; try { @@ -151,19 +150,13 @@ export const useSpecfile = (flags: useSpecFileInput): useSpecFileOutput => { return { specFile }; } - autoDetectedSpecFile = contextService.autoDetectSpecFile(); - - if (typeof autoDetectedSpecFile === 'undefined') { throw new NoSpecPathFoundError(); } - - specFile = new SpecificationFile(autoDetectedSpecFile); + specFile = autoDetectSpecFileInWorkingDir(contextService.autoDetectSpecFile()); return { specFile }; } catch (error) { if (error instanceof ContextFileNotFoundError) { try { - autoDetectedSpecFile = contextService.autoDetectSpecFile(); - if (typeof autoDetectedSpecFile === 'undefined') { throw new NoSpecPathFoundError(); } - specFile = new SpecificationFile(autoDetectedSpecFile); + specFile = autoDetectSpecFileInWorkingDir(contextService.autoDetectSpecFile()); return { specFile }; } catch (error) { return { error }; @@ -172,3 +165,8 @@ export const useSpecfile = (flags: useSpecFileInput): useSpecFileOutput => { return { error }; } }; + +const autoDetectSpecFileInWorkingDir = (specFile: string | undefined): SpecificationFile => { + if (typeof specFile === 'undefined') { throw new NoSpecPathFoundError(); } + return new SpecificationFile(specFile); +}; From c57cb868af7f76781205f6a5bd781ccbd83ce630 Mon Sep 17 00:00:00 2001 From: Souvik Date: Thu, 12 Aug 2021 14:10:23 +0530 Subject: [PATCH 3/5] feat: made suggested changes --- src/hooks/context/models.ts | 2 +- src/messages.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hooks/context/models.ts b/src/hooks/context/models.ts index b04c40db783..b6f1c383520 100644 --- a/src/hooks/context/models.ts +++ b/src/hooks/context/models.ts @@ -44,6 +44,6 @@ export class MissingArgumentstError extends Error { export class NoSpecPathFoundError extends Error { constructor() { super(); - this.message = messages.NO_SPEC_PATH_FOUND; + this.message = messages.NO_SPEC_FOUND; } } diff --git a/src/messages.tsx b/src/messages.tsx index c969df8227e..3b613fbb867 100644 --- a/src/messages.tsx +++ b/src/messages.tsx @@ -6,7 +6,7 @@ export const MISSING_CURRENT_CONTEXT = 'No context is set as current, please set export const MISSING_ARGUMENTS = 'Missing arguments.'; -export const NEW_CONTEXT_ADDED = (contextName: string) => `New context added.\n\nYou can set it as your current context:\n asyncapi context use ${contextName}\nYou can use this context when needed with --context flag: asyncapi validate --context ${contextName}`; +export const NEW_CONTEXT_ADDED = (contextName: string) => `New context added.\n\nYou can set it as your current context: asyncapi context use ${contextName}\nYou can use this context when needed with --context flag: asyncapi validate --context ${contextName}`; export const CONTEXT_DELETED = 'context deleted successfully'; @@ -15,4 +15,4 @@ export const ValidationMessage = (filePath: string) => ({ message: () => `File: ${filePath} successfully validated!` }); -export const NO_SPEC_PATH_FOUND = 'No spec path found in your working directory, please use flags or store a context'; +export const NO_SPEC_FOUND = 'Unable to perform validation. Specify what AsyncAPI file should be validated.\n\nThese are your options to specify in the CLI what AsyncAPI file should be used:\n- You can use --file flag to provide a path to the AsyncAPI file: asyncapi validate --file path/to/file/asyncapi.yml\n- You can use --context flag to provide a name of the context that points to your AsyncAPI file: asyncapi validate --context mycontext\n- In case you did not specify a context that you want to use, the CLI checks if there is a default context and uses it. To set default context run: asyncapi context use mycontext\n- In case you did not provide any reference to AsyncAPI file and there is no default context, the CLI detects if in your current working directory you have files like asyncapi.json, asyncapi.yaml, asyncapi.yml. Just rename your file accordingly.'; From da6926d42e192749f0505ff71b1fcb7403c64108 Mon Sep 17 00:00:00 2001 From: Souvik Date: Fri, 13 Aug 2021 17:53:25 +0530 Subject: [PATCH 4/5] feat: first approach for making message reusable --- src/hooks/cli/hook.ts | 12 ++++++++++++ src/hooks/cli/index.ts | 2 ++ src/hooks/cli/service.ts | 16 ++++++++++++++++ src/hooks/context/hooks.tsx | 10 ++++++---- src/hooks/context/models.ts | 4 ++-- src/messages.tsx | 2 +- 6 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 src/hooks/cli/hook.ts create mode 100644 src/hooks/cli/index.ts create mode 100644 src/hooks/cli/service.ts diff --git a/src/hooks/cli/hook.ts b/src/hooks/cli/hook.ts new file mode 100644 index 00000000000..8dd7bff7c4e --- /dev/null +++ b/src/hooks/cli/hook.ts @@ -0,0 +1,12 @@ +import { CLIService } from './service'; +import { container } from 'tsyringe'; + +export interface useCliOutput { + command: string, + args: any +} + +export const useCli = (): useCliOutput => { + const cliService: CLIService = container.resolve(CLIService); + return { command: cliService.command(), args: cliService.args() }; +}; diff --git a/src/hooks/cli/index.ts b/src/hooks/cli/index.ts new file mode 100644 index 00000000000..2e2b098406c --- /dev/null +++ b/src/hooks/cli/index.ts @@ -0,0 +1,2 @@ +export * from './hook'; +export * from './service'; diff --git a/src/hooks/cli/service.ts b/src/hooks/cli/service.ts new file mode 100644 index 00000000000..630fca6cfeb --- /dev/null +++ b/src/hooks/cli/service.ts @@ -0,0 +1,16 @@ +import meow from 'meow'; +import { injectable } from 'tsyringe'; + +injectable(); +export class CLIService { + private _cli = meow('', { argv: process.argv.slice(2), autoHelp: false }) + + command(): string { + return this._cli.input[0] || ''; + } + + args(): any { + return this._cli.flags; + } +} + diff --git a/src/hooks/context/hooks.tsx b/src/hooks/context/hooks.tsx index 07c29670a0b..86a5c513130 100644 --- a/src/hooks/context/hooks.tsx +++ b/src/hooks/context/hooks.tsx @@ -3,6 +3,7 @@ import { ContextService } from './contextService'; import { container } from 'tsyringe'; import { SpecificationFile } from '../validation'; import * as messages from '../../messages'; +import { CLIService } from '../cli'; export type Result = { response?: any, @@ -125,6 +126,7 @@ export interface useSpecFileOutput { export const useSpecfile = (flags: useSpecFileInput): useSpecFileOutput => { const contextService: ContextService = container.resolve(ContextService); + const cliService: CLIService = container.resolve(CLIService); let specFile: SpecificationFile; try { @@ -150,13 +152,13 @@ export const useSpecfile = (flags: useSpecFileInput): useSpecFileOutput => { return { specFile }; } - specFile = autoDetectSpecFileInWorkingDir(contextService.autoDetectSpecFile()); + specFile = autoDetectSpecFileInWorkingDir(contextService.autoDetectSpecFile(), cliService.command()); return { specFile }; } catch (error) { if (error instanceof ContextFileNotFoundError) { try { - specFile = autoDetectSpecFileInWorkingDir(contextService.autoDetectSpecFile()); + specFile = autoDetectSpecFileInWorkingDir(contextService.autoDetectSpecFile(), cliService.command()); return { specFile }; } catch (error) { return { error }; @@ -166,7 +168,7 @@ export const useSpecfile = (flags: useSpecFileInput): useSpecFileOutput => { } }; -const autoDetectSpecFileInWorkingDir = (specFile: string | undefined): SpecificationFile => { - if (typeof specFile === 'undefined') { throw new NoSpecPathFoundError(); } +const autoDetectSpecFileInWorkingDir = (specFile: string | undefined, command: string): SpecificationFile => { + if (typeof specFile === 'undefined') { throw new NoSpecPathFoundError(command); } return new SpecificationFile(specFile); }; diff --git a/src/hooks/context/models.ts b/src/hooks/context/models.ts index b6f1c383520..72b32e369df 100644 --- a/src/hooks/context/models.ts +++ b/src/hooks/context/models.ts @@ -42,8 +42,8 @@ export class MissingArgumentstError extends Error { } export class NoSpecPathFoundError extends Error { - constructor() { + constructor(command: string) { super(); - this.message = messages.NO_SPEC_FOUND; + this.message = messages.NO_SPEC_FOUND(command); } } diff --git a/src/messages.tsx b/src/messages.tsx index 3b613fbb867..d3db91285a1 100644 --- a/src/messages.tsx +++ b/src/messages.tsx @@ -15,4 +15,4 @@ export const ValidationMessage = (filePath: string) => ({ message: () => `File: ${filePath} successfully validated!` }); -export const NO_SPEC_FOUND = 'Unable to perform validation. Specify what AsyncAPI file should be validated.\n\nThese are your options to specify in the CLI what AsyncAPI file should be used:\n- You can use --file flag to provide a path to the AsyncAPI file: asyncapi validate --file path/to/file/asyncapi.yml\n- You can use --context flag to provide a name of the context that points to your AsyncAPI file: asyncapi validate --context mycontext\n- In case you did not specify a context that you want to use, the CLI checks if there is a default context and uses it. To set default context run: asyncapi context use mycontext\n- In case you did not provide any reference to AsyncAPI file and there is no default context, the CLI detects if in your current working directory you have files like asyncapi.json, asyncapi.yaml, asyncapi.yml. Just rename your file accordingly.'; +export const NO_SPEC_FOUND = (command: string) => `Unable to perform validation. Specify what AsyncAPI file should be validated.\n\nThese are your options to specify in the CLI what AsyncAPI file should be used:\n- You can use --file flag to provide a path to the AsyncAPI file: asyncapi ${command} --file path/to/file/asyncapi.yml\n- You can use --context flag to provide a name of the context that points to your AsyncAPI file: asyncapi ${command} --context mycontext\n- In case you did not specify a context that you want to use, the CLI checks if there is a default context and uses it. To set default context run: asyncapi context use mycontext\n- In case you did not provide any reference to AsyncAPI file and there is no default context, the CLI detects if in your current working directory you have files like asyncapi.json, asyncapi.yaml, asyncapi.yml. Just rename your file accordingly.`; From 63160a17ce4443bf1cfe76746b3b41a44569f18d Mon Sep 17 00:00:00 2001 From: Souvik Date: Mon, 16 Aug 2021 15:35:04 +0530 Subject: [PATCH 5/5] feat: divided the no_spec_found message --- src/messages.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/messages.tsx b/src/messages.tsx index d3db91285a1..0fa1d77864d 100644 --- a/src/messages.tsx +++ b/src/messages.tsx @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-object-injection */ export const NO_CONTEXTS_SAVED = 'No contexts saved yet, run asyncapi --help to learn more'; export const CONTEXT_NOT_FOUND = (contextName: string) => `Context ${contextName} does not exists.`; @@ -15,4 +16,8 @@ export const ValidationMessage = (filePath: string) => ({ message: () => `File: ${filePath} successfully validated!` }); -export const NO_SPEC_FOUND = (command: string) => `Unable to perform validation. Specify what AsyncAPI file should be validated.\n\nThese are your options to specify in the CLI what AsyncAPI file should be used:\n- You can use --file flag to provide a path to the AsyncAPI file: asyncapi ${command} --file path/to/file/asyncapi.yml\n- You can use --context flag to provide a name of the context that points to your AsyncAPI file: asyncapi ${command} --context mycontext\n- In case you did not specify a context that you want to use, the CLI checks if there is a default context and uses it. To set default context run: asyncapi context use mycontext\n- In case you did not provide any reference to AsyncAPI file and there is no default context, the CLI detects if in your current working directory you have files like asyncapi.json, asyncapi.yaml, asyncapi.yml. Just rename your file accordingly.`; +export const NO_SPEC_FOUND = (command: string) => `${FALLBACK_MESSAGES[command]}\n\nThese are your options to specify in the CLI what AsyncAPI file should be used:\n- You can use --file flag to provide a path to the AsyncAPI file: asyncapi ${command} --file path/to/file/asyncapi.yml\n- You can use --context flag to provide a name of the context that points to your AsyncAPI file: asyncapi ${command} --context mycontext\n- In case you did not specify a context that you want to use, the CLI checks if there is a default context and uses it. To set default context run: asyncapi context use mycontext\n- In case you did not provide any reference to AsyncAPI file and there is no default context, the CLI detects if in your current working directory you have files like asyncapi.json, asyncapi.yaml, asyncapi.yml. Just rename your file accordingly.`; + +export const FALLBACK_MESSAGES: {[name: string]: string} = { + validate: 'Unable to perform validation. Specify what AsyncAPI file should be validated.' +};