Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: change from flags to arguments when passing path to a file or context name #59

Merged
merged 12 commits into from
Sep 13, 2021
Merged
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,11 @@ context Manage contexts
```
USAGE

asyncapi validate [options]
asyncapi validate <spec-file-path | context-name> [options]

OPTIONS

-h, --help display help for command
-f, --file <spec-file-path> Path of the asyncapi file
-c, --context <saved-context-name> context name to use
-w, --watch Enable Watch Mode (not implemented yet)
```

Expand Down
2 changes: 1 addition & 1 deletion src/CommandsRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CliInput } from './CliModels';
import { CommandName, HelpMessageBuilder } from './help-message';

const commandsDictionary = (cliInput: CliInput): Record<string, any> => ({
validate: <Validate options={cliInput.options} />,
validate: <Validate options={cliInput.options} parameter={cliInput.arguments[0]} />,
context: contextRouter(cliInput)
});

Expand Down
14 changes: 8 additions & 6 deletions src/components/Validate/Validate.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import React, { useEffect, useState } from 'react';
import { Newline, Text } from 'ink';

import { useSpecfile } from '../../hooks/context';
import { loadSpecFileForValidation } from '../../hooks/context';
import { UseValidateResponse } from '../../hooks/validation/models';
import { SpecificationFile, useValidate } from '../../hooks/validation';
import { useValidate } from '../../hooks/validation';
import { Options } from '../../CliModels';

interface ValidateInput {
options: Options;
options: Options,
parameter?: string | undefined
}

const Validate: React.FunctionComponent<ValidateInput> = ({ options }) => {
const { specFile, error } = useSpecfile({ context: options.context, file: options.file });
const Validate: React.FunctionComponent<ValidateInput> = ({ options, parameter }) => {
const {specFile, error} = loadSpecFileForValidation(parameter);

if (error) {
return <Text color="red">{error.message}</Text>;
}
Expand All @@ -21,7 +23,7 @@ const Validate: React.FunctionComponent<ValidateInput> = ({ options }) => {
}

const validationInput = {
file: new SpecificationFile(specFile.getSpecificationName()),
file: specFile,
watchMode: options.watch,
};

Expand Down
21 changes: 12 additions & 9 deletions src/help-message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ export type CommandName = typeof CommandList[number]

export type Command = {
[name in CommandName]: {
usage: string;
usage: {
command?: string,
options?: string
};
shortDescription: string;
longDescription?: string;
flags: string[];
Expand All @@ -28,17 +31,17 @@ export class HelpMessage {

readonly commands: Command = {
validate: {
usage: 'asyncapi validate [options]',
usage: {
command: '<spec-file-path | context-name>'
},
shortDescription: 'Validate asyncapi file',
flags: [
this.helpFlag,
'-f, --file <spec-file-path> Path of the AsyncAPI file',
'-c, --context <saved-context-name> Context to use',
'-w, --watch Enable watch mode (not implemented yet)'
]
},
context: {
usage: 'asyncapi context [options] [command]',
usage: {},
shortDescription: 'Manage context',
longDescription: 'Context is what makes it easier for you to work with multiple AsyncAPI files.\nYou can add multiple different files to a context.\nThis way you do not have to pass --file flag with path to the file every time but just --context flag with reference name.\nYou can also set a default context, so neither --file nor --context flags are needed',
flags: [this.helpFlag],
Expand Down Expand Up @@ -103,10 +106,10 @@ export class HelpMessageBuilder {
<Text backgroundColor="greenBright" bold color="blackBright"> USAGE </Text>
<Newline />
<Text>
<Text color="greenBright">{commandHelpObject.usage.split(' ')[0]}</Text>{' '}
<Text>{commandHelpObject.usage.split(' ')[1]}</Text>{' '}
<Text color="yellowBright">{commandHelpObject.usage.split(' ')[2]}</Text>{' '}
<Text color="blueBright">{commandHelpObject.usage.split(' ')[3]}</Text>
<Text color="greenBright">asyncapi</Text>{' '}
<Text>{command}</Text>{' '}
<Text color="blueBright">{commandHelpObject.usage.command || '[command]'}</Text>{' '}
<Text color="yellowBright">{commandHelpObject.usage.options || '[options]'}</Text>
</Text>
<Newline />

Expand Down
5 changes: 5 additions & 0 deletions src/hooks/context/contextService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export class ContextService {
return JSON.parse(fs.readFileSync(CONTEXTFILE_PATH, 'utf-8')) as Context;
}

isContext(): boolean {
if (!fs.existsSync(CONTEXTFILE_PATH)) {return true;}
return false;
}

deleteContextFile(): void {
if (fs.existsSync(CONTEXTFILE_PATH)) {fs.unlinkSync(CONTEXTFILE_PATH);}
}
Expand Down
57 changes: 57 additions & 0 deletions src/hooks/context/hooks.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable security/detect-object-injection */
import { Context, ContextFileNotFoundError, ContextNotFoundError, MissingCurrentContextError, NoSpecPathFoundError } from './models';
import { ContextService } from './contextService';
import { container } from 'tsyringe';
Expand Down Expand Up @@ -172,3 +173,59 @@ const autoDetectSpecFileInWorkingDir = (specFile: string | undefined, command: s
if (typeof specFile === 'undefined') { throw new NoSpecPathFoundError(command); }
return new SpecificationFile(specFile);
};

const loadCurrentContext = (contextService: ContextService): { currentContext?: string | undefined, error?: Error } => {
let currentContext: string | undefined;
try {
const ctx = contextService.loadContextFile();
currentContext = ctx.store[ctx.current];
return { currentContext };
} catch (error) {
return { error };
}
};

export const loadSpecFileForValidation = (input: string | undefined): useSpecFileOutput => {
const contextService: ContextService = container.resolve(ContextService);
const cliService: CLIService = container.resolve(CLIService);
let specFile: SpecificationFile;

try {
if (!input) {
const { currentContext, error } = loadCurrentContext(contextService);
if (error) {
specFile = autoDetectSpecFileInWorkingDir(contextService.autoDetectSpecFile(), cliService.command());
return { specFile };
}
if (currentContext) {
specFile = new SpecificationFile(currentContext);
return { specFile };
}
throw new NoSpecPathFoundError(cliService.command());
}

specFile = new SpecificationFile(input);

if (!specFile.isNotValid()) {
return {specFile};
}
const ctx = contextService.loadContextFile();
if (Object.keys(ctx.store).includes(input)) {
specFile = new SpecificationFile(ctx.store[input] as string);
return { specFile };
}

throw new NoSpecPathFoundError(cliService.command());
} catch (error) {
if (error instanceof ContextFileNotFoundError) {
try {
specFile = autoDetectSpecFileInWorkingDir(contextService.autoDetectSpecFile(), cliService.command());
return { specFile };
} catch (error) {
return { error };
}
}

return { error };
}
};
4 changes: 2 additions & 2 deletions src/messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,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: 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 by passing ${contextName} as a parameter: asyncapi valiate ${contextName}`;

export const CONTEXT_DELETED = 'context deleted successfully';

Expand All @@ -16,7 +16,7 @@ export const ValidationMessage = (filePath: string) => ({
message: () => `File: ${filePath} successfully validated!`
});

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 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 provide a path to the AsyncAPI file: asyncapi ${command} path/to/file/asyncapi.yml\n- You can also pass a saved context that points to your AsyncAPI file: asyncapi ${command} 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.'
Expand Down