Skip to content

Commit 0e921ae

Browse files
committed
feat(apidom-ls): create schema rule for missing core keywords
fix #3549
1 parent dbdb2a1 commit 0e921ae

13 files changed

+467
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export const AsyncAPI200 = [{ namespace: 'asyncapi', version: '2.0.0' }];
2+
export const AsyncAPI210 = [{ namespace: 'asyncapi', version: '2.1.0' }];
3+
export const AsyncAPI220 = [{ namespace: 'asyncapi', version: '2.2.0' }];
4+
export const AsyncAPI230 = [{ namespace: 'asyncapi', version: '2.3.0' }];
5+
export const AsyncAPI240 = [{ namespace: 'asyncapi', version: '2.4.0' }];
6+
export const AsyncAPI250 = [{ namespace: 'asyncapi', version: '2.5.0' }];
7+
export const AsyncAPI260 = [{ namespace: 'asyncapi', version: '2.6.0' }];
8+
9+
export const AsyncAPI2 = [
10+
...AsyncAPI200,
11+
...AsyncAPI210,
12+
...AsyncAPI220,
13+
...AsyncAPI230,
14+
...AsyncAPI240,
15+
...AsyncAPI250,
16+
...AsyncAPI260,
17+
];

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

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ enum ApilintCodes {
6666
SCHEMA_EXAMPLE_DEPRECATED,
6767
SCHEMA_TYPE_OPENAPI_3_0,
6868
SCHEMA_NULLABLE_NOT_RECOMMENDED,
69+
SCHEMA_MISSING_CORE_FIELDS,
6970

7071
DUPLICATE_KEYS = 14999,
7172
NOT_ALLOWED_FIELDS = 15000,

packages/apidom-ls/src/config/common/schema/lint/index.ts

+8
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ import minLengthTypeLint from './min-length--type';
3636
import minPropertiesNonObjectLint from './min-properties--non-object';
3737
import minPropertiesTypeLint from './min-properties--type';
3838
import minimumPatternLint from './minimum--pattern';
39+
import missingCoreFieldsOpenAPI2_0Lint from './missing-core-fields-openapi-2-0';
40+
import missingCoreFieldsOpenAPI3_0Lint from './missing-core-fields-openapi-3-0';
41+
import missingCoreFieldsOpenAPI3_1Lint from './missing-core-fields-openapi-3-1';
42+
import missingCoreFieldsAsyncAPI2Lint from './missing-core-fields-asyncapi-2';
3943
import multipleOfTypeLint from './multiple-of--type';
4044
import notTypeLint from './not--type';
4145
import nullableNotRecommendedLint from './nullable--not-recommended';
@@ -102,6 +106,10 @@ const schemaLints = [
102106
minPropertiesNonObjectLint,
103107
minPropertiesTypeLint,
104108
minimumPatternLint,
109+
missingCoreFieldsOpenAPI2_0Lint,
110+
missingCoreFieldsOpenAPI3_0Lint,
111+
missingCoreFieldsOpenAPI3_1Lint,
112+
missingCoreFieldsAsyncAPI2Lint,
105113
multipleOfTypeLint,
106114
notTypeLint,
107115
nullableNotRecommendedLint,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { DiagnosticSeverity } from 'vscode-languageserver-types';
2+
3+
import ApilintCodes from '../../../codes';
4+
import { LinterMeta } from '../../../../apidom-language-types';
5+
import { AsyncAPI2 } from '../../../asyncapi/target-specs';
6+
7+
// eslint-disable-next-line @typescript-eslint/naming-convention
8+
const missingCoreFieldsAsyncAPI2Lint: LinterMeta = {
9+
code: ApilintCodes.SCHEMA_MISSING_CORE_FIELDS,
10+
source: 'apilint',
11+
message: 'Schema does not include any Schema Object keywords',
12+
severity: DiagnosticSeverity.Hint,
13+
linterFunction: 'existAnyOfFields',
14+
linterParams: [
15+
[
16+
'$id',
17+
'$schema',
18+
'$comment',
19+
'$ref',
20+
'if',
21+
'then',
22+
'else',
23+
'contentEncoding',
24+
'contentMediaType',
25+
'contains',
26+
'propertyNames',
27+
'const',
28+
'examples',
29+
'multipleOf',
30+
'maximum',
31+
'exclusiveMaximum',
32+
'minimum',
33+
'exclusiveMinimum',
34+
'maxLength',
35+
'minLength',
36+
'pattern',
37+
'additionalItems',
38+
'items',
39+
'maxItems',
40+
'minItems',
41+
'uniqueItems',
42+
'patternProperties',
43+
'dependencies',
44+
'definitions',
45+
'maxProperties',
46+
'minProperties',
47+
'required',
48+
'properties',
49+
'additionalProperties',
50+
'enum',
51+
'type',
52+
'allOf',
53+
'anyOf',
54+
'oneOf',
55+
'not',
56+
'title',
57+
'description',
58+
'default',
59+
'format',
60+
'readOnly',
61+
'writeOnly',
62+
'discriminator',
63+
'externalDocs',
64+
'deprecated',
65+
],
66+
true,
67+
'boolean',
68+
],
69+
marker: 'key',
70+
targetSpecs: [...AsyncAPI2],
71+
};
72+
73+
export default missingCoreFieldsAsyncAPI2Lint;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { DiagnosticSeverity } from 'vscode-languageserver-types';
2+
3+
import ApilintCodes from '../../../codes';
4+
import { LinterMeta } from '../../../../apidom-language-types';
5+
import { OpenAPI2 } from '../../../openapi/target-specs';
6+
7+
// eslint-disable-next-line @typescript-eslint/naming-convention
8+
const missingCoreFieldsOpenAPI2Lint: LinterMeta = {
9+
code: ApilintCodes.SCHEMA_MISSING_CORE_FIELDS,
10+
source: 'apilint',
11+
message: 'Schema does not include any Schema Object keywords',
12+
severity: DiagnosticSeverity.Hint,
13+
linterFunction: 'existAnyOfFields',
14+
linterParams: [
15+
[
16+
'$ref',
17+
'multipleOf',
18+
'maximum',
19+
'exclusiveMaximum',
20+
'minimum',
21+
'exclusiveMinimum',
22+
'maxLength',
23+
'minLength',
24+
'pattern',
25+
'additionalItems',
26+
'items',
27+
'maxItems',
28+
'minItems',
29+
'uniqueItems',
30+
'maxProperties',
31+
'minProperties',
32+
'required',
33+
'properties',
34+
'additionalProperties',
35+
'enum',
36+
'type',
37+
'allOf',
38+
'title',
39+
'description',
40+
'default',
41+
'format',
42+
'readOnly',
43+
'discriminator',
44+
'externalDocs',
45+
'xml',
46+
'example',
47+
],
48+
true,
49+
'boolean',
50+
],
51+
marker: 'key',
52+
targetSpecs: [...OpenAPI2],
53+
};
54+
55+
export default missingCoreFieldsOpenAPI2Lint;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { DiagnosticSeverity } from 'vscode-languageserver-types';
2+
3+
import ApilintCodes from '../../../codes';
4+
import { LinterMeta } from '../../../../apidom-language-types';
5+
import { OpenAPI30 } from '../../../openapi/target-specs';
6+
7+
// eslint-disable-next-line @typescript-eslint/naming-convention
8+
const missingCoreFieldsOpenAPI3_0Lint: LinterMeta = {
9+
code: ApilintCodes.SCHEMA_MISSING_CORE_FIELDS,
10+
source: 'apilint',
11+
message: 'Schema does not include any Schema Object keywords',
12+
severity: DiagnosticSeverity.Hint,
13+
linterFunction: 'existAnyOfFields',
14+
linterParams: [
15+
[
16+
'$ref',
17+
'multipleOf',
18+
'maximum',
19+
'exclusiveMaximum',
20+
'minimum',
21+
'exclusiveMinimum',
22+
'maxLength',
23+
'minLength',
24+
'pattern',
25+
'additionalItems',
26+
'items',
27+
'maxItems',
28+
'minItems',
29+
'uniqueItems',
30+
'maxProperties',
31+
'minProperties',
32+
'required',
33+
'properties',
34+
'additionalProperties',
35+
'enum',
36+
'type',
37+
'allOf',
38+
'anyOf',
39+
'oneOf',
40+
'not',
41+
'title',
42+
'description',
43+
'default',
44+
'format',
45+
'readOnly',
46+
'nullable',
47+
'discriminator',
48+
'externalDocs',
49+
'writeOnly',
50+
'xml',
51+
'example',
52+
'deprecated',
53+
],
54+
true,
55+
'boolean',
56+
],
57+
marker: 'key',
58+
targetSpecs: [...OpenAPI30],
59+
};
60+
61+
export default missingCoreFieldsOpenAPI3_0Lint;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { DiagnosticSeverity } from 'vscode-languageserver-types';
2+
3+
import ApilintCodes from '../../../codes';
4+
import { LinterMeta } from '../../../../apidom-language-types';
5+
import { OpenAPI31 } from '../../../openapi/target-specs';
6+
7+
// eslint-disable-next-line @typescript-eslint/naming-convention
8+
const missingCoreFieldsOpenAPI3_1Lint: LinterMeta = {
9+
code: ApilintCodes.SCHEMA_MISSING_CORE_FIELDS,
10+
source: 'apilint',
11+
message: 'Schema does not include any Schema Object keywords',
12+
severity: DiagnosticSeverity.Hint,
13+
linterFunction: 'existAnyOfFields',
14+
linterParams: [
15+
[
16+
'$ref',
17+
'$schema',
18+
'$id',
19+
'$vocabulary',
20+
'$anchor',
21+
'$dynamicAnchor',
22+
'$defs',
23+
'$comment',
24+
'if',
25+
'then',
26+
'else',
27+
'dependentSchemas',
28+
'prefixItems',
29+
'contains',
30+
'patternProperties',
31+
'propertyNames',
32+
'unevaluatedProperties',
33+
'unevaluatedItems',
34+
'const',
35+
'maxContains',
36+
'minContains',
37+
'dependencies',
38+
'dependentRequired',
39+
'examples',
40+
'contentEncoding',
41+
'contentMediaType',
42+
'contentSchema',
43+
'definitions',
44+
'multipleOf',
45+
'maximum',
46+
'exclusiveMaximum',
47+
'minimum',
48+
'exclusiveMinimum',
49+
'maxLength',
50+
'minLength',
51+
'pattern',
52+
'additionalItems',
53+
'items',
54+
'maxItems',
55+
'minItems',
56+
'uniqueItems',
57+
'maxProperties',
58+
'minProperties',
59+
'required',
60+
'properties',
61+
'additionalProperties',
62+
'enum',
63+
'type',
64+
'allOf',
65+
'anyOf',
66+
'oneOf',
67+
'not',
68+
'title',
69+
'description',
70+
'default',
71+
'format',
72+
'readOnly',
73+
'discriminator',
74+
'externalDocs',
75+
'writeOnly',
76+
'xml',
77+
'example',
78+
'deprecated',
79+
],
80+
true,
81+
'boolean',
82+
],
83+
marker: 'key',
84+
targetSpecs: [...OpenAPI31],
85+
};
86+
87+
export default missingCoreFieldsOpenAPI3_1Lint;

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

+24
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,30 @@ export const standardLinterfunctions: FunctionItem[] = [
172172
return true;
173173
},
174174
},
175+
{
176+
functionName: 'existAnyOfFields',
177+
function: (
178+
element: Element,
179+
keys: string[],
180+
allowEmpty: boolean,
181+
skipIfType?: string,
182+
): boolean => {
183+
if (element && isObject(element)) {
184+
if (skipIfType && isType(element, skipIfType)) {
185+
return true;
186+
}
187+
if (!element.keys() || element.keys().length === 0) {
188+
return allowEmpty;
189+
}
190+
for (const key of keys) {
191+
if (element.hasKey(key)) {
192+
return true;
193+
}
194+
}
195+
}
196+
return false;
197+
},
198+
},
175199
{
176200
functionName: 'allowedFields',
177201
function: (

0 commit comments

Comments
 (0)