Skip to content

Commit 2c591b6

Browse files
authored
feat(ls): add rules for OpenAPI 2.0 Response Object (#3627)
Refs #3607
1 parent 2ab2840 commit 2c591b6

14 files changed

+200
-47
lines changed

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

+7
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,13 @@ enum ApilintCodes {
753753
OPENAPI2_ITEMS_FIELD_ENUM_TYPE = 3111200,
754754
OPENAPI2_ITEMS_FIELD_MULTIPLE_OF_TYPE = 3111300,
755755

756+
OPENAPI2_RESPONSE = 3120000,
757+
OPENAPI2_RESPONSE_FIELD_DESCRIPTION_TYPE = 3120100,
758+
OPENAPI2_RESPONSE_FIELD_DESCRIPTION_REQUIRED,
759+
OPENAPI2_RESPONSE_FIELD_SCHEMA_TYPE = 3120200,
760+
OPENAPI2_RESPONSE_FIELD_HEADERS_TYPE = 3120300,
761+
OPENAPI2_RESPONSE_FIELD_EXAMPLES_TYPE = 3120400,
762+
756763
OPENAPI3_0 = 5000000,
757764

758765
OPENAPI3_0_OPENAPI_VALUE_PATTERN_3_0_0 = 5000100,

packages/apidom-ls/src/config/openapi/response/completion.ts

+58-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
CompletionFormat,
44
CompletionType,
55
} from '../../../apidom-language-types';
6-
import { OpenAPI30, OpenAPI31, OpenAPI3 } from '../target-specs';
6+
import { OpenAPI2, OpenAPI30, OpenAPI31, OpenAPI3 } from '../target-specs';
77

88
const completion: ApidomCompletionItem[] = [
99
{
@@ -17,7 +17,21 @@ const completion: ApidomCompletionItem[] = [
1717
kind: 'markdown',
1818
value: 'A reference to a Response.',
1919
},
20-
targetSpecs: OpenAPI3,
20+
targetSpecs: [...OpenAPI2, ...OpenAPI3],
21+
},
22+
{
23+
label: 'description',
24+
insertText: 'description',
25+
kind: 14,
26+
format: CompletionFormat.QUOTED,
27+
type: CompletionType.PROPERTY,
28+
insertTextFormat: 2,
29+
documentation: {
30+
kind: 'markdown',
31+
value:
32+
'**Required.** A short description of the response. [GFM syntax](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown) can be used for rich text representation.',
33+
},
34+
targetSpecs: OpenAPI2,
2135
},
2236
{
2337
label: 'description',
@@ -33,6 +47,34 @@ const completion: ApidomCompletionItem[] = [
3347
},
3448
targetSpecs: OpenAPI3,
3549
},
50+
{
51+
label: 'schema',
52+
insertText: 'schema',
53+
kind: 14,
54+
format: CompletionFormat.OBJECT,
55+
type: CompletionType.PROPERTY,
56+
insertTextFormat: 2,
57+
documentation: {
58+
kind: 'markdown',
59+
value:
60+
'[Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#schemaObject)\n\\\n\\\nA definition of the response structure. It can be a primitive, an array or an object. If this field does not exist, it means no content is returned as part of the response. As an extension to the [Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#schemaObject), its root `type` value may also be `"file"`. This SHOULD be accompanied by a relevant `produces` mime-type.',
61+
},
62+
targetSpecs: OpenAPI2,
63+
},
64+
{
65+
label: 'headers',
66+
insertText: 'headers',
67+
kind: 14,
68+
format: CompletionFormat.OBJECT,
69+
type: CompletionType.PROPERTY,
70+
insertTextFormat: 2,
71+
documentation: {
72+
kind: 'markdown',
73+
value:
74+
'[Headers Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#headersObject)\n\\\n\\\nA list of headers that are sent with the response.',
75+
},
76+
targetSpecs: OpenAPI2,
77+
},
3678
{
3779
label: 'headers',
3880
insertText: 'headers',
@@ -61,6 +103,20 @@ const completion: ApidomCompletionItem[] = [
61103
},
62104
targetSpecs: OpenAPI31,
63105
},
106+
{
107+
label: 'examples',
108+
insertText: 'examples',
109+
kind: 14,
110+
format: CompletionFormat.OBJECT,
111+
type: CompletionType.PROPERTY,
112+
insertTextFormat: 2,
113+
documentation: {
114+
kind: 'markdown',
115+
value:
116+
'[Example Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#exampleObject)\n\\\n\\\nAn example of the response message.',
117+
},
118+
targetSpecs: OpenAPI2,
119+
},
64120
{
65121
label: 'content',
66122
insertText: 'content',

packages/apidom-ls/src/config/openapi/response/documentation.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
1-
import { OpenAPI30, OpenAPI31, OpenAPI3 } from '../target-specs';
1+
import { OpenAPI2, OpenAPI30, OpenAPI31, OpenAPI3 } from '../target-specs';
22

33
const documentation = [
44
{
55
target: 'description',
66
docs: '**REQUIRED**. A description of the response. [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.',
77
targetSpecs: OpenAPI3,
88
},
9+
{
10+
target: 'description',
11+
docs: '**Required.** A short description of the response. [GFM syntax](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown) can be used for rich text representation.',
12+
targetSpecs: OpenAPI2,
13+
},
14+
{
15+
target: 'schema',
16+
docs: '[Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#schemaObject)\n\\\n\\\nA definition of the response structure. It can be a primitive, an array or an object. If this field does not exist, it means no content is returned as part of the response. As an extension to the [Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#schemaObject), its root `type` value may also be `"file"`. This SHOULD be accompanied by a relevant `produces` mime-type.',
17+
targetSpecs: OpenAPI2,
18+
},
19+
{
20+
target: 'headers',
21+
docs: '[Headers Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#headersObject)\n\\\n\\\nA list of headers that are sent with the response.',
22+
targetSpecs: OpenAPI2,
23+
},
924
{
1025
target: 'headers',
1126
docs: 'Map[`string`, [Header Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#headerObject) | [Reference Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#referenceObject)]\n\\\n\\\nMaps a header name to its definition. [RFC7230](https://tools.ietf.org/html/rfc7230#page-22) states header names are case insensitive. If a response header is defined with the name `"Content-Type"`, it SHALL be ignored.',
@@ -16,6 +31,11 @@ const documentation = [
1631
docs: 'Map[`string`, [Header Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#headerObject) | [Reference Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#referenceObject)]\n\\\n\\\nMaps a header name to its definition. [RFC7230](https://tools.ietf.org/html/rfc7230#page-22) states header names are case insensitive. If a response header is defined with the name `"Content-Type"`, it SHALL be ignored.',
1732
targetSpecs: OpenAPI31,
1833
},
34+
{
35+
target: 'examples',
36+
docs: '[Example Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#exampleObject)\n\\\n\\\nAn example of the response message.',
37+
targetSpecs: OpenAPI2,
38+
},
1939
{
2040
target: 'content',
2141
docs: 'Map[`string`, [Media Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#mediaTypeObject)]\n\\\n\\\nA map containing descriptions of potential response payloads. The key is a media type or [media type range](https://tools.ietf.org/html/rfc7231#appendix-D) and the value describes it. For responses that match multiple keys, only the most specific key is applicable. e.g. text/plain overrides text/*',
@@ -36,6 +56,10 @@ const documentation = [
3656
docs: 'Map[`string`, [Link Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#linkObject) | [Reference Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#referenceObject)]\n\\\n\\\nA map of operations links that can be followed from the response. The key of the map is a short name for the link, following the naming constraints of the names for [Component Objects](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#componentsObject).',
3757
targetSpecs: OpenAPI31,
3858
},
59+
{
60+
docs: '#### [Response Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#response-object)\n\nDescribes a single response from an API Operation.\n\n##### Fixed Fields\nField Name | Type | Description\n---|:---:|---\ndescription | `string` | **Required.** A short description of the response. [GFM syntax](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown) can be used for rich text representation.\nschema | [Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#schemaObject) | A definition of the response structure. It can be a primitive, an array or an object. If this field does not exist, it means no content is returned as part of the response. As an extension to the [Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#schemaObject), its root `type` value may also be `"file"`. This SHOULD be accompanied by a relevant `produces` mime-type.\nheaders | [Headers Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#headersObject) | A list of headers that are sent with the response.\nexamples | [Example Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#exampleObject) | An example of the response message.\n\n##### Patterned Objects\n\nField Pattern | Type | Description\n---|:---:|---\n^x- | Any | Allows extensions to the Swagger Schema. The field name MUST begin with `x-`, for example, `x-internal-id`. The value can be `null`, a primitive, an array or an object. See [Vendor Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#vendorExtensions) for further details.\n\n##### Response Object Examples\n\nResponse of an array of a complex type:\n\n```js\n{\n "description": "A complex object array response",\n "schema": {\n "type": "array",\n "items": {\n "$ref": "#/definitions/VeryComplexType"\n }\n }\n}\n```\n\n\n\\\nYAML\n```yaml\ndescription: A complex object array response\nschema:\n type: array\n items:\n $ref: \'#/definitions/VeryComplexType\'\n```\n\nResponse with a string type:\n\n```js\n{\n "description": "A simple string response",\n "schema": {\n "type": "string"\n }\n}\n```\n\n```yaml\ndescription: A simple string response\nschema:\n type: string\n```\n\nResponse with headers:\n\n```js\n{\n "description": "A simple string response",\n "schema": {\n "type": "string"\n },\n "headers": {\n "X-Rate-Limit-Limit": {\n "description": "The number of allowed requests in the current period",\n "type": "integer"\n },\n "X-Rate-Limit-Remaining": {\n "description": "The number of remaining requests in the current period",\n "type": "integer"\n },\n "X-Rate-Limit-Reset": {\n "description": "The number of seconds left in the current period",\n "type": "integer"\n }\n }\n}\n```\n\n```yaml\ndescription: A simple string response\nschema:\n type: string\nheaders:\n X-Rate-Limit-Limit:\n description: The number of allowed requests in the current period\n type: integer\n X-Rate-Limit-Remaining:\n description: The number of remaining requests in the current period\n type: integer\n X-Rate-Limit-Reset:\n description: The number of seconds left in the current period\n type: integer\n```\n\nResponse with no return value:\n\n```js\n{\n "description": "object created"\n}\n```\n\n```yaml\ndescription: object created\n```',
61+
targetSpecs: OpenAPI2,
62+
},
3963
{
4064
docs: '#### [Response Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#responseObject)\nDescribes a single response from an API Operation, including design-time, static\n`links` to operations based on the response.\n\n##### Fixed Fields\nField Name | Type | Description\n---|:---:|---\ndescription | `string` | **REQUIRED**. A short description of the response. [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.\nheaders | Map[`string`, [Header Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#headerObject) \\| [Reference Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#referenceObject)] | Maps a header name to its definition. [RFC7230](https://tools.ietf.org/html/rfc7230#page-22) states header names are case insensitive. If a response header is defined with the name `"Content-Type"`, it SHALL be ignored.\ncontent | Map[`string`, [Media Type Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#mediaTypeObject)] | A map containing descriptions of potential response payloads. The key is a media type or [media type range](https://tools.ietf.org/html/rfc7231#appendix-D) and the value describes it. For responses that match multiple keys, only the most specific key is applicable. e.g. text/plain overrides text/*\nlinks | Map[`string`, [Link Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#linkObject) \\| [Reference Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#referenceObject)] | A map of operations links that can be followed from the response. The key of the map is a short name for the link, following the naming constraints of the names for [Component Objects](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#componentsObject).\n\nThis object MAY be extended with [Specification Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions).\n\n##### Response Object Examples\n\nResponse of an array of a complex type:\n\n\n\\\nJSON\n```json\n{\n "description": "A complex object array response",\n "content": {\n "application/json": {\n "schema": {\n "type": "array",\n "items": {\n "$ref": "#/components/schemas/VeryComplexType"\n }\n }\n }\n }\n}\n```\n\n\n\\\nYAML\n```yaml\ndescription: A complex object array response\ncontent:\n application/json:\n schema:\n type: array\n items:\n $ref: \'#/components/schemas/VeryComplexType\'\n```\n\nResponse with a string type:\n\n```json\n{\n "description": "A simple string response",\n "content": {\n "text/plain": {\n "schema": {\n "type": "string"\n }\n }\n }\n\n}\n```\n\n```yaml\ndescription: A simple string response\ncontent:\n text/plain:\n schema:\n type: string\n```\n\nPlain text response with headers:\n\n```json\n{\n "description": "A simple string response",\n "content": {\n "text/plain": {\n "schema": {\n "type": "string",\n "example": "whoa!"\n }\n }\n },\n "headers": {\n "X-Rate-Limit-Limit": {\n "description": "The number of allowed requests in the current period",\n "schema": {\n "type": "integer"\n }\n },\n "X-Rate-Limit-Remaining": {\n "description": "The number of remaining requests in the current period",\n "schema": {\n "type": "integer"\n }\n },\n "X-Rate-Limit-Reset": {\n "description": "The number of seconds left in the current period",\n "schema": {\n "type": "integer"\n }\n }\n }\n}\n```\n\n```yaml\ndescription: A simple string response\ncontent:\n text/plain:\n schema:\n type: string\n example: \'whoa!\'\nheaders:\n X-Rate-Limit-Limit:\n description: The number of allowed requests in the current period\n schema:\n type: integer\n X-Rate-Limit-Remaining:\n description: The number of remaining requests in the current period\n schema:\n type: integer\n X-Rate-Limit-Reset:\n description: The number of seconds left in the current period\n schema:\n type: integer\n```\n\nResponse with no return value:\n\n```json\n{\n "description": "object created"\n}\n```\n\n```yaml\ndescription: object created\n```',
4165
targetSpecs: OpenAPI30,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { DiagnosticSeverity } from 'vscode-languageserver-types';
2+
3+
import ApilintCodes from '../../../codes';
4+
import { LinterMeta } from '../../../../apidom-language-types';
5+
import { OpenAPI2 } from '../../target-specs';
6+
7+
// eslint-disable-next-line @typescript-eslint/naming-convention
8+
const allowedFields2_0Lint: LinterMeta = {
9+
code: ApilintCodes.NOT_ALLOWED_FIELDS,
10+
source: 'apilint',
11+
message: 'Object includes not allowed fields',
12+
severity: DiagnosticSeverity.Error,
13+
linterFunction: 'allowedFields',
14+
linterParams: [['description', 'schema', 'headers', 'examples', '$ref'], 'x-'],
15+
marker: 'key',
16+
targetSpecs: OpenAPI2,
17+
};
18+
19+
export default allowedFields2_0Lint;

packages/apidom-ls/src/config/openapi/response/lint/description--required.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { DiagnosticSeverity } from 'vscode-languageserver-types';
22

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

77
const descriptionRequiredLint: LinterMeta = {
8-
code: ApilintCodes.OPENAPI3_0_RESPONSE_FIELD_DESCRIPTION_REQUIRED,
8+
code: ApilintCodes.OPENAPI2_RESPONSE_FIELD_DESCRIPTION_REQUIRED,
99
source: 'apilint',
1010
message: "should always have a 'description'",
1111
severity: DiagnosticSeverity.Error,
@@ -28,7 +28,7 @@ const descriptionRequiredLint: LinterMeta = {
2828
params: ['$ref'],
2929
},
3030
],
31-
targetSpecs: OpenAPI3,
31+
targetSpecs: [...OpenAPI2, ...OpenAPI3],
3232
};
3333

3434
export default descriptionRequiredLint;

packages/apidom-ls/src/config/openapi/response/lint/description--type.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { DiagnosticSeverity } from 'vscode-languageserver-types';
22

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

77
const descriptionTypeLint: LinterMeta = {
8-
code: ApilintCodes.OPENAPI3_0_RESPONSE_FIELD_DESCRIPTION_TYPE,
8+
code: ApilintCodes.OPENAPI2_RESPONSE_FIELD_DESCRIPTION_TYPE,
99
source: 'apilint',
1010
message: 'description must be a string',
1111
severity: DiagnosticSeverity.Error,
@@ -14,7 +14,7 @@ const descriptionTypeLint: LinterMeta = {
1414
marker: 'value',
1515
target: 'description',
1616
data: {},
17-
targetSpecs: OpenAPI3,
17+
targetSpecs: [...OpenAPI2, ...OpenAPI3],
1818
};
1919

2020
export default descriptionTypeLint;
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';
4+
import { LinterMeta } from '../../../../apidom-language-types';
5+
import { OpenAPI2 } from '../../target-specs';
6+
7+
const examplesTypeLint: LinterMeta = {
8+
code: ApilintCodes.OPENAPI2_RESPONSE_FIELD_EXAMPLES_TYPE,
9+
source: 'apilint',
10+
message: '"examples" must be an object',
11+
severity: DiagnosticSeverity.Error,
12+
linterFunction: 'apilintElementOrClass',
13+
linterParams: ['example'],
14+
marker: 'key',
15+
markerTarget: 'examples',
16+
target: 'examples',
17+
data: {},
18+
targetSpecs: OpenAPI2,
19+
};
20+
21+
export default examplesTypeLint;
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';
4+
import { LinterMeta } from '../../../../apidom-language-types';
5+
import { OpenAPI2 } from '../../target-specs';
6+
7+
const headersTypeLint: LinterMeta = {
8+
code: ApilintCodes.OPENAPI2_RESPONSE_FIELD_HEADERS_TYPE,
9+
source: 'apilint',
10+
message: '"headers" must be an object',
11+
severity: DiagnosticSeverity.Error,
12+
linterFunction: 'apilintElementOrClass',
13+
linterParams: ['headers'],
14+
marker: 'key',
15+
markerTarget: 'headers',
16+
target: 'headers',
17+
data: {},
18+
targetSpecs: OpenAPI2,
19+
};
20+
21+
export default headersTypeLint;

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

+8
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1+
import allowedFields2_0Lint from './allowed-fields-2-0';
12
import allowedFields3_0Lint from './allowed-fields-3-0';
23
import allowedFields3_1Lint from './allowed-fields-3-1';
34
import descriptionTypeLint from './description--type';
45
import descriptionRequiredLint from './description--required';
56
import headersValuesTypeLint from './headers--values-type';
7+
import headersTypeLint from './headers--type';
68
import contentValuesTypeLint from './content--values-type';
79
import linksValuesTypeLint from './links--values-type';
10+
import schemaTypeLint from './schema--type';
11+
import examplesTypeLint from './examples--type';
812

913
const lints = [
1014
descriptionTypeLint,
1115
descriptionRequiredLint,
16+
headersTypeLint,
1217
headersValuesTypeLint,
1318
contentValuesTypeLint,
1419
linksValuesTypeLint,
20+
schemaTypeLint,
21+
examplesTypeLint,
22+
allowedFields2_0Lint,
1523
allowedFields3_0Lint,
1624
allowedFields3_1Lint,
1725
];

0 commit comments

Comments
 (0)