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: add new context command to make it easier to work with multiple files #14

Merged
merged 34 commits into from
Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e25ff75
feat: adding context commands in help message
Souvikns Jun 18, 2021
32d7947
feat: initial approach
Souvikns Jun 20, 2021
b08d595
feat: context service created
Souvikns Jun 21, 2021
294be41
feat: connected list and current
Souvikns Jun 22, 2021
1a78f61
feat: update the context commands
Souvikns Jun 23, 2021
e0e1d7e
fix: issue with the file imports
Souvikns Jun 24, 2021
677136d
refactor: contextPath
Souvikns Jun 24, 2021
38775cd
chore: changed the old format
Souvikns Jun 25, 2021
bf1d1d7
feat: added required functions in useContextFile
Souvikns Jun 28, 2021
203264b
feat: command and subcommand router
Souvikns Jun 28, 2021
f061205
feat: auto detect specific spec file in the working directory
Souvikns Jun 28, 2021
4695943
feat: created all the component for context.
Souvikns Jun 30, 2021
959b6dd
feat: moved logic to service class and connected the commands to the …
Souvikns Jul 2, 2021
925b3d8
feat: added custom error message for keynotfound error
Souvikns Jul 2, 2021
0554dc5
feat: created context removing logic
Souvikns Jul 2, 2021
19f624a
feat: auto setting current when adding context for the first tme.
Souvikns Jul 5, 2021
b4e632c
feat: updated router logic
Souvikns Jul 5, 2021
d44ab80
chore: renamed filename from context to Context
Souvikns Jul 5, 2021
7835775
chore: cleaned all duplicate code
Souvikns Jul 5, 2021
e74850d
feat: updated command specification
Souvikns Jul 6, 2021
959ba71
feat: removed dummy spec paths
Souvikns Jul 6, 2021
5f4ee97
feat: added logic to fetch context from flag
Souvikns Jul 7, 2021
b954773
feat: updated validate component to work with saved context
Souvikns Jul 7, 2021
f429cd4
feat: updated the test cases for validate component
Souvikns Jul 7, 2021
1cfb902
fix: fixed the bug with deleting context that was set as current cont…
Souvikns Jul 7, 2021
66e82a9
fix: fixed the e.location bug
Souvikns Jul 9, 2021
33c5fab
fix: parser error logs.
Souvikns Jul 9, 2021
7dde0c7
fix: update help command text
Souvikns Jul 9, 2021
1214e6c
feat: updated documentation and CLI help message
Souvikns Jul 9, 2021
98b5c7b
feat: fixing the doc
Souvikns Jul 9, 2021
a244692
feat: add context now checks if the path exists.
Souvikns Jul 9, 2021
18d7076
feat: updated the suggested changes
Souvikns Jul 9, 2021
348c6bc
docs: added info about required --context flag for validation
Souvikns Jul 9, 2021
ec079c6
feat: created return type for useContextFile hook
Souvikns Jul 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,19 @@ Usage
Commands
validate
Options
-c --context Your AsyncAPI specification
-c --context context-name saved in the store
-w --watch Enable watchMode (not implemented yet)

context
current show the current set context
list show the list of all stored contexts
remove <context-name> remove a context from the store
use <context-name> set any context from store as current
add <context-name> <filepath> add/update new context

