Skip to content

Commit

Permalink
feat(rulesets): propagate review suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
magicmatatjahu committed May 19, 2022
1 parent 09a0dfa commit 7f36799
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ testRule('asyncapi-tags-uniqueness', [
},
errors: [
{
message: 'Tags contains duplicate tag names: one.',
path: ['tags'],
message: '"tags" object contains duplicate tag name "one".',
path: ['tags', '1', 'name'],
severity: DiagnosticSeverity.Error,
},
],
Expand All @@ -43,13 +43,13 @@ testRule('asyncapi-tags-uniqueness', [
},
errors: [
{
message: 'Tags contains duplicate tag names: one.',
path: ['channels', 'someChannel', 'publish', 'tags'],
message: '"tags" object contains duplicate tag name "one".',
path: ['channels', 'someChannel', 'publish', 'tags', '1', 'name'],
severity: DiagnosticSeverity.Error,
},
{
message: 'Tags contains duplicate tag names: one.',
path: ['channels', 'someChannel', 'subscribe', 'tags'],
message: '"tags" object contains duplicate tag name "one".',
path: ['channels', 'someChannel', 'subscribe', 'tags', '1', 'name'],
severity: DiagnosticSeverity.Error,
},
],
Expand Down Expand Up @@ -80,13 +80,13 @@ testRule('asyncapi-tags-uniqueness', [
},
errors: [
{
message: 'Tags contains duplicate tag names: one.',
path: ['channels', 'someChannel', 'publish', 'traits', '0', 'tags'],
message: '"tags" object contains duplicate tag name "one".',
path: ['channels', 'someChannel', 'publish', 'traits', '0', 'tags', '1', 'name'],
severity: DiagnosticSeverity.Error,
},
{
message: 'Tags contains duplicate tag names: one.',
path: ['channels', 'someChannel', 'subscribe', 'traits', '0', 'tags'],
message: '"tags" object contains duplicate tag name "one".',
path: ['channels', 'someChannel', 'subscribe', 'traits', '0', 'tags', '1', 'name'],
severity: DiagnosticSeverity.Error,
},
],
Expand All @@ -106,8 +106,8 @@ testRule('asyncapi-tags-uniqueness', [
},
errors: [
{
message: 'Tags contains duplicate tag names: one.',
path: ['components', 'messages', 'someMessage', 'tags'],
message: '"tags" object contains duplicate tag name "one".',
path: ['components', 'messages', 'someMessage', 'tags', '1', 'name'],
severity: DiagnosticSeverity.Error,
},
],
Expand All @@ -131,8 +131,28 @@ testRule('asyncapi-tags-uniqueness', [
},
errors: [
{
message: 'Tags contains duplicate tag names: one.',
path: ['components', 'messages', 'someMessage', 'traits', '0', 'tags'],
message: '"tags" object contains duplicate tag name "one".',
path: ['components', 'messages', 'someMessage', 'traits', '0', 'tags', '1', 'name'],
severity: DiagnosticSeverity.Error,
},
],
},

{
name: 'tags has duplicated more that two times this same name',
document: {
asyncapi: '2.0.0',
tags: [{ name: 'one' }, { name: 'one' }, { name: 'two' }, { name: 'one' }],
},
errors: [
{
message: '"tags" object contains duplicate tag name "one".',
path: ['tags', '1', 'name'],
severity: DiagnosticSeverity.Error,
},
{
message: '"tags" object contains duplicate tag name "one".',
path: ['tags', '3', 'name'],
severity: DiagnosticSeverity.Error,
},
],
Expand Down

This file was deleted.

4 changes: 2 additions & 2 deletions packages/rulesets/src/asyncapi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import asyncApi2DocumentSchema from './functions/asyncApi2DocumentSchema';
import asyncApi2SchemaValidation from './functions/asyncApi2SchemaValidation';
import asyncApi2PayloadValidation from './functions/asyncApi2PayloadValidation';
import asyncApi2UniquenessTags from './functions/asyncApi2UniquenessTags';
import { uniquenessTags } from '../shared/functions';

export default {
documentationUrl: 'https://meta.stoplight.io/docs/spectral/docs/reference/asyncapi-rules.md',
Expand Down Expand Up @@ -390,7 +390,7 @@ export default {
'$.components.messageTraits.*.tags',
],
then: {
function: asyncApi2UniquenessTags,
function: uniquenessTags,
},
},
'asyncapi-tags': {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { DiagnosticSeverity } from '@stoplight/types';
import testRule from './__helpers__/tester';

testRule('openapi-tags-uniqueness', [
{
name: 'valid case',
document: {
swagger: '2.0',
tags: [{ name: 'one' }, { name: 'two' }],
},
errors: [],
},

{
name: 'tags has duplicated names',
document: {
swagger: '2.0',
tags: [{ name: 'one' }, { name: 'one' }],
},
errors: [
{
message: '"tags" object contains duplicate tag name "one".',
path: ['tags', '1', 'name'],
severity: DiagnosticSeverity.Error,
},
],
},

{
name: 'tags has duplicated more that two times this same name',
document: {
swagger: '2.0',
tags: [{ name: 'one' }, { name: 'one' }, { name: 'two' }, { name: 'one' }],
},
errors: [
{
message: '"tags" object contains duplicate tag name "one".',
path: ['tags', '1', 'name'],
severity: DiagnosticSeverity.Error,
},
{
message: '"tags" object contains duplicate tag name "one".',
path: ['tags', '3', 'name'],
severity: DiagnosticSeverity.Error,
},
],
},
]);
12 changes: 12 additions & 0 deletions packages/rulesets/src/oas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
oasSchema,
oasDiscriminator,
} from './functions';
import { uniquenessTags } from '../shared/functions';

export { ruleset as default };

Expand Down Expand Up @@ -212,6 +213,17 @@ const ruleset = {
},
},
},
'openapi-tags-uniqueness': {
description: 'Each tags must have a unique names.',
message: '{{error}}',
severity: 'error',
recommended: true,
type: 'validation',
given: '$.tags',
then: {
function: uniquenessTags,
},
},
'openapi-tags': {
description: 'OpenAPI object must have non-empty "tags" array.',
recommended: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import asyncApi2UniquenessTags from '../asyncApi2UniquenessTags';
import uniquenessTags from '../uniquenessTags';

function runValidation(targetVal: Array<{ name: string }>) {
return asyncApi2UniquenessTags(targetVal, null, { path: ['tags'], documentInventory: {} } as any);
return uniquenessTags(targetVal, null, { path: ['tags'], documentInventory: {} } as any);
}

describe('asyncApi2UniquenessTags', () => {
describe('uniquenessTags', () => {
test('should skip empty tags', () => {
const results = runValidation([]);
expect(results).toEqual([]);
Expand Down Expand Up @@ -43,8 +43,8 @@ describe('asyncApi2UniquenessTags', () => {
const results = runValidation(tags);
expect(results).toEqual([
{
message: 'Tags contains duplicate tag names: one.',
path: ['tags'],
message: '"tags" object contains duplicate tag name "one".',
path: ['tags', 2, 'name'],
},
]);
});
Expand All @@ -66,13 +66,24 @@ describe('asyncApi2UniquenessTags', () => {
{
name: 'two',
},
{
name: 'two',
},
];

const results = runValidation(tags);
expect(results).toEqual([
{
message: 'Tags contains duplicate tag names: one, two.',
path: ['tags'],
message: '"tags" object contains duplicate tag name "one".',
path: ['tags', 3, 'name'],
},
{
message: '"tags" object contains duplicate tag name "two".',
path: ['tags', 4, 'name'],
},
{
message: '"tags" object contains duplicate tag name "two".',
path: ['tags', 5, 'name'],
},
]);
});
Expand Down
1 change: 1 addition & 0 deletions packages/rulesets/src/shared/functions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as uniquenessTags } from './uniquenessTags';
61 changes: 61 additions & 0 deletions packages/rulesets/src/shared/functions/uniquenessTags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { createRulesetFunction } from '@stoplight/spectral-core';

import type { IFunctionResult } from '@stoplight/spectral-core';

type Tags = Array<{ name: string }>;

function getDuplicateTagNames(tags: Tags): string[] {
const tagNames = tags.map(item => item.name);
return tagNames.reduce((acc, item, idx, arr) => {
if (arr.indexOf(item) !== idx && acc.indexOf(item) < 0) {
acc.push(item);
}
return acc;
}, [] as string[]);
}

export default createRulesetFunction<Tags, null>(
{
input: {
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string',
},
},
},
},
options: null,
},
function uniquenessTags(targetVal, _, ctx) {
const duplicatedTags = getDuplicateTagNames(targetVal);
if (duplicatedTags.length === 0) return [];

const results: IFunctionResult[] = [];

duplicatedTags.map(duplicatedTag => {
let checkedFirst = false;
const duplicatedTags: number[] = [];
targetVal.forEach((tag, index) => {
if (tag.name === duplicatedTag) {
if (!checkedFirst) {
checkedFirst = true;
return;
}
duplicatedTags.push(index);
}
});

results.push(
...duplicatedTags.map(duplicatedIndex => ({
message: `"tags" object contains duplicate tag name "${duplicatedTag}".`,
path: [...ctx.path, duplicatedIndex, 'name'],
})),
);
});

return results;
},
);

0 comments on commit 7f36799

Please sign in to comment.