Skip to content

Commit

Permalink
chore: config command UI/UX improvement. (#1643)
Browse files Browse the repository at this point in the history
  • Loading branch information
neoandmatrix authored Jan 24, 2025
1 parent 07514e6 commit 9817950
Show file tree
Hide file tree
Showing 12 changed files with 50 additions and 72 deletions.
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

0 comments on commit 9817950

Please sign in to comment.