Skip to content

Commit

Permalink
chore: apply design system to new and convert command (#1398)
Browse files Browse the repository at this point in the history
Co-authored-by: asyncapi-bot <bot+chan@asyncapi.io>%0ACo-authored-by: souvik <souvikde.ns@gmail.com>
  • Loading branch information
Shurtu-gal and Souvikns authored May 3, 2024
1 parent f9a6303 commit 2b75821
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 35 deletions.
10 changes: 7 additions & 3 deletions src/commands/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { load } from '../models/SpecificationFile';
import { SpecificationFileNotFound } from '../errors/specification-file';
import { convert } from '@asyncapi/converter';
import type { ConvertVersion } from '@asyncapi/converter';
import { cyan, green } from 'picocolors';

// @ts-ignore
import specs from '@asyncapi/specs';
Expand Down Expand Up @@ -35,15 +36,16 @@ export default class Convert extends Command {
try {
// LOAD FILE
this.specFile = await load(filePath);
// eslint-disable-next-line sonarjs/no-duplicate-string
this.metricsMetadata.to_version = flags['target-version'];

// CONVERSION
convertedFile = convert(this.specFile.text(), flags['target-version'] as ConvertVersion);
if (convertedFile) {
if (this.specFile.getFilePath()) {
this.log(`File ${this.specFile.getFilePath()} successfully converted!`);
this.log(`🎉 The ${cyan(this.specFile.getFilePath())} file has been successfully converted to version ${green(flags['target-version'])}!!`);
} else if (this.specFile.getFileURL()) {
this.log(`URL ${this.specFile.getFileURL()} successfully converted!`);
this.log(`🎉 The URL ${cyan(this.specFile.getFileURL())} has been successfully converted to version ${green(flags['target-version'])}!!`);
}
}

Expand All @@ -64,9 +66,11 @@ export default class Convert extends Command {
type: 'invalid-file',
filepath: filePath
}));
} else if (this.specFile?.toJson().asyncapi > flags['target-version']) {
this.error(`The ${cyan(filePath)} file cannot be converted to an older version. Downgrading is not supported.`);
} else {
this.error(err as Error);
}
}
}
}
}
7 changes: 4 additions & 3 deletions src/commands/new/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as inquirer from 'inquirer';
import { start as startStudio, DEFAULT_PORT } from '../../models/Studio';
import { resolve } from 'path';
import { load } from '../../models/SpecificationFile';
import { cyan } from 'picocolors';

const { writeFile, readFile } = fPromises;
const DEFAULT_ASYNCAPI_FILE_NAME = 'asyncapi.yaml';
Expand Down Expand Up @@ -158,16 +159,16 @@ export default class NewFile extends Command {
try {
const content = await readFile(fileNameToWriteToDisk, { encoding: 'utf8' });
if (content !== undefined) {
console.log(`File ${fileNameToWriteToDisk} already exists. Ignoring...`);
console.log(`A file named ${fileNameToWriteToDisk} already exists. Please choose a different name.`);
return;
}
} catch (e:any) {
if (e.code === 'EACCES') {
this.error('Permission denied to read the file. You do not have the necessary permissions.');
this.error('Permission has been denied to access the file.');
}
}
await writeFile(fileNameToWriteToDisk, asyncApiFile, { encoding: 'utf8' });
console.log(`Created file ${fileNameToWriteToDisk}...`);
console.log(`The ${cyan(fileNameToWriteToDisk)} has been successfully created.`);
this.specFile = await load(fileNameToWriteToDisk);
this.metricsMetadata.selected_template = selectedTemplate;
}
Expand Down
36 changes: 26 additions & 10 deletions src/commands/new/glee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,32 @@ import { prompt } from 'inquirer';
// eslint-disable-next-line
// @ts-ignore
import Generator from '@asyncapi/generator';
import { cyan, gray } from 'picocolors';