Examples
$ asyncapi validate --context=specification.yml
$ asyncapi context add dummy ./asyncapi.yml
$ asyncapi validate --context=dummy
```

> For now --context flag is requried to run validate command
23 changes: 15 additions & 8 deletions src/CliModels.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

export type Command = string;
export type HelpMessage = string;
export type Arguments = string[];

export interface Options {
context: string,
Expand All @@ -10,12 +11,12 @@ export interface Options {
export class CliInput {
private readonly _command: Command
private readonly _options: Options
private readonly _helpMessage: HelpMessage
private readonly _arguments: Arguments

private constructor(command: Command, options: Options, helpMessage: HelpMessage) {
private constructor(command: Command, options: Options, args: Arguments) {
this._command = command;
this._options = options;
this._helpMessage = helpMessage;
this._arguments = args;
}

get command(): Command {
Expand All @@ -26,14 +27,20 @@ export class CliInput {
return this._options;
}

get helpMessage(): HelpMessage {
return this._helpMessage;

get arguments(): Arguments {
return this._arguments;
}

static createFromMeow(meowOutput: any): CliInput {
const [command] = meowOutput.input;
const help = meowOutput.help;
const [command, ...args] = meowOutput.input;
const { context, watch } = meowOutput.flags;
return new CliInput(command || 'help', { context, watch }, help);
return new CliInput(command || 'help', { context, watch }, args);
}

static createSubCommand(cliInput: CliInput): CliInput {
const [command, ...args] = cliInput.arguments;
return new CliInput(command || 'help', cliInput.options, args);
}
}

2 changes: 1 addition & 1 deletion src/CommandsRouter.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('CommandsRouter should', () => {

setTimeout(() => {
expect(lastFrame()).toBe(chalk.green(`File: ${file.getSpecificationName()} successfully validated!`));
done();
}, 200);
done();
Souvikns marked this conversation as resolved.
Show resolved Hide resolved
});
});
8 changes: 5 additions & 3 deletions src/CommandsRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import React from "react";
import Validate from "./components/Validate/Validate";
import { contextRouter } from './components/Context';
import { CliInput } from "./CliModels";


const commandsDictionary = (cliInput: CliInput) => ({
['validate']: <Validate options={cliInput.options}/>,
['validate']: <Validate options={cliInput.options} />,
['context']: contextRouter(cliInput)
});

export const commandsRouter = (cli: any) => {
const cliInput = CliInput.createFromMeow(cli);
// @ts-ignore

//@ts-ignore
return commandsDictionary(cliInput)[cliInput.command];
};
13 changes: 10 additions & 3 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,24 @@ const cli = meow(`
Commands
validate
Options
-c --context Your AsyncAPI specification
-c --context context-name saved in the store
-w --watch Enable watchMode (not implemented yet)
context
current show the current set context
list show the list of all stored context
remove <context-name> remove a context from the store
use <context-name> set any context as current
add <context-name> <filepath> add/update new context

