Skip to content

Commit

Permalink
feat: implemented logic to output diagnostics log by adding additiona…
Browse files Browse the repository at this point in the history
…l output flag to validate cli command. (#1563)

Co-authored-by: Souvik De <[email protected]>
  • Loading branch information
SahilDahekar and Souvikns authored Nov 13, 2024
1 parent b18c368 commit 3ecd126
Showing 1 changed file with 84 additions and 16 deletions.
100 changes: 84 additions & 16 deletions src/core/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,29 @@ import { red, yellow, green, cyan } from 'chalk';
import type { Diagnostic } from '@asyncapi/parser/cjs';
import type Command from './base';
import type { Specification } from './models/SpecificationFile';
import { promises } from 'fs';
import path from 'path';

type DiagnosticsFormat = 'stylish' | 'json' | 'junit' | 'html' | 'text' | 'teamcity' | 'pretty';

export type SeverityKind = 'error' | 'warn' | 'info' | 'hint';

export { convertToOldAPI };

const { writeFile } = promises;

const formatExtensions: Record<DiagnosticsFormat, string> = {
stylish: '.txt',
json: '.json',
junit: '.xml',
html: '.html',
text: '.txt',
teamcity: '.txt',
pretty: '.txt',
};

const validFormats = ['stylish', 'json', 'junit', 'html', 'text', 'teamcity', 'pretty'];

const parser = new Parser({
__unstable: {
resolver: {
Expand Down Expand Up @@ -56,13 +74,18 @@ export function validationFlags({ logDiagnostics = true }: ValidationFlagsOption
options: ['error', 'warn', 'info', 'hint'] as const,
default: 'error',
})(),
output: Flags.string({
description: 'The output file name. Omitting this flag the result will be printed in the console.',
char: 'o'
})
};
}

export interface ValidateOptions {
'log-diagnostics'?: boolean;
'diagnostics-format'?: `${OutputFormat}`;
'fail-severity'?: SeverityKind;
'output'?: string;
}

export async function validate(command: Command, specFile: Specification, options: ValidateOptions = {}) {
Expand All @@ -76,30 +99,56 @@ export async function parse(command: Command, specFile: Specification, options:
return { document, diagnostics, status };
}

function logDiagnostics(diagnostics: Diagnostic[], command: Command, specFile: Specification, options: ValidateOptions = {}): 'valid' | 'invalid' {
function logDiagnostics(
diagnostics: Diagnostic[],
command: Command,
specFile: Specification,
options: ValidateOptions = {}
): 'valid' | 'invalid' {
const logDiagnostics = options['log-diagnostics'];
const failSeverity = options['fail-severity'] ?? 'error';
const diagnosticsFormat = options['diagnostics-format'] ?? 'stylish';

const sourceString = specFile.toSourceString();
if (diagnostics.length) {
if (hasFailSeverity(diagnostics, failSeverity)) {
if (logDiagnostics) {
command.logToStderr(`\n${sourceString} and/or referenced documents have governance issues.`);
command.logToStderr(formatOutput(diagnostics, diagnosticsFormat, failSeverity));
}
return ValidationStatus.INVALID;
}

if (logDiagnostics) {
command.log(`\n${sourceString} is valid but has (itself and/or referenced documents) governance issues.`);
command.log(formatOutput(diagnostics, diagnosticsFormat, failSeverity));
}
} else if (logDiagnostics) {
const hasIssues = diagnostics.length > 0;
const isFailSeverity = hasIssues && hasFailSeverity(diagnostics, failSeverity);

if (logDiagnostics) {
logGovernanceMessage(command, sourceString, hasIssues, isFailSeverity);
outputDiagnostics(command, diagnostics, diagnosticsFormat, failSeverity, options);
}

return isFailSeverity ? ValidationStatus.INVALID : ValidationStatus.VALID;
}

function logGovernanceMessage(
command: Command,
sourceString: string,
hasIssues: boolean,
isFailSeverity: boolean
) {
if (!hasIssues) {
command.log(`\n${sourceString} is valid! ${sourceString} and referenced documents don't have governance issues.`);
} else if (isFailSeverity) {
command.logToStderr(`\n${sourceString} and/or referenced documents have governance issues.`);
} else {
command.log(`\n${sourceString} is valid but has (itself and/or referenced documents) governance issues.`);
}
}

return ValidationStatus.VALID;
function outputDiagnostics(
command: Command,
diagnostics: Diagnostic[],
diagnosticsFormat: DiagnosticsFormat,
failSeverity: SeverityKind,
options: ValidateOptions
) {
const diagnosticsOutput = formatOutput(diagnostics, diagnosticsFormat, failSeverity);
if (options.output) {
writeValidationDiagnostic(options.output, command, diagnosticsFormat, diagnosticsOutput);
} else {
command.log(diagnosticsOutput);
}
}

export function formatOutput(diagnostics: Diagnostic[], format: `${OutputFormat}`, failSeverity: SeverityKind) {
Expand Down Expand Up @@ -145,3 +194,22 @@ function hasFailSeverity(diagnostics: Diagnostic[], failSeverity: SeverityKind)
const diagnosticSeverity = getDiagnosticSeverity(failSeverity);
return diagnostics.some(diagnostic => diagnostic.severity <= diagnosticSeverity);
}

async function writeValidationDiagnostic(outputPath: string, command: Command, format: DiagnosticsFormat, formatOutput: string) {
if (!validFormats.includes(format)) {
command.logToStderr(`Invalid diagnostics format: "${format}"`);
return;
}

const expectedExtension = formatExtensions[format as keyof typeof formatExtensions];
const actualExtension = path.extname(outputPath);

// Validate file extension against diagnostics format
if (expectedExtension && (actualExtension !== expectedExtension)) {
command.logToStderr(`Invalid file extension for format "${format}". Expected extension: "${expectedExtension}"`);
} else {
await writeFile(path.resolve(process.cwd(), outputPath), formatOutput, {
encoding: 'utf-8',
}).catch(err => console.log(err));
}
}

0 comments on commit 3ecd126

Please sign in to comment.