Skip to content

Commit 66e4118

Browse files
committed
feat(ls): migrate disallowing equivalent paths in OAS2
1 parent 774fd62 commit 66e4118

File tree

6 files changed

+98
-1
lines changed

6 files changed

+98
-1
lines changed

packages/apidom-ls/src/config/codes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ enum ApilintCodes {
667667
OPENAPI2_PATH_TEMPLATE = 3040000,
668668
OPENAPI2_PATH_TEMPLATE_VALUE_WELL_FORMED = 3040100,
669669
OPENAPI2_PATH_TEMPLATE_VALUE_VALID,
670+
OPENAPI2_PATH_TEMPLATE_EQUIVALENT_NOT_ALLOWED,
670671

671672
OPENAPI2_LICENSE = 3050000,
672673
OPENAPI2_LICENSE_FIELD_NAME_TYPE = 3050100,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { DiagnosticSeverity } from 'vscode-languageserver-types';
2+
3+
import ApilintCodes from '../../../codes.ts';
4+
import { LinterMeta } from '../../../../apidom-language-types.ts';
5+
import { OpenAPI2 } from '../../target-specs.ts';
6+
7+
const equivalentPathsNotAllowedLint: LinterMeta = {
8+
code: ApilintCodes.OPENAPI2_PATH_TEMPLATE_EQUIVALENT_NOT_ALLOWED,
9+
source: 'apilint',
10+
message: 'Equivalent paths are not allowed.',
11+
severity: DiagnosticSeverity.Error,
12+
linterFunction: 'apilintNoEquivalentPaths',
13+
marker: 'value',
14+
targetSpecs: [...OpenAPI2],
15+
};
16+
17+
export default equivalentPathsNotAllowedLint;
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import valueWellFormedLint from './value--well-formed.ts';
22
import valueValidLint from './value--valid.ts';
3+
import equivalentPathsNotAllowedLint from './equivalent-paths-not-allowed.ts';
34

4-
const lints = [valueWellFormedLint, valueValidLint];
5+
const lints = [valueWellFormedLint, valueValidLint, equivalentPathsNotAllowedLint];
56

67
export default lints;

packages/apidom-ls/src/services/validation/linter-functions.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,4 +1133,24 @@ export const standardLinterfunctions: FunctionItem[] = [
11331133
return true;
11341134
},
11351135
},
1136+
{
1137+
functionName: 'apilintNoEquivalentPaths',
1138+
function: (element: Element): boolean => {
1139+
const PATH_TEMPLATES_REGEX = /\{[^}]+\}/g;
1140+
const isFirstOccurrence = (currentKey: string, allKeys: unknown[]) => {
1141+
const normalize = (x: string) => x.replace(PATH_TEMPLATES_REGEX, '~~');
1142+
const currentKeyNormalized = normalize(currentKey);
1143+
const firstIndex = allKeys.findIndex(
1144+
(e) => typeof e === 'string' && normalize(e) === currentKeyNormalized,
1145+
);
1146+
1147+
return allKeys[firstIndex] === currentKey;
1148+
};
1149+
const paths = element.parent.parent;
1150+
1151+
return isStringElement(element) && isObject(paths)
1152+
? isFirstOccurrence(element.toValue(), paths.keys())
1153+
: true;
1154+
},
1155+
},
11361156
];
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
swagger: '2.0'
2+
info:
3+
title: Test API
4+
version: 1.0.0
5+
paths:
6+
/items/{id}:
7+
get:
8+
summary: Get item by ID
9+
parameters:
10+
- name: id
11+
in: path
12+
required: true
13+
type: string
14+
responses:
15+
200:
16+
description: OK
17+
/items/{itemId}:
18+
get:
19+
summary: Get item by itemId
20+
parameters:
21+
- name: itemId
22+
in: path
23+
required: true
24+
type: string
25+
responses:
26+
200:
27+
description: OK
28+

packages/apidom-ls/test/validate.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3788,4 +3788,34 @@ describe('apidom-ls-validate', function () {
37883788

37893789
languageService.terminate();
37903790
});
3791+
3792+
it('oas 2.0 should not allow equivalent paths', async function () {
3793+
const spec = fs
3794+
.readFileSync(
3795+
path.join(__dirname, 'fixtures', 'validation', 'oas', 'equivalent-paths-not-allowed.yaml'),
3796+
)
3797+
.toString();
3798+
const doc: TextDocument = TextDocument.create(
3799+
'equivalent-paths-not-allowed.yaml',
3800+
'yaml',
3801+
0,
3802+
spec,
3803+
);
3804+
3805+
const languageService: LanguageService = getLanguageService(contextNoSchema);
3806+
3807+
const result = await languageService.doValidation(doc);
3808+
const expected: Diagnostic[] = [
3809+
{
3810+
message: 'Equivalent paths are not allowed.',
3811+
severity: 1,
3812+
code: 3040102,
3813+
source: 'apilint',
3814+
range: { start: { line: 16, character: 2 }, end: { line: 16, character: 17 } },
3815+
},
3816+
];
3817+
assert.deepEqual(result, expected);
3818+
3819+
languageService.terminate();
3820+
});
37913821
});

0 commit comments

Comments
 (0)