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

chore: config command UI/UX improvement. #1643

Merged
merged 2 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .changeset/five-moles-care.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"@asyncapi/cli": patch
---

Added UI/UX improvements to start command
implemented new UI/UX improvements in config command
11 changes: 6 additions & 5 deletions src/commands/config/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Command from '../../core/base';
import { promises as fPromises } from 'fs';
import { homedir } from 'os';
import { analyticsFlags } from '../../core/flags/config/analytics.flags';
import { blueBright, redBright } from 'picocolors';

const { readFile, writeFile } = fPromises;

Expand All @@ -26,7 +27,7 @@ export default class Analytics extends Command {
this.log('\nAnalytics enabled.\n');
this.metricsMetadata.analytics_enabled = flags.enable;
} else if (!flags.status) {
this.log('\nPlease append the "--disable" flag to the command in case you prefer to disable analytics, or use the "--enable" flag if you want to enable analytics back again. In case you do not know the analytics current status, then you can append the "--status" flag to be aware of it.\n');
this.log(`\nPlease append the ${blueBright('--disable')} flag to the command if you prefer to disable analytics, or use the ${blueBright('--enable')} flag if you want to enable analytics again. To check the current analytics status, use the ${blueBright('--status')} flag.\n`);
return;
}
await writeFile(analyticsConfigFile, JSON.stringify(analyticsConfigFileContent), { encoding: 'utf8' });
Expand All @@ -35,20 +36,20 @@ export default class Analytics extends Command {
if (analyticsConfigFileContent.analyticsEnabled === 'true') {
this.log('\nAnalytics are enabled.\n');
} else {
this.log('\nAnalytics are disabled. Please append the "--enable" flag to the command in case you prefer to enable analytics.\n');
this.log(`\n${redBright('Analytics are disabled.')} To enable analytics, use the ${blueBright('--enable')} flag.\n`);
}
this.metricsMetadata.analytics_status_checked = flags.status;
}
} catch (e: any) {
switch (e.code) {
case 'ENOENT':
this.error(`Unable to access the analytics configuration file. We tried to access the ".asyncapi-analytics" file in in the path "${analyticsConfigFile}" but the file could not be found.`);
this.error(`Unable to access the analytics configuration file. We tried to access the ${blueBright('.asyncapi-analytics')} file in the path "${blueBright(analyticsConfigFile)}" but the file could not be found.`);
break;
case 'EEXIST':
this.error(`Unable to update the analytics configuration file. We tried to update your ".asyncapi-analytics" file in the path "${analyticsConfigFile}" but the file does not exist.`);
this.error(`Unable to update the analytics configuration file. We tried to update your ".asyncapi-analytics" file in the path "${blueBright(analyticsConfigFile)}" but the file does not exist.`);
break;
default:
this.error(`Unable to change your analytics configuration. Please check the following message for further info about the error:\n\n${e}`);
this.error(`Unable to change your analytics configuration. Please check the following message for further info about the error:\n\n${redBright(e)}`);
}
}
}
Expand Down
15 changes: 4 additions & 11 deletions src/commands/config/context/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ContextFileWrongFormatError,
} from '../../../core/errors/context-error';
import { addFlags } from '../../../core/flags/config/context.flags';
import { blueBright } from 'picocolors';

