Skip to content

Commit

Permalink
fix(cli): do not return duplicate results (#681)
Browse files Browse the repository at this point in the history
  • Loading branch information
P0lip authored Nov 10, 2019
1 parent 6983e65 commit 35e85b4
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 4 deletions.
4 changes: 2 additions & 2 deletions src/cli/services/linter/linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { isRuleEnabled } from '../../../runner';
import { IRuleResult, Spectral } from '../../../spectral';
import { FormatLookup, IParsedResult } from '../../../types';
import { ILintConfig } from '../../../types/config';
import { getRuleset, listFiles, skipRules } from './utils';
import { deduplicateResults, getRuleset, listFiles, skipRules } from './utils';

const KNOWN_FORMATS: Array<[string, FormatLookup, string]> = [
['oas2', isOpenApiv2, 'OpenAPI 2.0 (Swagger) detected'],
Expand Down Expand Up @@ -91,5 +91,5 @@ export async function lint(documents: string[], flags: ILintConfig) {
);
}

return results;
return deduplicateResults(results);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
[
{
"code": "valid-schema-example-in-content",
"message": "\"schema.example\" property type should be integer",
"path": [
"paths",
"/resource",
"get",
"responses",
"200",
"content",
"application/json",
"schema",
"example"
],
"severity": 0,
"source": "/home/Spectral/test-harness/scenarios/documents/invalid-schema-example/lib.yaml",
"range": {
"start": {
"line": 20,
"character": 15
},
"end": {
"line": 20,
"character": 19
}
}
},
{
"code": "valid-schema-example-in-content",
"message": "\"schema.example\" property type should be integer",
"path": [
"paths",
"/resource",
"get",
"responses",
"200",
"content",
"application/json",
"schema",
"example"
],
"severity": 0,
"source": "/home/Spectral/test-harness/scenarios/documents/invalid-schema-example/lib.yaml",
"range": {
"start": {
"line": 20,
"character": 15
},
"end": {
"line": 20,
"character": 19
}
}
},
{
"code": "valid-schema-example-in-content",
"message": "\"schema.example\" property type should be integer",
"path": [
"paths",
"/resource",
"get",
"responses",
"200",
"content",
"application/json",
"schema",
"example"
],
"severity": 0,
"source": "/home/Spectral/test-harness/scenarios/documents/invalid-schema-example/lib.yaml",
"range": {
"start": {
"line": 20,
"character": 15
},
"end": {
"line": 20,
"character": 19
}
}
},
{
"code": "valid-example-in-schemas",
"message": "\"Test.example\" property type should be integer",
"path": [
"components",
"schemas",
"Test",
"example"
],
"severity": 0,
"source": "/home/Spectral/test-harness/scenarios/documents/invalid-schema-example/lib.yaml",
"range": {
"start": {
"line": 20,
"character": 15
},
"end": {
"line": 20,
"character": 19
}
}
}
]
32 changes: 32 additions & 0 deletions src/cli/services/linter/utils/__tests__/deduplicateResults.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { deduplicateResults } from '../deduplicateResults';

import * as duplicateValidationResults from './__fixtures__/duplicate-validation-results.json';

describe('deduplicateResults util', () => {
it('deduplicate exact validation results', () => {
expect(deduplicateResults(duplicateValidationResults)).toEqual([
expect.objectContaining({
code: 'valid-schema-example-in-content',
}),
expect.objectContaining({
code: 'valid-example-in-schemas',
}),
]);
});

it('deduplicate exact validation results with unknown source', () => {
const duplicateValidationResultsWithNoSource = duplicateValidationResults.map(result => ({
...result,
source: void 0,
}));

expect(deduplicateResults(duplicateValidationResultsWithNoSource)).toEqual([
expect.objectContaining({
code: 'valid-schema-example-in-content',
}),
expect.objectContaining({
code: 'valid-example-in-schemas',
}),
]);
});
});
26 changes: 26 additions & 0 deletions src/cli/services/linter/utils/deduplicateResults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { IRange } from '@stoplight/types/dist';
import { IRuleResult } from '../../../../types';

type Dictionary<T, K extends PropertyKey> = { [key in K]: T };

const ARTIFICIAL_ROOT = Symbol('root');

const serializeRange = ({ start, end }: IRange) => `${start.line}:${start.character}:${end.line}:${end.character}`;
const getIdentifier = (result: IRuleResult) => `${result.path.join('/')}${result.code}${serializeRange(result.range)}`;

export const deduplicateResults = (results: IRuleResult[]) => {
const seen: Dictionary<Dictionary<string, string>, symbol> = {};

return results.filter(result => {
const source = result.source === void 0 ? ARTIFICIAL_ROOT : result.source;
const identifier = getIdentifier(result);
if (!(source in seen)) {
seen[source] = {};
} else if (identifier in seen[source]) {
return false;
}

seen[source][identifier] = true;
return true;
});
};
1 change: 1 addition & 0 deletions src/cli/services/linter/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './deduplicateResults';
export * from './getRuleset';
export * from './listFiles';
export * from './skipRules';
4 changes: 2 additions & 2 deletions src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const runRules = (
rules: RunRuleCollection,
functions: FunctionCollection,
): IRuleResult[] => {
let results: IRuleResult[] = [];
const results: IRuleResult[] = [];

for (const name in rules) {
if (!rules.hasOwnProperty(name)) continue;
Expand All @@ -34,7 +34,7 @@ export const runRules = (
}

try {
results = results.concat(runRule(resolved, rule, functions));
results.push(...runRule(resolved, rule, functions));
} catch (e) {
console.error(`Unable to run rule '${name}':\n${e}`);
}
Expand Down

0 comments on commit 35e85b4

Please sign in to comment.