Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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 @@ -667,6 +667,7 @@ enum ApilintCodes {
OPENAPI2_PATH_TEMPLATE = 3040000,
OPENAPI2_PATH_TEMPLATE_VALUE_WELL_FORMED = 3040100,
OPENAPI2_PATH_TEMPLATE_VALUE_VALID,
OPENAPI2_PATH_TEMPLATE_VALUE_EQUIVALENT_NOT_ALLOWED,

OPENAPI2_LICENSE = 3050000,
OPENAPI2_LICENSE_FIELD_NAME_TYPE = 3050100,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import valueWellFormedLint from './value--well-formed.ts';
import valueValidLint from './value--valid.ts';
import valueEquivalentNotAllowedLint from './value--equivalent-not-allowed.ts';

const lints = [valueWellFormedLint, valueValidLint];
const lints = [valueWellFormedLint, valueValidLint, valueEquivalentNotAllowedLint];

export default lints;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { DiagnosticSeverity } from 'vscode-languageserver-types';

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

const valueEquivalentNotAllowedLint: LinterMeta = {
code: ApilintCodes.OPENAPI2_PATH_TEMPLATE_VALUE_EQUIVALENT_NOT_ALLOWED,
source: 'apilint',
message: 'Equivalent paths are not allowed',
severity: DiagnosticSeverity.Error,
linterFunction: 'apilintOpenAPIPathTemplateNoEquivalent',
marker: 'value',
targetSpecs: OpenAPI2,
};

export default valueEquivalentNotAllowedLint;
18 changes: 18 additions & 0 deletions packages/apidom-ls/src/services/validation/linter-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
test as testPathTemplate,
resolve as resolvePathTemplate,
parse as parsePathTemplate,
isIdentical,
} from 'openapi-path-templating';

// eslint-disable-next-line import/no-cycle
Expand Down Expand Up @@ -1133,4 +1134,21 @@ export const standardLinterfunctions: FunctionItem[] = [
return true;
},
},
{
functionName: 'apilintOpenAPIPathTemplateNoEquivalent',
function: (element: Element): boolean => {
const isFirstOccurrence = (currentKey: string, allKeys: unknown[]) => {
const firstIndex = allKeys.findIndex(
(e) => typeof e === 'string' && isIdentical(e, currentKey),
);

return allKeys[firstIndex] === currentKey;
};
const paths = element.parent?.parent;

return isStringElement(element) && isObject(paths)
? isFirstOccurrence(element.toValue(), paths.keys())
: true;
},
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
swagger: '2.0'
info:
title: Test API
version: 1.0.0
paths:
/items/{id}:
get:
summary: Get item by ID
parameters:
- name: id
in: path
required: true
type: string
responses:
200:
description: OK
/items/{itemId}:
get:
summary: Get item by itemId
parameters:
- name: itemId
in: path
required: true
type: string
responses:
200:
description: OK

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 2.0 should not allow equivalent paths', async function () {
const spec = fs
.readFileSync(
path.join(
__dirname,
'fixtures',
'validation',
'oas',
'path-template-equivalent-not-allowed.yaml',
),
)
.toString();
const doc: TextDocument = TextDocument.create(
'path-template-equivalent-not-allowed.yaml',
'yaml',
0,
spec,
);

const languageService: LanguageService = getLanguageService(contextNoSchema);

const result = await languageService.doValidation(doc);
const expected: Diagnostic[] = [
{
message: 'Equivalent paths are not allowed',
severity: 1,
code: 3040102,
source: 'apilint',
range: { start: { line: 16, character: 2 }, end: { line: 16, character: 17 } },
},
];
assert.deepEqual(result, expected);

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