Examples
$ asyncapi validate --context=specification.yml
$ asyncapi context add dummy ./asyncapi.yml
$ asyncapi validate --context=dummy
`, {
flags: {
context: {
alias: 'c',
type: 'string',
isRequired: true
isRequired: false
derberg marked this conversation as resolved.
Show resolved Hide resolved
Souvikns marked this conversation as resolved.
Show resolved Hide resolved
},
watch: {
alias: 'w',
Expand Down
87 changes: 87 additions & 0 deletions src/components/Context/Context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { useContextFile } from '../../hooks/context';
import React, { FunctionComponent } from 'react';
import { Box, Text } from 'ink';
import ContextError from './contexterror';
import { SpecificationFile } from '../../hooks/validation';

export const ListContexts: FunctionComponent = () => {
const { response, error } = useContextFile().list();

if (error) {
return <ContextError error={error} />
}

if (response) {
return <Box flexDirection="column">
{response.map((context: any) => <Text key={context.key}>{context.key} : {context.path}</Text>)}
</Box>
}

return <></>
}

export const ShowCurrentContext: FunctionComponent = () => {
const { response, error } = useContextFile().current();

if (error) {
return <ContextError error={error} />
}

if (response) {
return <Text>{response.key} : {response.path}</Text>
}

return <></>
}

export const AddContext: FunctionComponent<{ options: any, args: string[] }> = ({ args }) => {
const [key, path] = args

if (!key || !path) {
return <ContextError error={new Error("missing arguments")} />
}

const { response, error } = useContextFile().addContext(key, new SpecificationFile(path));

if (error) {
return <ContextError error={error} />
}

return <Text>{response}</Text>
}

export const SetCurrent: FunctionComponent<{ options: any, args: string[] }> = ({ args }) => {
const [key,] = args;

if (!key) {
return <ContextError error={new Error("missing arguments")} />
}
Souvikns marked this conversation as resolved.
Show resolved Hide resolved

const { response, error } = useContextFile().setCurrent(key);

if (error) {
return <ContextError error={error} />
}

if (response) {
return <Text>{response.key} : {response.path}</Text>
}

return <></>
}

export const RemoveContext: FunctionComponent<{ options: any, args: string[] }> = ({ args }) => {
const [key] = args;

if (!key) {
return <ContextError error={new Error("missing arguments")} />
}

const { response, error } = useContextFile().deleteContext(key);

if (error) {
return <ContextError error={error} />
}

return <Text>{response}</Text>
}
60 changes: 60 additions & 0 deletions src/components/Context/context.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import { render } from 'ink-testing-library';
import { ListContexts, ShowCurrentContext, AddContext, SetCurrent } from './Context';
import { ContextTestingHelper } from '../../constants';

let testing = new ContextTestingHelper();

describe('listing contexts', () => {
test("should render error when no context file found", () => {
testing.deleteDummyContextFile();
let { lastFrame } = render(<ListContexts />);
expect(lastFrame()).toMatch("No contexts saved yet.");
Souvikns marked this conversation as resolved.
Show resolved Hide resolved
})

test("Should render the context list", () => {
testing.createDummyContextFile()
let { lastFrame } = render(<ListContexts />);
expect(lastFrame()).toMatch(
`home : ${testing.context.store["home"]}\n` +
`code : ${testing.context.store["code"]}`
);
})
})
Souvikns marked this conversation as resolved.
Show resolved Hide resolved

describe('rendering current context', () => {
test('showing error if now current context is found', () => {
testing.deleteDummyContextFile();
let { lastFrame } = render(<ShowCurrentContext />);
expect(lastFrame()).toMatch('No contexts saved yet.');
})

test('showing current context ', () => {
testing.createDummyContextFile();
let { lastFrame } = render(<ShowCurrentContext />);
expect(lastFrame()).toMatch(`home : ${testing.context.store["home"]}`);
})
})

describe('AddContext ', () => {
test("should return message", () => {
testing.createDummyContextFile();
let { lastFrame } = render(<AddContext options={{}} args={['home', './test/specification.yml']} />);
expect(lastFrame()).toMatch('New context added');
})
})

describe('SetContext ', () => {

test('Should render error message is key is not in store', () => {
testing.createDummyContextFile();
let { lastFrame } = render(<SetCurrent args={['name']} options={{}} />)
expect(lastFrame()).toMatch('The context you are trying to use is not present');
});

test('Should render the update context', () => {
testing.createDummyContextFile();
let { lastFrame } = render(<SetCurrent args={['code']} options={{}} />)
expect(lastFrame()).toMatch(`code : ${testing.context.store['code']}`);
});
})
22 changes: 22 additions & 0 deletions src/components/Context/contexterror.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { FunctionComponent } from 'react';
import { Text } from 'ink';
import { ContextFileNotFoundError, DeletingCurrentContextError, KeyNotFoundError } from '../../hooks/context';

const ContextError: FunctionComponent<{ error: Error }> = ({ error }) => {

if (error instanceof ContextFileNotFoundError) {
return <Text>No contexts saved yet.</Text>
}

if(error instanceof KeyNotFoundError){
return <Text>The context you are trying to use is not present</Text>
}

if(error instanceof DeletingCurrentContextError) {
return <Text>You are trying to delete a context that is set as current.</Text>
}

return <Text>{error.message}</Text>
};

export default ContextError;
18 changes: 18 additions & 0 deletions src/components/Context/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { CliInput } from '../../CliModels';
import { ListContexts,AddContext,RemoveContext,ShowCurrentContext,SetCurrent } from './Context';

//@ts-ignore
const commandDictionary = (cliInput: CliInput) => ({
['list']: <ListContexts />,
['current']: <ShowCurrentContext />,
['use']: <SetCurrent options={cliInput.options} args={cliInput.arguments} />,
['add']: <AddContext options={cliInput.options} args={cliInput.arguments} />,
['remove']: <RemoveContext options={cliInput.options} args={cliInput.arguments} />
})

export const contextRouter = (cliInput: CliInput) => {
let subCommand = CliInput.createSubCommand(cliInput);
//@ts-ignore
return commandDictionary(subCommand)[subCommand.command];
}
15 changes: 11 additions & 4 deletions src/components/Validate/Validate.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Options } from "../../CliModels";
import { UseValidateResponse } from "../../hooks/validation/models";
import chalk from "chalk";
import * as validationHook from '../../hooks/validation';
import { ContextTestingHelper } from '../../constants';



function makeUseValidateReturn(response: UseValidateResponse) {
Expand All @@ -14,13 +16,16 @@ function makeUseValidateReturn(response: UseValidateResponse) {
}

function renderValidationComponentWith(options: Options) {
return render(<Validate options={options}/>);
return render(<Validate options={options} />);
}

const testing = new ContextTestingHelper();

describe('Validate component should', () => {
test('render the success message in green color when the specification is correct', (done) => {
testing.createDummyContextFile();
const options: Options = {
context: 'oneFile.yml',
context: 'home',
watch: false,
};

Expand All @@ -35,8 +40,9 @@ describe('Validate component should', () => {
});

test('render the single error message in red color when the file is not valid', (done) => {
testing.createDummyContextFile();
const options: Options = {
context: 'oneFile.yml',
context: 'home',
watch: false,
};

Expand All @@ -51,8 +57,9 @@ describe('Validate component should', () => {
});

test('render the different error messages in red color when the validation fails', (done) => {
testing.createDummyContextFile();
const options: Options = {
context: 'oneFile.yml',
context: 'home',
watch: false,
};

Expand Down
Loading