export const successMessage = (projectName: string) =>
`🎉 Your Glee project has been successfully created!
⏩ Next steps: follow the instructions ${cyan('below')} to manage your project:
cd ${projectName}\t\t ${gray('# Navigate to the project directory')}
npm install\t\t ${gray('# Install the project dependencies')}
npm run dev\t\t ${gray('# Start the project in development mode')}
You can also open the project in your favourite editor and start tweaking it.
`;

const errorMessages = {
alreadyExists: (projectName: string) =>
`Unable to create the project because the directory "${cyan(projectName)}" already exists at "${process.cwd()}/${projectName}".
To specify a different name for the new project, please run the command below with a unique project name:
${gray('asyncapi new glee --name ') + gray(projectName) + gray('-1')}`,
};

export default class NewGlee extends Command {
static description = 'Creates a new Glee project';
protected commandName = 'glee';
static readonly successMessage = successMessage;
static readonly errorMessages = errorMessages;

static flags = {
help: Flags.help({ char: 'h' }),
Expand Down Expand Up @@ -93,12 +115,10 @@ export default class NewGlee extends Command {
fs.existsSync(PROJECT_DIRECTORY) &&
fs.readdirSync(PROJECT_DIRECTORY).length > 0
) {
throw new Error(
`Unable to create the project. We tried to use "${projectName}" as the directory of your new project but it already exists (${PROJECT_DIRECTORY}). Please specify a different name for the new project. For example, run the following command instead:\n\n asyncapi new ${this.commandName} -f ${file} --name ${projectName}-1\n`
);
throw new Error(errorMessages.alreadyExists(projectName));
}
} catch (error: any) {
this.error(error.message);
this.log(error.message);
}
}