export default class ContextAdd extends Command {
static description = 'Add a context to the store';
Expand All @@ -24,24 +25,16 @@ export default class ContextAdd extends Command {

try {
await addContext(contextName, specFilePath);
this.log(
`Added context "${contextName}".\n\nYou can set it as your current context: asyncapi config context use ${contextName}\nYou can use this context when needed by passing ${contextName} as a parameter: asyncapi validate ${contextName}`
);

this.log(`🎉 Context ${blueBright(contextName)} added successfully!\nYou can set it as your current context:\n ${blueBright('asyncapi')} ${blueBright('config')} ${blueBright('context')} ${blueBright('use')} ${blueBright(contextName)}\nYou can use this context when needed by passing ${blueBright(contextName)} as a parameter:\n ${blueBright('asyncapi')} ${blueBright('validate')} ${blueBright(contextName)}`);
if (setAsCurrent) {
await setCurrentContext(contextName);
this.log(
`The newly added context "${contextName}", is set as your current context!`
);
this.log(`\nThe newly added context, ${blueBright(contextName)}, is set as your current context!`);
}
} catch (e) {
if (
e instanceof (MissingContextFileError || ContextFileWrongFormatError)
) {
this.log(
'You have no context file configured. Run "asyncapi config context init" to initialize it.'
);
return;
this.error(`Unable to add context. You have no context file configured.\nRun ${blueBright('asyncapi config context init')} to initialize it.`);
}
throw e;
}
Expand Down
16 changes: 5 additions & 11 deletions src/commands/config/context/current.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ContextNotFoundError,
} from '../../../core/errors/context-error';
import { helpFlag } from '../../../core/flags/global.flags';
import { blueBright } from 'picocolors';

export default class ContextCurrent extends Command {
static description = 'Shows the current context that is being used';
Expand All @@ -21,27 +22,20 @@ export default class ContextCurrent extends Command {
if (
e instanceof (MissingContextFileError || ContextFileWrongFormatError)
) {
this.log(
'You have no context file configured. Run "asyncapi config context init" to initialize it.'
);
return;
this.error(`Unable to show current context. You have no context file configured.\nRun ${blueBright('asyncapi config context init')} to initialize it.`);
} else if (e instanceof ContextFileEmptyError) {
this.log(`Context file "${CONTEXT_FILE_PATH}" is empty.`);
return;
this.error(`Context file ${blueBright(CONTEXT_FILE_PATH)} is empty.`);
} else if (
e instanceof ContextNotFoundError ||
(fileContent && !fileContent.current)
) {
this.log(
'No context is set as current. Run "asyncapi config context" to see all available options.'
);
return;
this.error(`No context is set as current.\nRun ${blueBright('asyncapi config context')} to see all available options.`);
}
throw e;
}

if (fileContent) {
this.log(`${fileContent.current}: ${fileContent.context}`);
this.log(`${blueBright(fileContent.current)}: ${fileContent.context}`);
}
}
}
13 changes: 4 additions & 9 deletions src/commands/config/context/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ContextFileEmptyError,
} from '../../../core/errors/context-error';
import { helpFlag } from '../../../core/flags/global.flags';
import { blueBright } from 'picocolors';

