Skip to content

Commit ef012f1

Browse files
feat(ls): add validation rules for formData (#4998)
1 parent cba90d5 commit ef012f1

14 files changed

+504
-8
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,8 @@ enum ApilintCodes {
710710
OPENAPI2_OPERATION_FIELD_CONSUMES_TYPE = 3080600,
711711
OPENAPI2_OPERATION_FIELD_PRODUCES_TYPE = 3080700,
712712
OPENAPI2_OPERATION_FIELD_PARAMETERS_TYPE = 3080800,
713+
OPENAPI2_OPERATION_FIELD_PARAMETERS_TYPE_CONSUMES_REQUIRED,
714+
OPENAPI2_OPERATION_FIELD_PARAMETERS_IN_CONSUMES_REQUIRED,
713715
OPENAPI2_OPERATION_FIELD_PARAMETERS_ITEMS_TYPE = 3080900,
714716
OPENAPI2_OPERATION_FIELD_RESPONSES_TYPE = 3081000,
715717
OPENAPI2_OPERATION_FIELD_RESPONSES_REQUIRED,
@@ -730,6 +732,7 @@ enum ApilintCodes {
730732
OPENAPI2_PARAMETER_FIELD_IN_REQUIRED,
731733
OPENAPI2_PARAMETER_FIELD_IN_VALID,
732734
OPENAPI2_PARAMETER_FIELD_IN_MULTIPLE_BODY,
735+
OPENAPI2_PARAMETER_FIELD_IN_OVERLAPS,
733736
OPENAPI2_PARAMETER_FIELD_DESCRIPTION_TYPE = 3100300,
734737
OPENAPI2_PARAMETER_FIELD_REQUIRED_TYPE = 3100400,
735738
OPENAPI2_PARAMETER_FIELD_REQUIRED_REQUIRED,

packages/apidom-ls/src/config/openapi/operation/lint/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import consumesTypeLint from './consumes--type.ts';
1010
import producesTypeLint from './produces--type.ts';
1111
import parametersTypeLint from './parameters--type.ts';
1212
import parametersItemsTypeLint from './parameters--items-type.ts';
13+
import parametersTypeConsumesRequiredLint from './parameters-type-consumes--required.ts';
14+
import parametersInConsumesRequiredLint from './parameters-in-consumes--required.ts';
15+
import parametersInOverlapsLint from './parameters-in--overlaps.ts';
1316
import requestBodyTypeLint from './request-body--type.ts';
1417
import responsesTypeLint from './responses--type.ts';
1518
import responsesRequired2_0__3_0Lint from './responses-2-0--3-0--required.ts';
@@ -34,6 +37,9 @@ const lints = [
3437
producesTypeLint,
3538
parametersTypeLint,
3639
parametersItemsTypeLint,
40+
parametersTypeConsumesRequiredLint,
41+
parametersInConsumesRequiredLint,
42+
parametersInOverlapsLint,
3743
requestBodyTypeLint,
3844
responsesTypeLint,
3945
responsesRequired2_0__3_0Lint,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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 inOverlapsLint: LinterMeta = {
8+
code: ApilintCodes.OPENAPI2_PARAMETER_FIELD_IN_OVERLAPS,
9+
source: 'apilint',
10+
message:
11+
'Parameters cannot have both a "in: body" and "in: formData", as "formData" _will_ be the body',
12+
severity: DiagnosticSeverity.Error,
13+
linterFunction: 'apilintParametersInOverlaps',
14+
marker: 'key',
15+
markerTarget: 'parameters',
16+
target: 'parameters',
17+
data: {},
18+
targetSpecs: OpenAPI2,
19+
};
20+
21+
export default inOverlapsLint;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 parametersInConsumesRequiredLint: LinterMeta = {
8+
code: ApilintCodes.OPENAPI2_OPERATION_FIELD_PARAMETERS_IN_CONSUMES_REQUIRED,
9+
source: 'apilint',
10+
message:
11+
'Operations with Parameter of "in: formData" must include "application/x-www-form-urlencoded" or "multipart/form-data" in their "consumes" property',
12+
severity: DiagnosticSeverity.Error,
13+
linterFunction: 'apilintFieldValueOrArray',
14+
linterParams: ['consumes', ['multipart/form-data', 'application/x-www-form-urlencoded']],
15+
marker: 'key',
16+
conditions: [
17+
{
18+
targets: [{ path: 'parameters' }],
19+
function: 'apilintHasParameterKeyValue',
20+
params: ['in', 'formData'],
21+
},
22+
],
23+
data: {},
24+
targetSpecs: OpenAPI2,
25+
};
26+
27+
export default parametersInConsumesRequiredLint;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 parametersTypeConsumesRequiredLint: LinterMeta = {
8+
code: ApilintCodes.OPENAPI2_OPERATION_FIELD_PARAMETERS_TYPE_CONSUMES_REQUIRED,
9+
source: 'apilint',
10+
message:
11+
'Operations with Parameter of "type: file" must include "application/x-www-form-urlencoded" or "multipart/form-data" in their "consumes" property',
12+
severity: DiagnosticSeverity.Error,
13+
linterFunction: 'apilintFieldValueOrArray',
14+
linterParams: ['consumes', ['multipart/form-data', 'application/x-www-form-urlencoded']],
15+
marker: 'key',
16+
conditions: [
17+
{
18+
targets: [{ path: 'parameters' }],
19+
function: 'apilintHasParameterKeyValue',
20+
params: ['type', 'file'],
21+
},
22+
],
23+
data: {},
24+
targetSpecs: OpenAPI2,
25+
};
26+
27+
export default parametersTypeConsumesRequiredLint;

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

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -406,17 +406,61 @@ export const standardLinterfunctions: FunctionItem[] = [
406406
{
407407
functionName: 'apilintFieldValueOrArray',
408408
function: (element: Element, key: string, values: string[]): boolean => {
409+
const api = root(element) as ObjectElement;
410+
409411
if (element && isObject(element)) {
410-
if (element.get(key)) {
411-
const elValue = toValue(element.get(key));
412-
const isArrayVal = Array.isArray(elValue);
413-
if (!isArrayVal && !values.includes(elValue)) {
412+
if (!element.get(key) && !api.get(key)) {
413+
return false;
414+
}
415+
const keyToCheck = element.get(key) ?? api.get(key);
416+
const elValue = toValue(keyToCheck);
417+
const isArrayVal = Array.isArray(elValue);
418+
if (!isArrayVal && !values.includes(elValue)) {
419+
return false;
420+
}
421+
if (isArrayVal && !elValue.every((v) => values.includes(v))) {
422+
return false;
423+
}
424+
}
425+
return true;
426+
},
427+
},
428+
{
429+
functionName: 'apilintHasParameterKeyValue',
430+
function: (element: Element, key: string, value: string): boolean => {
431+
if (element && isArrayElement(element)) {
432+
return (
433+
element.findElements((el: Element) => {
434+
if (isObject(el)) {
435+
return toValue(el.get(key)) === value;
436+
}
414437
return false;
415-
}
416-
if (isArrayVal && !elValue.every((v) => values.includes(v))) {
438+
}, {}).length > 0
439+
);
440+
}
441+
return false;
442+
},
443+
},
444+
{
445+
functionName: 'apilintParametersInOverlaps',
446+
function: (element: Element): boolean => {
447+
if (element && isArrayElement(element)) {
448+
const hasBodyValue =
449+
element.findElements((el: Element) => {
450+
if (isObject(el)) {
451+
return toValue(el.get('in')) === 'body';
452+
}
417453
return false;
418-
}
419-
}
454+
}, {}).length > 0;
455+
const hasFormDataValue =
456+
element.findElements((el: Element) => {
457+
if (isObject(el)) {
458+
return toValue(el.get('in')) === 'formData';
459+
}
460+
return false;
461+
}, {}).length > 0;
462+
463+
return !(hasBodyValue && hasFormDataValue);
420464
}
421465
return true;
422466
},
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
swagger: "2.0"
2+
info:
3+
version: 1.0.0
4+
title: 'test'
5+
paths:
6+
/pets:
7+
get:
8+
responses:
9+
default:
10+
description: string
11+
parameters:
12+
- name: pathLevel
13+
type: string
14+
in: formData
15+
description: tags to filter by
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
swagger: "2.0"
2+
info:
3+
version: 1.0.0
4+
title: 'test'
5+
paths:
6+
/pets:
7+
get:
8+
consumes:
9+
- application/file
10+
responses:
11+
default:
12+
description: string
13+
parameters:
14+
- name: pathLevel
15+
type: string
16+
in: formData
17+
description: tags to filter by
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
swagger: "2.0"
2+
info:
3+
version: 1.0.0
4+
title: 'test'
5+
consumes:
6+
- application/x-www-form-urlencoded
7+
paths:
8+
/pets:
9+
get:
10+
responses:
11+
default:
12+
description: string
13+
parameters:
14+
- name: pathLevel
15+
type: file
16+
in: formData
17+
description: tags to filter by
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
swagger: "2.0"
2+
info:
3+
version: 1.0.0
4+
title: 'test'
5+
consumes:
6+
- application/json
7+
paths:
8+
/pets:
9+
get:
10+
consumes:
11+
- application/x-www-form-urlencoded
12+
responses:
13+
default:
14+
description: string
15+
parameters:
16+
- name: pathLevel
17+
type: file
18+
in: formData
19+
description: tags to filter by

0 commit comments

Comments
 (0)