Skip to content

Commit

Permalink
feat: readme documentation included
Browse files Browse the repository at this point in the history
  • Loading branch information
PagoNxt-Trade committed Nov 25, 2022
1 parent 1e85328 commit 1f5b99e
Show file tree
Hide file tree
Showing 25 changed files with 233 additions and 109 deletions.
10 changes: 6 additions & 4 deletions docs/guides/2-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Other options include:
--stdin-filepath path to a file to pretend that stdin comes from [string]
--resolver path to custom json-ref-resolver instance [string]
-r, --ruleset path/URL to a ruleset file [string]
-s, --scoring-matrix path/URL to a scoring matrix config file [string]
-s, --scoring-config path/URL to a scoring config file [string]
-F, --fail-severity results of this level or above will trigger a failure exit code
[string] [choices: "error", "warn", "info", "hint"] [default: "error"]
-D, --display-only-failures only output results equal to or greater than --fail-severity [boolean] [default: false]
Expand Down Expand Up @@ -72,12 +72,12 @@ The scoring is produced in two different metrics:

Also it introduces a quality gate, were an API scoring below the specific threshold will fail in a pipeline.

Enabling scoring is done using a new parameter called --scoring-matrix or -s and the scoring matrix configuration file, where you can define how an error or a warning affects to the scoring
Enabling scoring is done using a new parameter called --scoring-config or -s and the scoring configuration file, where you can define how an error or a warning affects to the scoring

Usage:

```bash
spectral lint ./reference/**/*.oas*.{json,yml,yaml} --ruleset mycustomruleset.js --scoring-matrix ./scoringFile.json
spectral lint ./reference/**/*.oas*.{json,yml,yaml} --ruleset mycustomruleset.js --scoring-config ./scoringFile.json
```
or
```bash
Expand Down Expand Up @@ -123,7 +123,7 @@ Usage:
```

Where:
- scoringSubtract : An object with an array for every result level we want to subtract percentage, with the percentage to subtract from number of rewsults on every result type
- scoringSubtract : An object with a key/value pair objects for every result level we want to subtract percentage, with the percentage to subtract from number of results on every result type
- scoringLetter : An object with key/value pairs with scoring letter and scoring percentage, that the result must be greater , for this letter
- threshold : A number with minimum percentage value to provide valid the file we are checking
- warningsSubtract : A boolean to setup if accumulate the result types to less the scoring percentage or stop counting on most critical result types
Expand All @@ -135,6 +135,8 @@ Where:

1 error, the scoring is 45% and D
2 errors, the scoring is 35% and E
3 errors, the scoring is 25% and E
4 errors, the scoring is 25% and E
and so on

Output:
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/__tests__/lint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,10 @@ describe('lint', () => {
);
});

