Skip to content

Commit 69b6b62

Browse files
authored
feat(ls): add validation for empty responses object (#5040)
1 parent fe74bc7 commit 69b6b62

File tree

9 files changed

+123
-9
lines changed

9 files changed

+123
-9
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,9 @@ enum ApilintCodes {
868868
OPENAPI2_REFERENCE_FIELD_$REF_FORMAT_URI = 3240100,
869869
OPENAPI2_REFERENCE_NOT_USED = 3240300,
870870

871+
OPENAPI2_RESPONSES = 3250000,
872+
OPENAPI2_RESPONSES_REQUIRED_FIELDS,
873+
871874
OPENAPI3_0 = 5000000,
872875

873876
OPENAPI3_0_OPENAPI_VALUE_PATTERN_3_0_0 = 5000100,
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import allowedFields2_0Lint from './allowed-fields-2-0.ts';
22
import allowedFields3_0__3_1Lint from './allowed-fields-3-0--3-1.ts';
33
import valuesTypeLint from './values--type.ts';
4+
import requiredFieldsLint from './required-fields.ts';
45

5-
const lints = [valuesTypeLint, allowedFields2_0Lint, allowedFields3_0__3_1Lint];
6+
const lints = [valuesTypeLint, allowedFields2_0Lint, allowedFields3_0__3_1Lint, requiredFieldsLint];
67

78
export default lints;
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, OpenAPI3 } from '../../target-specs.ts';
6+
7+
const requiredFieldsLint: LinterMeta = {
8+
code: ApilintCodes.OPENAPI2_RESPONSES_REQUIRED_FIELDS,
9+
source: 'apilint',
10+
message: 'Responses Object should define at least one response',
11+
severity: DiagnosticSeverity.Error,
12+
linterFunction: 'apilintOpenAPIEmptyResponses',
13+
marker: 'key',
14+
targetSpecs: [...OpenAPI2, ...OpenAPI3],
15+
};
16+
17+
export default requiredFieldsLint;

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,4 +1479,17 @@ export const standardLinterfunctions: FunctionItem[] = [
14791479
: true;
14801480
},
14811481
},
1482+
{
1483+
functionName: 'apilintOpenAPIEmptyResponses',
1484+
function: (element: Element): boolean => {
1485+
if (element && isObject(element)) {
1486+
if (!element.keys() || element.keys().length === 0) {
1487+
return false;
1488+
}
1489+
1490+
return element.keys().some((k) => typeof k !== 'string' || !k.startsWith('x-'));
1491+
}
1492+
return true;
1493+
},
1494+
},
14821495
];

packages/apidom-ls/test/fixtures/validation/oas/parameter-unique-name-2-0.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ paths:
66
/pets:
77
get:
88
responses:
9+
'200':
10+
description: ok
911
parameters:
1012
- name: pathLevel
1113
type: string

packages/apidom-ls/test/fixtures/validation/oas/parameter-unique-name-3-0.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ paths:
66
/pets:
77
get:
88
responses:
9+
'200':
10+
description: ok
911
parameters:
1012
- name: pathLevel
1113
in: query
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
swagger: '2.0'
2+
info:
3+
title: Test
4+
version: 1.0.0
5+
paths:
6+
/:
7+
get:
8+
responses:
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
openapi: 3.0.0
2+
info:
3+
title: Test
4+
version: 1.0.0
5+
paths:
6+
/:
7+
get:
8+
responses:

packages/apidom-ls/test/validate.ts

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4205,11 +4205,11 @@ describe('apidom-ls-validate', function () {
42054205
range: {
42064206
end: {
42074207
character: 14,
4208-
line: 9,
4208+
line: 11,
42094209
},
42104210
start: {
42114211
character: 10,
4212-
line: 9,
4212+
line: 11,
42134213
},
42144214
},
42154215
severity: 1,
@@ -4222,11 +4222,11 @@ describe('apidom-ls-validate', function () {
42224222
range: {
42234223
end: {
42244224
character: 14,
4225-
line: 14,
4225+
line: 16,
42264226
},
42274227
start: {
42284228
character: 10,
4229-
line: 14,
4229+
line: 16,
42304230
},
42314231
},
42324232
severity: 1,
@@ -4268,11 +4268,11 @@ describe('apidom-ls-validate', function () {
42684268
range: {
42694269
end: {
42704270
character: 14,
4271-
line: 9,
4271+
line: 11,
42724272
},
42734273
start: {
42744274
character: 10,
4275-
line: 9,
4275+
line: 11,
42764276
},
42774277
},
42784278
severity: 1,
@@ -4285,11 +4285,11 @@ describe('apidom-ls-validate', function () {
42854285
range: {
42864286
end: {
42874287
character: 14,
4288-
line: 15,
4288+
line: 17,
42894289
},
42904290
start: {
42914291
character: 10,
4292-
line: 15,
4292+
line: 17,
42934293
},
42944294
},
42954295
severity: 1,
@@ -6099,4 +6099,64 @@ describe('apidom-ls-validate', function () {
60996099

61006100
languageService.terminate();
61016101
});
6102+
6103+
it('oas 2.0 - Responses Object should define at least one response', async function () {
6104+
const spec = fs
6105+
.readFileSync(
6106+
path.join(__dirname, 'fixtures', 'validation', 'oas', 'responses-required-fields-2-0.yaml'),
6107+
)
6108+
.toString();
6109+
const doc: TextDocument = TextDocument.create(
6110+
'foo://bar/responses-required-fields-2-0.yaml',
6111+
'yaml',
6112+
0,
6113+
spec,
6114+
);
6115+
6116+
const languageService: LanguageService = getLanguageService(contextNoSchema);
6117+
6118+
const result = await languageService.doValidation(doc);
6119+
const expected: Diagnostic[] = [
6120+
{
6121+
message: 'Responses Object should define at least one response',
6122+
severity: 1,
6123+
code: 3250001,
6124+
source: 'apilint',
6125+
range: { start: { line: 7, character: 6 }, end: { line: 7, character: 15 } },
6126+
},
6127+
];
6128+
assert.deepEqual(result, expected);
6129+
6130+
languageService.terminate();
6131+
});
6132+
6133+
it('oas 3.x - Responses Object should define at least one response', async function () {
6134+
const spec = fs
6135+
.readFileSync(
6136+
path.join(__dirname, 'fixtures', 'validation', 'oas', 'responses-required-fields-3-0.yaml'),
6137+
)
6138+
.toString();
6139+
const doc: TextDocument = TextDocument.create(
6140+
'foo://bar/responses-required-fields-3-0.yaml',
6141+
'yaml',
6142+
0,
6143+
spec,
6144+
);
6145+
6146+
const languageService: LanguageService = getLanguageService(contextNoSchema);
6147+
6148+
const result = await languageService.doValidation(doc);
6149+
const expected: Diagnostic[] = [
6150+
{
6151+
message: 'Responses Object should define at least one response',
6152+
severity: 1,
6153+
code: 3250001,
6154+
source: 'apilint',
6155+
range: { start: { line: 7, character: 6 }, end: { line: 7, character: 15 } },
6156+
},
6157+
];
6158+
assert.deepEqual(result, expected);
6159+
6160+
languageService.terminate();
6161+
});
61026162
});

0 commit comments

Comments
 (0)