export default class ContextEdit extends Command {
static description = 'Edit a context in the store';
Expand All @@ -23,20 +24,14 @@ export default class ContextEdit extends Command {

try {
await editContext(contextName, newSpecFilePath);
this.log(
`Edited context "${contextName}".\n\nYou can set it as your current context: asyncapi config context use ${contextName}\nYou can use this context when needed by passing ${contextName} as a parameter: asyncapi validate ${contextName}`
);
this.log(`🎉 Context ${blueBright(contextName)} edited successfully!\nYou can set it as your current context:\n ${blueBright('asyncapi')} ${blueBright('config')} ${blueBright('context')} ${blueBright('use')} ${blueBright(contextName)}\nYou can use this context when needed by passing ${blueBright(contextName)} as a parameter:\n ${blueBright('asyncapi')} ${blueBright('validate')} ${blueBright(contextName)}`);
} catch (e) {
if (
e instanceof (MissingContextFileError || ContextFileWrongFormatError)
) {
this.log(
'You have no context file configured. Run "asyncapi config context init" to initialize it.'
);
return;
this.error(`Unable to edit context. You have no context file configured.\nRun ${blueBright('asyncapi config context init')} to initialize it.`);
} else if (e instanceof ContextFileEmptyError) {
this.log(`Context file "${CONTEXT_FILE_PATH}" is empty.`);
return;
this.error(`Context file ${blueBright(CONTEXT_FILE_PATH)} is empty.`);
}
throw e;
}
Expand Down
10 changes: 5 additions & 5 deletions src/commands/config/context/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { Args } from '@oclif/core';
import Command from '../../../core/base';
import { initContext } from '../../../core/models/Context';
import { helpFlag } from '../../../core/flags/global.flags';

import { blueBright } from 'picocolors';
export default class ContextInit extends Command {
static description = 'Initialize context';
static flags = helpFlag();

static contextFilePathMessage = `Specify directory in which context file should be created:
- current directory : asyncapi config context init . (default)
- root of current repository : asyncapi config context init ./
- user's home directory : asyncapi config context init ~`;
- current directory : ${blueBright('asyncapi config context init .')}(default)
- root of current repository : ${blueBright('asyncapi config context init ./ ')}
- user's home directory : ${blueBright('asyncapi config context init ~`')}`;

static args = {
'context-file-path': Args.string({description: `${ContextInit.contextFilePathMessage}`, required: false})
Expand All @@ -21,6 +21,6 @@ export default class ContextInit extends Command {
const contextFilePath = args['context-file-path'];

const contextWritePath = await initContext(contextFilePath as string);
this.log(`Initialized context ${contextWritePath}`);
this.log(`🎉 Context initialized at ${blueBright(contextWritePath)}`);
}
}
9 changes: 4 additions & 5 deletions src/commands/config/context/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ContextFileWrongFormatError,
} from '../../../core/errors/context-error';
import { helpFlag } from '../../../core/flags/global.flags';
import { blueBright } from 'picocolors';

export default class ContextList extends Command {
static description = 'List all the stored contexts in the store';
Expand All @@ -19,24 +20,22 @@ export default class ContextList extends Command {
const fileContent = await loadContextFile();

if (await isContextFileEmpty(fileContent)) {
this.log(`Context file "${CONTEXT_FILE_PATH}" is empty.`);
this.log(`Context file ${blueBright(CONTEXT_FILE_PATH)} is empty.`);
return;
}

if (fileContent) {
for (const [contextName, filePath] of Object.entries(
fileContent.store
)) {
this.log(`${contextName}: ${filePath}`);
this.log(`${blueBright(contextName)}: ${filePath}`);
}
}
} catch (e) {
if (
e instanceof (MissingContextFileError || ContextFileWrongFormatError)
) {
this.log(
'You have no context file configured. Run "asyncapi config context init" to initialize it.'
);
this.log(`Unable to list contexts. You have no context file configured.\nRun ${blueBright('asyncapi config context init')} to initialize it.\n`);
return;
}
throw e;
Expand Down
11 changes: 4 additions & 7 deletions src/commands/config/context/remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ContextFileEmptyError,
} from '../../../core/errors/context-error';
import { helpFlag } from '../../../core/flags/global.flags';
import { blueBright } from 'picocolors';

export default class ContextRemove extends Command {
static description = 'Delete a context from the store';
Expand All @@ -22,18 +23,14 @@ export default class ContextRemove extends Command {

try {
await removeContext(contextName);
this.log(`${contextName} successfully deleted`);
this.log(`Context ${blueBright(contextName)} removed successfully!\n`);
} catch (e) {
if (
e instanceof (MissingContextFileError || ContextFileWrongFormatError)
) {
this.log(
'You have no context file configured. Run "asyncapi config context init" to initialize it.'
);
return;
this.error(`Unable to remove context. You have no context file configured.\nRun ${blueBright('asyncapi config context init')} to initialize it.`);
} else if (e instanceof ContextFileEmptyError) {
this.log(`Context file "${CONTEXT_FILE_PATH}" is empty.`);
return;
this.error(`Context file ${blueBright(CONTEXT_FILE_PATH)} is empty.`);
}
throw e;
}
Expand Down
10 changes: 4 additions & 6 deletions src/commands/config/context/use.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ContextFileEmptyError,
} from '../../../core/errors/context-error';
import { helpFlag } from '../../../core/flags/global.flags';
import { blueBright } from 'picocolors';

export default class ContextUse extends Command {
static description = 'Set a context as current';
Expand All @@ -22,17 +23,14 @@ export default class ContextUse extends Command {

try {
await setCurrentContext(contextName);
this.log(`${contextName} is set as current`);
this.log(`Context ${blueBright(contextName)} is now set as current.`);
} catch (e) {
if (
e instanceof (MissingContextFileError || ContextFileWrongFormatError)
) {
this.log(
'You have no context file configured. Run "asyncapi config context init" to initialize it.'
);
return;
this.error(`Unable to set the current context. You have no context file configured.\nRun ${blueBright('asyncapi config context init')} to initialize it.`);
} else if (e instanceof ContextFileEmptyError) {
this.log(`Context file "${CONTEXT_FILE_PATH}" is empty.`);
this.error(`Context file ${blueBright(CONTEXT_FILE_PATH)} is empty.`);
return;
}
throw e;
Expand Down
5 changes: 3 additions & 2 deletions src/commands/config/versions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Command from '../../core/base';
import { helpFlag } from '../../core/flags/global.flags';
import { blueBright, gray } from 'picocolors';

export default class Versions extends Command {
static description = 'Show versions of AsyncAPI tools used';
Expand Down Expand Up @@ -30,7 +31,7 @@ export default class Versions extends Command {
}

// Showing information available with `--version` flag.
this.log(this.config.userAgent);
this.log(gray(`\n${this.config.userAgent}\n`));

// Iteration through the array containing all dependencies '@asyncapi/*'
// along with their versions.
Expand All @@ -49,6 +50,6 @@ export default class Versions extends Command {
}
}

this.log(`Repository: ${this.config.pjson.homepage}`);
this.log(`Repository: ${blueBright(this.config.pjson.homepage)}`);
}
}
2 changes: 1 addition & 1 deletion test/integration/config/analytics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('config:analytics', () => {
.stdout()
.command(['config:analytics'])
.it('should show informational message when no flags are used', (ctx, done) => {
expect(ctx.stdout).to.equal('\nPlease append the "--disable" flag to the command in case you prefer to disable analytics, or use the "--enable" flag if you want to enable analytics back again. In case you do not know the analytics current status, then you can append the "--status" flag to be aware of it.\n\n');
expect(ctx.stdout).to.equal('\nPlease append the --disable flag to the command if you prefer to disable analytics, or use the --enable flag if you want to enable analytics again. To check the current analytics status, use the --status flag.\n\n');
expect(ctx.stderr).to.equal('');
done();
});
Expand Down
18 changes: 9 additions & 9 deletions test/integration/context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe('config:context, positive scenario', () => {
.command(['config:context:add', 'test', './test/integration/specification.yml'])
.it('should add new context called "test"', (ctx, done) => {
expect(ctx.stdout).to.equals(
'Added context "test".\n\nYou can set it as your current context: asyncapi config context use test\nYou can use this context when needed by passing test as a parameter: asyncapi validate test\n'
'🎉 Context test added successfully!\nYou can set it as your current context:\n asyncapi config context use test\nYou can use this context when needed by passing test as a parameter:\n asyncapi validate test\n'
);
expect(ctx.stderr).to.equals('');
done();
Expand Down Expand Up @@ -86,7 +86,7 @@ describe('config:context, positive scenario', () => {
.stdout()
.command(['config:context:edit', 'test', './test/specification2.yml'])
.it('should edit existing context "test"', (ctx, done) => {
expect(ctx.stdout).to.contain('Edited context "test".');
expect(ctx.stdout).to.contain('🎉 Context test edited successfully!');
expect(ctx.stderr).to.equals('');
done();
});
Expand All @@ -98,7 +98,7 @@ describe('config:context, positive scenario', () => {
.stdout()
.command(['config:context:use', 'code'])
.it('should update the current context', (ctx, done) => {
expect(ctx.stdout).to.equals('code is set as current\n');
expect(ctx.stdout).to.equals('Context code is now set as current.\n');
expect(ctx.stderr).to.equals('');
done();
});
Expand All @@ -114,7 +114,7 @@ describe('config:context, positive scenario', () => {
.stdout()
.command(['config:context:remove', 'code'])
.it('should remove existing context', (ctx, done) => {
expect(ctx.stdout).to.equals('code successfully deleted\n');
expect(ctx.stdout).to.equals('Context code removed successfully!\n\n');
expect(ctx.stderr).to.equals('');
done();
});
Expand All @@ -126,7 +126,7 @@ describe('config:context, positive scenario', () => {
.stdout()
.command(['config:context:init'])
.it('should initialize new empty context file without a switch', (ctx, done) => {
expect(ctx.stdout).to.contain('Initialized context');
expect(ctx.stdout).to.contain('🎉 Context initialized at');
expect(ctx.stderr).to.equals('');
done();
});
Expand All @@ -138,7 +138,7 @@ describe('config:context, positive scenario', () => {
.stdout()
.command(['config:context:init', '.'])
.it('should initialize new empty context file with switch "."', (ctx, done) => {
expect(ctx.stdout).to.contain('Initialized context');
expect(ctx.stdout).to.contain('🎉 Context initialized at');
expect(ctx.stderr).to.equals('');
done();
});
Expand All @@ -150,7 +150,7 @@ describe('config:context, positive scenario', () => {
.stdout()
.command(['config:context:init', './'])
.it('should initialize new empty context file with switch "./"', (ctx, done) => {
expect(ctx.stdout).to.contain('Initialized context');
expect(ctx.stdout).to.contain('🎉 Context initialized at');
expect(ctx.stderr).to.equals('');
done();
});
Expand All @@ -162,7 +162,7 @@ describe('config:context, positive scenario', () => {
.stdout()
.command(['config:context:init', '~'])
.it('should initialize new empty context file with switch "~"', (ctx, done) => {
expect(ctx.stdout).to.contain('Initialized context');
expect(ctx.stdout).to.contain('🎉 Context initialized at');
expect(ctx.stderr).to.equals('');
done();
});
Expand Down Expand Up @@ -279,7 +279,7 @@ describe('config:context, negative scenario', () => {
.it(
'should output info message (to stdout, NOT stderr) about absence of context file.',
(ctx, done) => {
expect(ctx.stdout).to.contain('You have no context file configured.');
expect(ctx.stdout).to.contain('Unable to list contexts. You have no context file configured.');
expect(ctx.stderr).to.equals('');
done();
}
Expand Down
Loading