it('calls lint with document, ruleset and scoring matrix config file', async () => {
it('calls lint with document, ruleset and scoring config file', async () => {
const doc = './__fixtures__/empty-oas2-document.json';
const ruleset = 'custom-ruleset.json';
const configFile = 'scoring-matrix.json';
const configFile = 'scoring-config.json';
await run(`lint -r ${ruleset} -s ${configFile} ${doc}`);
expect(lint).toBeCalledWith([doc], {
encoding: 'utf8',
Expand Down
10 changes: 5 additions & 5 deletions packages/cli/src/commands/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { formatOutput, writeOutput } from '../services/output';
import { FailSeverity, ILintConfig, OutputFormat } from '../services/config';

import { CLIError } from '../errors';
import { getScoringMatrix } from '../formatters/utils/getScoring';
import { getScoringConfig } from '../formatters/utils/getScoring';

const formatOptions = Object.values(OutputFormat);

Expand Down Expand Up @@ -128,9 +128,9 @@ const lintCommand: CommandModule = {
description: 'path/URL to a ruleset file',
type: 'string',
},
'scoring-matrix': {
'scoring-config': {
alias: 's',
description: 'path/URL to a scoring matrix config file',
description: 'path/URL to a scoring config file',
type: 'string',
},
'fail-severity': {
Expand Down Expand Up @@ -174,7 +174,7 @@ const lintCommand: CommandModule = {
failSeverity,
displayOnlyFailures,
ruleset,
scoringMatrix,
scoringConfig,
stdinFilepath,
format,
output,
Expand Down Expand Up @@ -208,7 +208,7 @@ const lintCommand: CommandModule = {
format.map(f => {
const formattedOutput = formatOutput(results, f, {
failSeverity: getDiagnosticSeverity(failSeverity),
scoringMatrix: getScoringMatrix(scoringMatrix),
scoringConfig: getScoringConfig(scoringConfig),
});
return writeOutput(formattedOutput, output?.[f] ?? '<stdout>');
}),
Expand Down
12 changes: 6 additions & 6 deletions packages/cli/src/formatters/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ export const json: Formatter = (results: ISpectralDiagnostic[], options: Formatt
let spectralVersion = '';
let groupedResults;
let scoringText = '';
if (options.scoringMatrix !== void 0) {
if (options.scoringMatrix.customScoring !== undefined) {
spectralVersion = `${options.scoringMatrix.customScoring} ${version as string}`;
if (options.scoringConfig !== void 0) {
if (options.scoringConfig.customScoring !== undefined) {
spectralVersion = `${options.scoringConfig.customScoring} ${version as string}`;
}
groupedResults = groupBySource(uniqueErrors(results));
scoringText = getScoringText(getCountsBySeverity(groupedResults), options.scoringMatrix);
scoringText = getScoringText(getCountsBySeverity(groupedResults), options.scoringConfig);
}
const outputJson = results.map(result => {
return {
Expand All @@ -27,12 +27,12 @@ export const json: Formatter = (results: ISpectralDiagnostic[], options: Formatt
};
});
let objectOutput;
if (options.scoringMatrix !== void 0) {
if (options.scoringConfig !== void 0) {
const scoring = +(scoringText !== null ? scoringText.replace('%', '').split(/[()]+/)[1] : 0);
objectOutput = {
version: spectralVersion,
scoring: scoringText.replace('SCORING:', '').trim(),
passed: scoring >= options.scoringMatrix.threshold,
passed: scoring >= options.scoringConfig.threshold,
results: outputJson,
};
} else {
Expand Down
16 changes: 8 additions & 8 deletions packages/cli/src/formatters/pretty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ function formatRange(range?: IRange): string {
export const pretty: Formatter = (results: ISpectralDiagnostic[], options: FormatterOptions) => {
const cliui = require('cliui');
let output = '\n';
if (options.scoringMatrix !== void 0) {
if (options.scoringMatrix.customScoring !== void 0) {
output += `${options.scoringMatrix.customScoring}${version as string}\n`;
if (options.scoringConfig !== void 0) {
if (options.scoringConfig.customScoring !== void 0) {
output += `${options.scoringConfig.customScoring}${version as string}\n`;
}
}
output += '\n';
Expand All @@ -77,12 +77,12 @@ export const pretty: Formatter = (results: ISpectralDiagnostic[], options: Forma
let scoringColor = '';
let scoringText = null;

if (options.scoringMatrix !== void 0) {
if (options.scoringMatrix.uniqueErrors) {
if (options.scoringConfig !== void 0) {
if (options.scoringConfig.uniqueErrors) {
groupedUniqueResults = { ...groupBySource(uniqueResults) };
}
scoringColor = getColorForSeverity(DiagnosticSeverity.Information);
scoringText = getScoringText(getCountsBySeverity(groupedUniqueResults), options.scoringMatrix);
scoringText = getScoringText(getCountsBySeverity(groupedUniqueResults), options.scoringConfig);
}

const uniqueIssues: IDiagnostic['code'][] = [];
Expand Down Expand Up @@ -114,10 +114,10 @@ export const pretty: Formatter = (results: ISpectralDiagnostic[], options: Forma
output += ui.toString();
output += chalk[summaryColor].bold(`${uniqueIssues.length} Unique Issue(s)\n`);
output += chalk[summaryColor].bold(`\u2716${summaryText !== null ? ` ${summaryText}` : ''}\n`);
if (options.scoringMatrix !== void 0) {
if (options.scoringConfig !== void 0) {
output += chalk[scoringColor].bold(`\u2716${scoringText !== null ? ` ${scoringText}` : ''}\n`);
const scoring = +(scoringText !== null ? scoringText.replace('%', '').split(/[()]+/)[1] : 0);
if (scoring >= options.scoringMatrix.threshold) {
if (scoring >= options.scoringConfig.threshold) {
output += chalk['green'].bold(`\u2716 PASSED!\n`);
} else {
output += chalk['red'].bold(`\u2716 NOT PASSED!\n`);
Expand Down
16 changes: 8 additions & 8 deletions packages/cli/src/formatters/stylish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ function getMessageType(severity: DiagnosticSeverity): string {

export const stylish: Formatter = (results: ISpectralDiagnostic[], options: FormatterOptions) => {
let output = '\n';
if (options.scoringMatrix !== void 0) {
if (options.scoringMatrix.customScoring !== void 0) {
output += `${options.scoringMatrix.customScoring}${version as string}\n`;
if (options.scoringConfig !== void 0) {
if (options.scoringConfig.customScoring !== void 0) {
output += `${options.scoringConfig.customScoring}${version as string}\n`;
}
}
output += '\n';
Expand All @@ -85,12 +85,12 @@ export const stylish: Formatter = (results: ISpectralDiagnostic[], options: Form
let scoringColor = '';
let scoringText = null;

if (options.scoringMatrix !== void 0) {
if (options.scoringMatrix.uniqueErrors) {
if (options.scoringConfig !== void 0) {
if (options.scoringConfig.uniqueErrors) {
groupedUniqueResults = { ...groupBySource(uniqueResults) };
}
scoringColor = getColorForSeverity(DiagnosticSeverity.Information);
scoringText = getScoringText(getCountsBySeverity(groupedUniqueResults), options.scoringMatrix);
scoringText = getScoringText(getCountsBySeverity(groupedUniqueResults), options.scoringConfig);
}

Object.keys(groupedResults).map(path => {
Expand Down Expand Up @@ -124,10 +124,10 @@ export const stylish: Formatter = (results: ISpectralDiagnostic[], options: Form
}

output += chalk[summaryColor].bold(`\u2716 ${summaryText}\n`);
if (options.scoringMatrix !== void 0) {
if (options.scoringConfig !== void 0) {
output += chalk[scoringColor].bold(`\u2716${scoringText !== null ? ` ${scoringText}` : ''}\n`);
const scoring = +(scoringText !== null ? scoringText.replace('%', '').split(/[()]+/)[1] : 0);
if (scoring >= options.scoringMatrix.threshold) {
if (scoring >= options.scoringConfig.threshold) {
output += chalk['green'].bold(`\u2716 PASSED!\n`);
} else {
output += chalk['red'].bold(`\u2716 NOT PASSED!\n`);
Expand Down
9 changes: 6 additions & 3 deletions packages/cli/src/formatters/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import type { HumanReadableDiagnosticSeverity } from '@stoplight/spectral-core';
import type { DiagnosticSeverity } from '@stoplight/types';

export type ScoringTable = {
[key in HumanReadableDiagnosticSeverity]: number[];
[key in HumanReadableDiagnosticSeverity]: ScoringSubtract[];
};
export interface ScoringSubtract {
[key: number]: number;
}
export interface ScoringLevel {
[key: string]: number;
}
export type ScoringMatrix = {
export type ScoringConfig = {
customScoring?: string;
scoringSubtract: ScoringTable[];
scoringLetter: ScoringLevel[];
Expand All @@ -19,7 +22,7 @@ export type ScoringMatrix = {

export type FormatterOptions = {
failSeverity: DiagnosticSeverity;
scoringMatrix?: ScoringMatrix;
scoringConfig?: ScoringConfig;
};

export type Formatter = (results: ISpectralDiagnostic[], options: FormatterOptions) => string;
23 changes: 15 additions & 8 deletions packages/cli/src/formatters/utils/getScoring.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { SEVERITY_MAP } from '@stoplight/spectral-core';
import { DiagnosticSeverity } from '@stoplight/types';
import { ScoringMatrix } from '../types';
import { ScoringConfig } from '../types';
import * as path from '@stoplight/path';
import fs from 'fs';

export const getScoringMatrix = (scoringFile?: string): ScoringMatrix | undefined => {
export const getScoringConfig = (scoringFile?: string): ScoringConfig | undefined => {
if (scoringFile === void 0) {
return undefined;
} else if (!path.isAbsolute(scoringFile)) {
scoringFile = path.join(process.cwd(), scoringFile);
}

const scoringMatrix: ScoringMatrix = JSON.parse(fs.readFileSync(scoringFile, 'utf-8')) as ScoringMatrix;
const scoringConfig: ScoringConfig = JSON.parse(fs.readFileSync(scoringFile, 'utf-8')) as ScoringConfig;

return scoringMatrix;
return scoringConfig;
};

export const getScoringText = (
Expand All @@ -23,15 +23,22 @@ export const getScoringText = (
[DiagnosticSeverity.Information]: number;
[DiagnosticSeverity.Hint]: number;
},
scoringMatrix: ScoringMatrix,
scoringConfig: ScoringConfig,
): string => {
const { scoringSubtract, scoringLetter, warningsSubtract } = scoringMatrix;
const { scoringSubtract, scoringLetter, warningsSubtract } = scoringConfig;
let scoring = 100;
Object.keys(issuesCount).forEach(key => {
const scoringKey = Object.keys(SEVERITY_MAP).filter(mappedKey => SEVERITY_MAP[mappedKey])[0];
const scoringKey = Object.keys(SEVERITY_MAP).filter(mappedKey => SEVERITY_MAP[mappedKey] == key)[0];
if (scoringSubtract[scoringKey] !== void 0) {
if (scoring < 100 && !warningsSubtract) return;
scoring -= (scoringSubtract[scoringKey] as number[])[(issuesCount[key] >= 10 ? 10 : issuesCount[key]) as number];
let subtractValue = 0;
Object.keys(scoringSubtract[scoringKey]).forEach(
subtractKey =>
subtractValue = issuesCount[key] >= subtractKey ?
scoringSubtract[scoringKey][subtractKey] :
subtractValue
);
scoring -= subtractValue;
}
});
let scoringLevel: string = Object.keys(scoringLetter)[Object.keys(scoringLetter).length - 1];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"scoringSubtract":
{
"error":
{
"1":55,
"2":65,
"3":75,
"6":85,
"10":95
},
"warn":
{
"1":3,
"2":7,
"3":10,
"6":15,
"10":18
}
},
"scoringLetter":
{
"A": 75,
"B": 65,
"C": 55,
"D": 45,
"E": 0
},
"threshold": 50,
"warningsSubtract": true,
"uniqueErrors": false
}

This file was deleted.

8 changes: 4 additions & 4 deletions packages/cli/src/services/__tests__/linter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jest.mock('../output');
const validCustomOas3SpecPath = resolve(__dirname, '__fixtures__/openapi-3.0-valid-custom.yaml');
const invalidRulesetPath = resolve(__dirname, '__fixtures__/ruleset-invalid.js');
const validRulesetPath = resolve(__dirname, '__fixtures__/ruleset-valid.js');
const validScoringMatrixRulesetPath = resolve(__dirname, '__fixtures__/scorint-matrix.json');
const validScoringConfigRulesetPath = resolve(__dirname, '__fixtures__/scorint-config.json');
const validOas3SpecPath = resolve(__dirname, './__fixtures__/openapi-3.0-valid.yaml');

async function run(command: string) {
Expand Down Expand Up @@ -369,8 +369,8 @@ describe('Linter service', () => {
});
});

describe('--scoring-matrix ', () => {
describe('when single scoring-matrix option provided', () => {
describe('--scoring-config ', () => {
describe('when single scoring-config option provided', () => {
it('outputs normal output if it does not exist', () => {
return expect(
run(`lint ${validCustomOas3SpecPath} -r ${validRulesetPath} -s non-existent-path`),
Expand All @@ -379,7 +379,7 @@ describe('Linter service', () => {

it('outputs no issues', () => {
return expect(
run(`lint ${validCustomOas3SpecPath} -r ${validRulesetPath} -s ${validScoringMatrixRulesetPath}`),
run(`lint ${validCustomOas3SpecPath} -r ${validRulesetPath} -s ${validScoringConfigRulesetPath}`),
).resolves.toEqual([]);
});
});
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/services/__tests__/output.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jest.mock('fs', () => ({
}));
jest.mock('process');

const scoringMatrix = {
const scoringConfig = {
scoringSubtract: {
error: [0, 55, 65, 75, 75, 75, 85, 85, 85, 85, 95],
warn: [0, 3, 7, 10, 10, 10, 15, 15, 15, 15, 18],
Expand Down Expand Up @@ -60,7 +60,7 @@ describe('Output service', () => {
expect(formatOutput(results, format as OutputFormat, { failSeverity: DiagnosticSeverity.Error })).toEqual(output);
});

it.each(['stylish', 'json', 'pretty'])('calls %s formatter with given result and scoring-matrix', format => {
it.each(['stylish', 'json', 'pretty'])('calls %s formatter with given result and scoring-config', format => {
const results = [
{
code: 'info-contact',
Expand All @@ -84,7 +84,7 @@ describe('Output service', () => {
const output = `value for ${format}`;
(formatters[format] as jest.Mock).mockReturnValueOnce(output);
expect(
formatOutput(results, format as OutputFormat, { failSeverity: DiagnosticSeverity.Error, scoringMatrix }),
formatOutput(results, format as OutputFormat, { failSeverity: DiagnosticSeverity.Error, scoringConfig }),
).toEqual(output);
});
});
Expand Down
Loading

0 comments on commit 1f5b99e

Please sign in to comment.