Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/apidom-ls/src/config/codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,7 @@ enum ApilintCodes {
OPENAPI2_TAG_FIELD_NAME_REQUIRED,
OPENAPI2_TAG_FIELD_DESCRIPTION_TYPE = 3190200,
OPENAPI2_TAG_FIELD_EXTERNAL_DOCS_TYPE = 3190300,
OPENAPI2_TAG_FIELD_UNIQUE_NAME_VALUE_TYPE = 3190400,

OPENAPI2_XML = 3200000,
OPENAPI2_XML_FIELD_NAME_TYPE = 3200100,
Expand Down
10 changes: 10 additions & 0 deletions packages/apidom-ls/src/config/openapi/tag/documentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ import { OpenAPI2, OpenAPI30, OpenAPI31, OpenAPI3 } from '../target-specs.ts';
const documentation = [
{ target: 'name', docs: '**Required.** The name of the tag.', targetSpecs: OpenAPI2 },
{ target: 'name', docs: '**REQUIRED**. The name of the tag.', targetSpecs: OpenAPI3 },
{
target: 'name',
docs: 'Tag Objects must have unique `name` field values.',
targetSpecs: OpenAPI3,
},
{
target: 'name',
docs: 'Tag Objects must have unique `name` field values.',
targetSpecs: OpenAPI2,
},
{
target: 'description',
docs: 'A short description for the tag. [GFM syntax](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown) can be used for rich text representation.',
Expand Down
2 changes: 2 additions & 0 deletions packages/apidom-ls/src/config/openapi/tag/lint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import nameTypeLint from './name--type.ts';
import nameRequiredLint from './name--required.ts';
import descriptionTypeLint from './description--type.ts';
import externalDocsTypeLint from './external-docs--type.ts';
import nameUniqueLint from './name--unique.ts';

const lints = [
allowedFieldsLint,
nameTypeLint,
nameRequiredLint,
nameUniqueLint,
descriptionTypeLint,
externalDocsTypeLint,
];
Expand Down
21 changes: 21 additions & 0 deletions packages/apidom-ls/src/config/openapi/tag/lint/name--unique.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { DiagnosticSeverity } from 'vscode-languageserver-types';

import ApilintCodes from '../../../codes.ts';
import { LinterMeta } from '../../../../apidom-language-types.ts';
import { OpenAPI2, OpenAPI3 } from '../../target-specs.ts';

const nameUniqueLint: LinterMeta = {
code: ApilintCodes.OPENAPI2_TAG_FIELD_UNIQUE_NAME_VALUE_TYPE,
source: 'apilint',
message: 'Tag Objects must have unique `name` field values.',
severity: DiagnosticSeverity.Error,
linterFunction: 'apilintPropertyUniqueSiblingValue',
linterParams: ['tags', 'name'],
marker: 'value',
target: 'name',
markerTarget: 'name',
data: {},
targetSpecs: [...OpenAPI2, ...OpenAPI3],
};

export default nameUniqueLint;
24 changes: 24 additions & 0 deletions packages/apidom-ls/src/services/validation/linter-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,30 @@ export const standardLinterfunctions: FunctionItem[] = [
return true;
},
},
{
functionName: 'apilintPropertyUniqueSiblingValue',
function: (element, elementOrClasses, key) => {
const value = toValue(element);

const filterSiblingsOAS2 = (
el: Element & { key?: { content?: string }; content: { value?: string } },
) => isString(el) && el.key?.content === key && toValue(el.content.value) === value;

const filterSiblingsOAS3 = (el: Element) =>
isObject(el) && el.hasKey(key) && toValue(el.get(key)) === value;

const elements = filter((el: Element) => {
const classes: string[] = toValue(el.getMetaProperty('classes', []));

return (
(elementOrClasses.includes(el.element) ||
classes.every((v) => elementOrClasses.includes(v))) &&
(filterSiblingsOAS2(el) || filterSiblingsOAS3(el))
);
}, element.parent?.parent?.parent);
return elements.length <= 1;
},
},
{
functionName: 'apilintChannelParameterExist',
function: (element: Element): boolean => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
openapi: 3.0.0
info:
title: test
version: 1.0.12
tags:
- name: pet
description: Everything about your Pets
externalDocs:
description: Find out more
url: http://swagger.io
- name: pet
description: Access to Petstore orders
externalDocs:
description: Find out more about our store
url: http://swagger.io
- name: user
description: Operations about user
paths:
36 changes: 36 additions & 0 deletions packages/apidom-ls/test/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3788,4 +3788,40 @@ describe('apidom-ls-validate', function () {

languageService.terminate();
});

it('oas - tags name should have unique values', async function () {
const spec = fs
.readFileSync(path.join(__dirname, 'fixtures', 'validation', 'oas', 'tags-unique-name.yaml'))
.toString();
const doc: TextDocument = TextDocument.create(
'foo://bar/tags-unique-name.yaml',
'yaml',
0,
spec,
);
const languageService: LanguageService = getLanguageService(contextNoSchema);

const result = await languageService.doValidation(doc);
const expected: Diagnostic[] = [
{
message: 'Tag Objects must have unique `name` field values.',
severity: 1,
code: 3190400,
source: 'apilint',
data: {},
range: { start: { line: 5, character: 10 }, end: { line: 5, character: 13 } },
},
{
message: 'Tag Objects must have unique `name` field values.',
severity: 1,
code: 3190400,
source: 'apilint',
data: {},
range: { start: { line: 10, character: 10 }, end: { line: 10, character: 13 } },
},
];
assert.deepEqual(result, expected);

languageService.terminate();
});
});