Expand Down Expand Up @@ -199,9 +219,7 @@ export default class NewGlee extends Command {
} catch (err: any) {
switch (err.code) {
case 'EEXIST':
this.error(
`Unable to create the project. We tried to use "${projectName}" as the directory of your new project but it already exists (${PROJECT_DIRECTORY}). Please specify a different name for the new project. For example, run the following command instead:\n\n asyncapi new ${this.commandName} --name ${projectName}-1\n`
);
this.error(errorMessages.alreadyExists(projectName));
break;
case 'EACCES':
this.error(
Expand Down Expand Up @@ -234,9 +252,7 @@ export default class NewGlee extends Command {
`${PROJECT_DIRECTORY}/README-template.md`,
`${PROJECT_DIRECTORY}/README.md`
);
this.log(
`Your project "${projectName}" has been created successfully!\n\nNext steps:\n\n cd ${projectName}\n npm install\n npm run dev\n\nAlso, you can already open the project in your favorite editor and start tweaking it.`
);
this.log(successMessage(projectName));
} catch (err) {
this.error(
`Unable to create the project. Please check the following message for further info about the error:\n\n${err}`
Expand Down
4 changes: 3 additions & 1 deletion src/models/Studio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import chokidar from 'chokidar';
import open from 'open';
import path from 'path';
import { version as studioVersion } from '@asyncapi/studio/package.json';
import { gray } from 'picocolors';

const { readFile, writeFile } = fPromises;

Expand Down Expand Up @@ -99,7 +100,8 @@ export function start(filePath: string, port: number = DEFAULT_PORT): void {

server.listen(port, () => {
const url = `http://localhost:${port}?liveServer=${port}&studio-version=${studioVersion}`;
console.log(`Studio is running at ${url}`);
console.log(`Studio is now running at ${url}.`);
console.log(`You can open this URL in your web browser, and if needed, press ${gray('Ctrl + C')} to stop the process.`);
console.log(`Watching changes on file ${filePath}`);
open(url);
});
Expand Down
32 changes: 29 additions & 3 deletions src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AvroSchemaParser } from '@asyncapi/avro-schema-parser';
import { OpenAPISchemaParser } from '@asyncapi/openapi-schema-parser';
import { Parser, convertToOldAPI } from '@asyncapi/parser/cjs';
import { DiagnosticSeverity, Parser, convertToOldAPI } from '@asyncapi/parser/cjs';
import { RamlDTSchemaParser } from '@asyncapi/raml-dt-schema-parser';
import { Flags } from '@oclif/core';
import { ProtoBuffSchemaParser } from '@asyncapi/protobuf-schema-parser';
import { getDiagnosticSeverity } from '@stoplight/spectral-core';
import { OutputFormat } from '@stoplight/spectral-cli/dist/services/config';
import { html, json, junit, pretty, stylish, teamcity, text } from '@stoplight/spectral-formatters';
import { red, yellow, green, cyan } from 'chalk';

import type { Diagnostic } from '@asyncapi/parser/cjs';
import type Command from './base';
Expand Down Expand Up @@ -102,9 +103,10 @@ function logDiagnostics(diagnostics: Diagnostic[], command: Command, specFile: S
}

export function formatOutput(diagnostics: Diagnostic[], format: `${OutputFormat}`, failSeverity: SeverityKind) {
const options = { failSeverity: getDiagnosticSeverity(failSeverity) };
const diagnosticSeverity = getDiagnosticSeverity(failSeverity);
const options = { failSeverity: diagnosticSeverity !== -1 ? diagnosticSeverity : DiagnosticSeverity.Error };
switch (format) {
case 'stylish': return stylish(diagnostics, options);
case 'stylish': return formatStylish(diagnostics, options);
case 'json': return json(diagnostics, options);
case 'junit': return junit(diagnostics, options);
case 'html': return html(diagnostics, options);
Expand All @@ -115,6 +117,30 @@ export function formatOutput(diagnostics: Diagnostic[], format: `${OutputFormat}
}
}

function formatStylish(diagnostics: Diagnostic[], options: { failSeverity: DiagnosticSeverity }) {
const groupedDiagnostics = diagnostics.reduce((acc, diagnostic) => {
const severity = diagnostic.severity;
if (!acc[severity as DiagnosticSeverity]) {
acc[severity as DiagnosticSeverity] = [];
}
acc[severity as DiagnosticSeverity].push(diagnostic);
return acc;
}, {} as Record<DiagnosticSeverity, Diagnostic[]>);

return Object.entries(groupedDiagnostics).map(([severity, diagnostics]) => {
return `${getSeverityTitle(Number(severity))} ${stylish(diagnostics, options)}`;
}).join('\n');
}

function getSeverityTitle(severity: DiagnosticSeverity) {
switch (severity) {
case DiagnosticSeverity.Error: return red('Errors');
case DiagnosticSeverity.Warning: return yellow('Warnings');
case DiagnosticSeverity.Information: return cyan('Information');
case DiagnosticSeverity.Hint: return green('Hints');
}
}

function hasFailSeverity(diagnostics: Diagnostic[], failSeverity: SeverityKind) {
const diagnosticSeverity = getDiagnosticSeverity(failSeverity);
return diagnostics.some(diagnostic => diagnostic.severity <= diagnosticSeverity);
Expand Down
10 changes: 5 additions & 5 deletions test/integration/convert.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('convert', () => {
.stdout()
.command(['convert', filePath])
.it('works when file path is passed', (ctx, done) => {
expect(ctx.stdout).to.contain('File ./test/fixtures/specification.yml successfully converted!\n');
expect(ctx.stdout).to.contain('The ./test/fixtures/specification.yml file has been successfully converted to version 3.0.0!!');
expect(ctx.stderr).to.equal('');
done();
});
Expand All @@ -52,7 +52,7 @@ describe('convert', () => {
.stdout()
.command(['convert', 'http://localhost:8080/dummySpec.yml'])
.it('works when url is passed', (ctx, done) => {
expect(ctx.stdout).to.contain('URL http://localhost:8080/dummySpec.yml successfully converted!\n');
expect(ctx.stdout).to.contain('The URL http://localhost:8080/dummySpec.yml has been successfully converted to version 3.0.0!!');
expect(ctx.stderr).to.equal('');
done();
});
Expand All @@ -73,7 +73,7 @@ describe('convert', () => {
.stdout()
.command(['convert'])
.it('converts from current context', (ctx, done) => {
expect(ctx.stdout).to.contain(`File ${path.resolve(__dirname, '../fixtures/specification.yml')} successfully converted!\n`);
expect(ctx.stdout).to.contain(`The ${path.resolve(__dirname, '../fixtures/specification.yml')} file has been successfully converted to version 3.0.0!!\n`);
expect(ctx.stderr).to.equal('');
done();
});
Expand Down Expand Up @@ -159,7 +159,7 @@ describe('convert', () => {
.stdout()
.command(['convert', filePath, '-o=./test/fixtures/specification_output.yml'])
.it('works when .yml file is passed', (ctx, done) => {
expect(ctx.stdout).to.equal(`File ${filePath} successfully converted!\n`);
expect(ctx.stdout).to.contain(`The ${filePath} file has been successfully converted to version 3.0.0!!`);
expect(fs.existsSync('./test/fixtures/specification_output.yml')).to.equal(true);
expect(ctx.stderr).to.equal('');
fs.unlinkSync('./test/fixtures/specification_output.yml');
Expand All @@ -171,7 +171,7 @@ describe('convert', () => {
.stdout()
.command(['convert', JSONFilePath, '-o=./test/fixtures/specification_output.json'])
.it('works when .json file is passed', (ctx, done) => {
expect(ctx.stdout).to.equal(`File ${JSONFilePath} successfully converted!\n`);
expect(ctx.stdout).to.contain(`The ${JSONFilePath} file has been successfully converted to version 3.0.0!!`);
expect(fs.existsSync('./test/fixtures/specification_output.json')).to.equal(true);
expect(ctx.stderr).to.equal('');
fs.unlinkSync('./test/fixtures/specification_output.json');
Expand Down
6 changes: 3 additions & 3 deletions test/integration/new/file.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('new', () => {
.command(['new', '--no-tty', '-n=specification.yaml'])
.it('runs new command', async (ctx,done) => {
expect(ctx.stderr).to.equal('');
expect(ctx.stdout).to.equal('Created file specification.yaml...\n');
expect(ctx.stdout).to.equal('The specification.yaml has been successfully created.\n');
done();
});

Expand All @@ -36,7 +36,7 @@ describe('new', () => {
.command(['new:file', '--no-tty', '-n=specification.yaml'])
.it('runs new file command', async (ctx,done) => {
expect(ctx.stderr).to.equal('');
expect(ctx.stdout).to.equal('Created file specification.yaml...\n');
expect(ctx.stdout).to.equal('The specification.yaml has been successfully created.\n');
done();
});
});
Expand All @@ -62,7 +62,7 @@ describe('new', () => {
.command(['new:file', '--no-tty', '-n=specification.yaml'])
.it('should inform about the existing file and finish the process', async (ctx,done) => {
expect(ctx.stderr).to.equal('');
expect(ctx.stdout).to.equal('File specification.yaml already exists. Ignoring...\n');
expect(ctx.stdout).to.equal('A file named specification.yaml already exists. Please choose a different name.\n');
done();
});
});
Expand Down
11 changes: 9 additions & 2 deletions test/integration/new/glee.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { PROJECT_DIRECTORY_PATH } from '../../helpers';
import { expect } from '@oclif/test';

const testHelper = new TestHelper();
const successMessage = (projectName: string) =>
'🎉 Your Glee project has been successfully created!';

const errorMessages = {
alreadyExists: (projectName: string) =>
`Unable to create the project because the directory "${projectName}" already exists at "${process.cwd()}/${projectName}".
To specify a different name for the new project, please run the command below with a unique project name:`};

describe('new glee', () => {
before(() => {
Expand All @@ -27,7 +34,7 @@ describe('new glee', () => {
.command(['new:glee', '-n=test-project'])
.it('runs new glee command with name flag', async (ctx,done) => {
expect(ctx.stderr).to.equal('');
expect(ctx.stdout).to.equal('Your project "test-project" has been created successfully!\n\nNext steps:\n\n cd test-project\n npm install\n npm run dev\n\nAlso, you can already open the project in your favorite editor and start tweaking it.\n');
expect(ctx.stdout).to.contains(successMessage('test-project'));
done();
});
});
Expand All @@ -52,7 +59,7 @@ describe('new glee', () => {
.stdout()
.command(['new:glee', '-n=test-project'])
.it('should throw error if name of the new project already exists', async (ctx,done) => {
expect(ctx.stderr).to.equal(`Error: Unable to create the project. We tried to use "test-project" as the directory of your new project but it already exists (${PROJECT_DIRECTORY_PATH}). Please specify a different name for the new project. For example, run the following command instead:\n\n asyncapi new glee --name test-project-1\n\n`);
expect(ctx.stderr).to.contains(`Error: ${errorMessages.alreadyExists('test-project')}`);
expect(ctx.stdout).to.equal('');
done();
});
Expand Down
10 changes: 5 additions & 5 deletions test/integration/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('validate', () => {
.stdout()
.command(['validate', './test/fixtures/specification.yml'])
.it('works when file path is passed', (ctx, done) => {
expect(ctx.stdout).to.match(/File .\/test\/fixtures\/specification.yml is valid but has \(itself and\/or referenced documents\) governance issues.\n\ntest\/fixtures\/specification.yml/);
expect(ctx.stdout).to.contain('File ./test/fixtures/specification.yml is valid but has (itself and/or referenced documents) governance issues.\n');
expect(ctx.stderr).to.equal('');
done();
});
Expand All @@ -41,7 +41,7 @@ describe('validate', () => {
.stdout()
.command(['validate', './test/fixtures/specification-avro.yml'])
.it('works when file path is passed and schema is avro', (ctx, done) => {
expect(ctx.stdout).to.match(/File .\/test\/fixtures\/specification-avro.yml is valid but has \(itself and\/or referenced documents\) governance issues.\n/);
expect(ctx.stdout).to.contain('File ./test/fixtures/specification-avro.yml is valid but has (itself and/or referenced documents) governance issues.\n');
expect(ctx.stderr).to.equal('');
done();
});
Expand All @@ -61,7 +61,7 @@ describe('validate', () => {
.stdout()
.command(['validate', 'http://localhost:8080/dummySpec.yml'])
.it('works when url is passed', (ctx, done) => {
expect(ctx.stdout).to.match(/URL http:\/\/localhost:8080\/dummySpec.yml is valid but has \(itself and\/or referenced documents\) governance issues.\n\nhttp:\/\/localhost:8080\/dummySpec.yml/);
expect(ctx.stdout).to.contain('URL http://localhost:8080/dummySpec.yml is valid but has (itself and/or referenced documents) governance issues.\n');
expect(ctx.stderr).to.equal('');
done();
});
Expand Down Expand Up @@ -180,7 +180,7 @@ describe('validate', () => {
.stdout()
.command(['validate', './test/fixtures/specification.yml', '--log-diagnostics'])
.it('works with --log-diagnostics', (ctx, done) => {
expect(ctx.stdout).to.match(/File .\/test\/fixtures\/specification.yml is valid but has \(itself and\/or referenced documents\) governance issues.\n\ntest\/fixtures\/specification.yml/);
expect(ctx.stdout).to.contain('File ./test/fixtures/specification.yml is valid but has (itself and/or referenced documents) governance issues.\n');
expect(ctx.stderr).to.equal('');
done();
});
Expand Down Expand Up @@ -240,7 +240,7 @@ describe('validate', () => {
.stdout()
.command(['validate', './test/fixtures/specification.yml', '--fail-severity=warn'])
.it('works with --fail-severity', (ctx, done) => {
expect(ctx.stderr).to.include('\nFile ./test/fixtures/specification.yml and/or referenced documents have governance issues.\n\ntest/fixtures/specification.yml');
expect(ctx.stderr).to.contain('File ./test/fixtures/specification.yml and/or referenced documents have governance issues.');
expect(process.exitCode).to.equal(1);
done();
});
Expand Down

0 comments on commit 2b75821

Please sign in to comment.