Skip to content

Commit

Permalink
feat(apidom-ls): lint parameter defined in path template
Browse files Browse the repository at this point in the history
  • Loading branch information
kowalczyk-krzysztof committed Dec 22, 2023
1 parent 2a83fd6 commit 7710ef2
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 0 deletions.
3 changes: 3 additions & 0 deletions packages/apidom-ls/src/config/codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,9 @@ enum ApilintCodes {
OPENAPI2_OPERATION_FIELD_SECURITY_TYPE = 3081300,
OPENAPI2_OPERATION_FIELD_SECURITY_ITEMS_TYPE,

OPENAPI2_PARAMETER = 3090000,
OPENAPI2_PARAMETER_FIELD_IS_DEFINED_WITHIN_PATH_TEMPLATE = 3090100,

OPENAPI3_0 = 5000000,

OPENAPI3_0_OPENAPI_VALUE_PATTERN_3_0_0 = 5000100,
Expand Down
2 changes: 2 additions & 0 deletions packages/apidom-ls/src/config/openapi/parameter/lint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import examplesMutuallyExclusiveLint from './examples--mutually-exclusive';
import contentValuesTypeLint from './content--values-type';
import contentAllowedFields3_0Lint from './content--allowed-fields-3-0';
import contentAllowedFields3_1Lint from './content--allowed-fields-3-1';
import isDefinedWithinPathTemplate from './is-defined-within-path-template';

const lints = [
nameTypeLint,
Expand All @@ -46,6 +47,7 @@ const lints = [
requiredFieldsLint,
allowedFields3_0Lint,
allowedFields3_1Lint,
isDefinedWithinPathTemplate,
];

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

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

const isDefinedWithinPathTemplate: LinterMeta = {
code: ApilintCodes.OPENAPI2_PARAMETER_FIELD_IS_DEFINED_WITHIN_PATH_TEMPLATE,
source: 'apilint',
message: 'parameter is not defined within path template',
severity: DiagnosticSeverity.Error,
linterFunction: 'apilintOpenAPIParameterFieldIsDefinedWithinPathTemplate',
marker: 'value',
targetSpecs: [...OpenAPI2, ...OpenAPI3],
conditions: [
{
function: 'apilintOpenAPIPathTemplateWellFormed',
params: [true],
},
],
};

export default isDefinedWithinPathTemplate;
26 changes: 26 additions & 0 deletions packages/apidom-ls/src/services/validation/linter-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1069,4 +1069,30 @@ export const standardLinterfunctions: FunctionItem[] = [
return true;
},
},
{
functionName: 'apilintOpenAPIParameterFieldIsDefinedWithinPathTemplate',
function: (element: Element) => {
if (element.element === 'parameter') {
const allowedLocations = ['path', 'query'];
const parameterName = toValue((element as ObjectElement).get('name'));
const parameterLocation = toValue((element as ObjectElement).get('in'));

const isAncestorOfOperationElement = (el: Element): boolean =>
el.parent.parent.parent.element === 'operation';

if (isAncestorOfOperationElement(element)) {
const pathTemplate: string = toValue(
(element.parent.parent.parent.parent.parent.parent as MemberElement).key,
);

return (
pathTemplate.includes(parameterName) && allowedLocations.includes(parameterLocation)
);

// TODO: handle when parameter is not ancestor of operation element
}
}
return true;
},
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
swagger: '2.0'
info:
title: Foo
version: 0.1.0
parameters:
test_id:
name: test_id
in: path
required: true
schema:
type: string
format: uuid
title: Test Id
paths:
/foo/{bar_id}:
delete:
summary: Delete foo bar id
operationId: deleteFooBar
parameters:
- name: foo_id
in: path
required: true
schema:
type: string
format: uuid
title: Foo Id
- name: bar_id
in: path
required: true
schema:
type: string
format: uuid
title: Bar Id
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
openapi: 3.0.0
info:
title: Foo
version: 0.1.0
components:
parameters:
test_id:
name: test_id
in: path
required: true
schema:
type: string
format: uuid
title: Test Id
paths:
/foo/{bar_id}:
delete:
summary: Delete foo bar id
operationId: deleteFooBar
parameters:
- name: foo_id
in: path
required: true
schema:
type: string
format: uuid
title: Foo Id
- name: bar_id
in: path
required: true
schema:
type: string
format: uuid
title: Bar Id
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
openapi: 3.1.0
info:
title: Foo
version: 0.1.0
components:
parameters:
test_id:
name: test_id
in: path
required: true
schema:
type: string
format: uuid
title: Test Id
paths:
/foo/{bar_id}:
delete:
summary: Delete foo bar id
operationId: deleteFooBar
parameters:
- name: foo_id
in: path
required: true
schema:
type: string
format: uuid
title: Foo Id
- name: bar_id
in: path
required: true
schema:
type: string
format: uuid
title: Bar Id
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
126 changes: 126 additions & 0 deletions packages/apidom-ls/test/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3549,4 +3549,130 @@ describe('apidom-ls-validate', function () {

languageService.terminate();
});

it('oas 2.0 / yaml - parameter object should be defined within path template', async function () {
const validationContext: ValidationContext = {
comments: DiagnosticSeverity.Error,
maxNumberOfProblems: 100,
relatedInformation: false,
};

const spec = fs
.readFileSync(
path.join(
__dirname,
'fixtures',
'validation',
'oas',
'parameter-defined-within-path-template-2-0.yaml',
),
)
.toString();
const doc: TextDocument = TextDocument.create(
'foo://bar/parameter-defined-within-path-template-2-0.yaml',
'yaml',
0,
spec,
);

const languageService: LanguageService = getLanguageService(contextNoSchema);

const result = await languageService.doValidation(doc, validationContext);
const expected: Diagnostic[] = [
{
range: { start: { line: 19, character: 10 }, end: { line: 25, character: 25 } },
message: 'parameter is not defined within path template',
severity: 1,
code: 3090100,
source: 'apilint',
},
];
assert.deepEqual(result, expected as Diagnostic[]);

languageService.terminate();
});

it('oas 3.0 / yaml - parameter object should be defined within path template', async function () {
const validationContext: ValidationContext = {
comments: DiagnosticSeverity.Error,
maxNumberOfProblems: 100,
relatedInformation: false,
};

const spec = fs
.readFileSync(
path.join(
__dirname,
'fixtures',
'validation',
'oas',
'parameter-defined-within-path-template-3-0.yaml',
),
)
.toString();
const doc: TextDocument = TextDocument.create(
'foo://bar/parameter-defined-within-path-template-3-0.yaml',
'yaml',
0,
spec,
);

const languageService: LanguageService = getLanguageService(contextNoSchema);

const result = await languageService.doValidation(doc, validationContext);
const expected: Diagnostic[] = [
{
range: { start: { line: 20, character: 10 }, end: { line: 26, character: 25 } },
message: 'parameter is not defined within path template',
severity: 1,
code: 3090100,
source: 'apilint',
},
];
assert.deepEqual(result, expected as Diagnostic[]);

languageService.terminate();
});

it('oas 3.1 / yaml - parameter object should be defined within path template', async function () {
const validationContext: ValidationContext = {
comments: DiagnosticSeverity.Error,
maxNumberOfProblems: 100,
relatedInformation: false,
};

const spec = fs
.readFileSync(
path.join(
__dirname,
'fixtures',
'validation',
'oas',
'parameter-defined-within-path-template-3-1.yaml',
),
)
.toString();
const doc: TextDocument = TextDocument.create(
'foo://bar/parameter-defined-within-path-template-3-1.yaml',
'yaml',
0,
spec,
);

const languageService: LanguageService = getLanguageService(contextNoSchema);

const result = await languageService.doValidation(doc, validationContext);
const expected: Diagnostic[] = [
{
range: { start: { line: 20, character: 10 }, end: { line: 26, character: 25 } },
message: 'parameter is not defined within path template',
severity: 1,
code: 3090100,
source: 'apilint',
},
];
assert.deepEqual(result, expected as Diagnostic[]);

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

0 comments on commit 7710ef2

Please sign in to comment.