From dfa91c2b5a8a9b41e05524db31d6e79d18994f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Gorej?= Date: Tue, 12 Dec 2023 14:01:16 +0100 Subject: [PATCH] feat(ls): add rules for OpenAPI 2.0 Swagger Object (#3527) These rules include: - Completion rules - Documentation rules - Linting rules Refs #3104 --- packages/apidom-ls/src/config/codes.ts | 27 +- .../openapi/openapi3_0/lint/info--required.ts | 2 +- .../src/config/openapi/swagger/completion.ts | 220 +++++- .../config/openapi/swagger/documentation.ts | 68 +- .../swagger/lint/base-path--pattern.ts | 20 + .../openapi/swagger/lint/consumes--type.ts | 20 + .../openapi/swagger/lint/definitions--type.ts | 20 + .../swagger/lint/external-docs--type.ts | 20 + .../openapi/swagger/lint/host--pattern.ts | 23 + .../src/config/openapi/swagger/lint/index.ts | 38 +- .../openapi/swagger/lint/info--required.ts | 28 + .../config/openapi/swagger/lint/info--type.ts | 20 + .../openapi/swagger/lint/parameters--type.ts | 20 + .../openapi/swagger/lint/paths--required.ts | 28 + .../openapi/swagger/lint/paths--type.ts | 20 + .../openapi/swagger/lint/produces--type.ts | 20 + .../openapi/swagger/lint/responses--type.ts | 20 + .../openapi/swagger/lint/schemes--equals.ts | 20 + .../openapi/swagger/lint/schemes--type.ts | 20 + .../openapi/swagger/lint/security--type.ts | 20 + .../lint/security-definitions--type.ts | 20 + .../config/openapi/swagger/lint/tags--type.ts | 20 + .../oas/valid/oas-2.0-petstore.yaml | 700 ++++++++++++++++++ packages/apidom-ls/test/validate.ts | 52 +- 24 files changed, 1448 insertions(+), 18 deletions(-) create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/base-path--pattern.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/consumes--type.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/definitions--type.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/external-docs--type.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/host--pattern.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/info--required.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/info--type.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/parameters--type.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/paths--required.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/paths--type.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/produces--type.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/responses--type.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/schemes--equals.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/schemes--type.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/security--type.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/security-definitions--type.ts create mode 100644 packages/apidom-ls/src/config/openapi/swagger/lint/tags--type.ts create mode 100644 packages/apidom-ls/test/fixtures/validation/oas/valid/oas-2.0-petstore.yaml diff --git a/packages/apidom-ls/src/config/codes.ts b/packages/apidom-ls/src/config/codes.ts index 786af59db3..0ca6896eee 100644 --- a/packages/apidom-ls/src/config/codes.ts +++ b/packages/apidom-ls/src/config/codes.ts @@ -627,9 +627,28 @@ enum ApilintCodes { OPENAPI2 = 3000000, - OPENAPI2_CONTACT = 3010000, - - OPENAPI2_INFO = 3020000, + OPENAPI2_SWAGGER = 3010000, + OPENAPI2_SWAGGER_FIELD_INFO_TYPE = 3010100, + OPENAPI2_SWAGGER_FIELD_INFO_REQUIRED, + OPENAPI2_SWAGGER_FIELD_HOST_PATTERN = 3010200, + OPENAPI2_SWAGGER_FIELD_BASE_PATH_PATTERN = 3010300, + OPENAPI2_SWAGGER_FIELD_SCHEMES_TYPE = 3010400, + OPENAPI2_SWAGGER_FIELD_SCHEMES_EQUALS, + OPENAPI2_SWAGGER_FIELD_CONSUMES_TYPE = 3010500, + OPENAPI2_SWAGGER_FIELD_PRODUCES_TYPE = 3010600, + OPENAPI2_SWAGGER_FIELD_PATHS_TYPE = 3010700, + OPENAPI2_SWAGGER_FIELD_PATHS_REQUIRED, + OPENAPI2_SWAGGER_FIELD_DEFINITIONS_TYPE = 3010800, + OPENAPI2_SWAGGER_FIELD_PARAMETERS_TYPE = 3010900, + OPENAPI2_SWAGGER_FIELD_RESPONSES_TYPE = 3011000, + OPENAPI2_SWAGGER_FIELD_SECURITY_DEFINITIONS_TYPE = 3011100, + OPENAPI2_SWAGGER_FIELD_SECURITY_TYPE = 3011200, + OPENAPI2_SWAGGER_FIELD_TAGS_TYPE = 3011300, + OPENAPI2_SWAGGER_FIELD_EXTERNAL_DOCS_TYPE = 3011400, + + OPENAPI2_CONTACT = 3020000, + + OPENAPI2_INFO = 3030000, OPENAPI3_0 = 5000000, @@ -640,7 +659,7 @@ enum ApilintCodes { OPENAPI3_0_OPEN_API = 5010000, OPENAPI3_0_OPEN_API_FIELD_INFO_TYPE = 5010100, - OPENAPI3_0_OPEN_API_FIELD_INFO_TYPE_REQUIRED, + OPENAPI3_0_OPEN_API_FIELD_INFO_REQUIRED, OPENAPI3_0_OPEN_API_FIELD_SERVERS_TYPE = 5010200, OPENAPI3_0_OPEN_API_FIELD_SERVERS_ITEMS_TYPE, OPENAPI3_0_OPEN_API_FIELD_PATHS_TYPE = 5010300, diff --git a/packages/apidom-ls/src/config/openapi/openapi3_0/lint/info--required.ts b/packages/apidom-ls/src/config/openapi/openapi3_0/lint/info--required.ts index dde65ca582..b8dd18aa95 100644 --- a/packages/apidom-ls/src/config/openapi/openapi3_0/lint/info--required.ts +++ b/packages/apidom-ls/src/config/openapi/openapi3_0/lint/info--required.ts @@ -5,7 +5,7 @@ import { LinterMeta } from '../../../../apidom-language-types'; import { OpenAPI30 } from '../../target-specs'; const infoRequiredLint: LinterMeta = { - code: ApilintCodes.OPENAPI3_0_OPEN_API_FIELD_INFO_TYPE_REQUIRED, + code: ApilintCodes.OPENAPI3_0_OPEN_API_FIELD_INFO_REQUIRED, source: 'apilint', message: "should always have a 'info' section", severity: DiagnosticSeverity.Error, diff --git a/packages/apidom-ls/src/config/openapi/swagger/completion.ts b/packages/apidom-ls/src/config/openapi/swagger/completion.ts index 6426e942fd..eb9d0a38fc 100644 --- a/packages/apidom-ls/src/config/openapi/swagger/completion.ts +++ b/packages/apidom-ls/src/config/openapi/swagger/completion.ts @@ -1,5 +1,221 @@ -import { ApidomCompletionItem } from '../../../apidom-language-types'; +import { + ApidomCompletionItem, + CompletionFormat, + CompletionType, +} from '../../../apidom-language-types'; +import { OpenAPI2 } from '../target-specs'; -const completion: ApidomCompletionItem[] = []; +const completion: ApidomCompletionItem[] = [ + { + label: 'swagger', + insertText: 'swagger', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '**Required.** Specifies the Swagger Specification version being used. It can be used by the Swagger UI and other clients to interpret the API listing. The value MUST be `"2.0"`.', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'info', + insertText: 'info', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[Info Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#infoObject)\n\\\n\\\n**Required.** Provides metadata about the API. The metadata can be used by the clients if needed.', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'host', + insertText: 'host', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor sub-paths. It MAY include a port. If the `host` is not included, the host serving the documentation is to be used (including the port). The `host` does not support [path templating](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#pathTemplating).', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'basePath', + insertText: 'basePath', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + 'The base path on which the API is served, which is relative to the [`host`](#swaggerHost). If it is not included, the API is served directly under the `host`. The value MUST start with a leading slash (`/`). The `basePath` does not support [path templating](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#pathTemplating).', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'schemes', + insertText: 'schemes', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[string]\n\\\n\\\nThe transfer protocol of the API. Values MUST be from the list: `"http"`, `"https"`, `"ws"`, `"wss"`. If the `schemes` is not included, the default scheme to be used is the one used to access the Swagger definition itself.', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'consumes', + insertText: 'consumes', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[string]\n\\\n\\\nA list of MIME types the APIs can consume. This is global to all APIs but can be overridden on specific API calls. Value MUST be as described under [Mime Types](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#mimeTypes).', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'produces', + insertText: 'produces', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[string]\n\\\n\\\nA list of MIME types the APIs can produce. This is global to all APIs but can be overridden on specific API calls. Value MUST be as described under [Mime Types](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#mimeTypes).', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'paths', + insertText: 'paths', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[Paths Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#pathsObject)\n\\\n\\\n**Required.** The available paths and operations for the API.', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'definitions', + insertText: 'definitions', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[Definitions Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#definitionsObject)\n\\\n\\\nAn object to hold data types produced and consumed by operations.', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'parameters', + insertText: 'parameters', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[Parameters Definitions Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#parametersDefinitionsObject)\n\\\n\\\nAn object to hold parameters that can be used across operations. This property does not define global parameters for all operations.', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'responses', + insertText: 'responses', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[Responses Definitions Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#responsesDefinitionsObject)\n\\\n\\\nAn object to hold responses that can be used across operations. This property does not define global responses for all operations.', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'securityDefinitions', + insertText: 'securityDefinitions', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[Security Definitions Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#securityDefinitionsObject)\n\\\n\\\nSecurity scheme definitions that can be used across the specification.', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'security', + insertText: 'security', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[[Security Requirement Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#securityRequirementObject)]\n\\\n\\\nA declaration of which security schemes are applied for the API as a whole. The list of values describes alternative security schemes that can be used (that is, there is a logical OR between the security requirements). Individual operations can override this definition.', + }, + targetSpecs: OpenAPI2, + }, + { + label: 'tags', + insertText: 'tags', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + "[[Tag Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#tagObject)]\n\\\n\\\nA list of tags used by the specification with additional metadata. The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used by the [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#operationObject) must be declared. The tags that are not declared may be organized randomly or based on the tools' logic. Each tag name in the list MUST be unique.", + }, + targetSpecs: OpenAPI2, + }, + { + label: 'externalDocs', + insertText: 'externalDocs', + kind: 14, + format: CompletionFormat.QUOTED, + type: CompletionType.PROPERTY, + insertTextFormat: 2, + documentation: { + kind: 'markdown', + value: + '[External Documentation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#externalDocumentationObject)\n\\\n\\\nAdditional external documentation.', + }, + targetSpecs: OpenAPI2, + }, +]; export default completion; diff --git a/packages/apidom-ls/src/config/openapi/swagger/documentation.ts b/packages/apidom-ls/src/config/openapi/swagger/documentation.ts index 554b878282..164a2a241e 100644 --- a/packages/apidom-ls/src/config/openapi/swagger/documentation.ts +++ b/packages/apidom-ls/src/config/openapi/swagger/documentation.ts @@ -1,5 +1,71 @@ import { DocumentationMeta } from '../../../apidom-language-types'; +import { OpenAPI2 } from '../target-specs'; -const documentation: DocumentationMeta[] = []; +/** + * Omitted fixed fields: + * - definitions + * - parameters + * - responses + * - securityDefinitions + * - externalDocs + * + * Field omission reason: omitted fields do have a non-union type. Thus, + * documentation for these fields doesn't need to be specified here and will + * come directly from the type itself. Description of these fields doesn't + * contain significant information. + */ + +const documentation: DocumentationMeta[] = [ + { + target: 'swagger', + docs: '**Required.** Specifies the Swagger Specification version being used. It can be used by the Swagger UI and other clients to interpret the API listing. The value MUST be `"2.0"`.', + targetSpecs: OpenAPI2, + }, + { + target: 'info', + docs: '[Info Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#infoObject)\n\\\n\\\n**Required.** Provides metadata about the API. The metadata can be used by the clients if needed.', + targetSpecs: OpenAPI2, + }, + { + target: 'host', + docs: 'The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor sub-paths. It MAY include a port. If the `host` is not included, the host serving the documentation is to be used (including the port). The `host` does not support [path templating](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#pathTemplating).', + targetSpecs: OpenAPI2, + }, + { + target: 'basePath', + docs: 'The base path on which the API is served, which is relative to the [`host`](#swaggerHost). If it is not included, the API is served directly under the `host`. The value MUST start with a leading slash (`/`). The `basePath` does not support [path templating](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#pathTemplating).', + targetSpecs: OpenAPI2, + }, + { + target: 'schemes', + docs: '[string]\n\\\n\\\nThe transfer protocol of the API. Values MUST be from the list: `"http"`, `"https"`, `"ws"`, `"wss"`. If the `schemes` is not included, the default scheme to be used is the one used to access the Swagger definition itself.', + targetSpecs: OpenAPI2, + }, + { + target: 'consumes', + docs: '[string]\n\\\n\\\nA list of MIME types the APIs can consume. This is global to all APIs but can be overridden on specific API calls. Value MUST be as described under [Mime Types](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#mimeTypes).', + targetSpecs: OpenAPI2, + }, + { + target: 'produces', + docs: '[string]\n\\\n\\\nA list of MIME types the APIs can produce. This is global to all APIs but can be overridden on specific API calls. Value MUST be as described under [Mime Types](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#mimeTypes).', + targetSpecs: OpenAPI2, + }, + { + target: 'paths', + docs: '[Paths Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#pathsObject)\n\\\n\\\n**Required.** The available paths and operations for the API.', + targetSpecs: OpenAPI2, + }, + { + target: 'security', + docs: '[[Security Requirement Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#securityRequirementObject)]\n\\\n\\\nA declaration of which security schemes are applied for the API as a whole. The list of values describes alternative security schemes that can be used (that is, there is a logical OR between the security requirements). Individual operations can override this definition.', + targetSpecs: OpenAPI2, + }, + { + target: 'tags', + docs: "[[Tag Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#tagObject)]\n\\\n\\\nA list of tags used by the specification with additional metadata. The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used by the [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#operationObject) must be declared. The tags that are not declared may be organized randomly or based on the tools' logic. Each tag name in the list MUST be unique.", + targetSpecs: OpenAPI2, + }, +]; export default documentation; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/base-path--pattern.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/base-path--pattern.ts new file mode 100644 index 0000000000..ebb1402f43 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/base-path--pattern.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const basePathPatternLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_BASE_PATH_PATTERN, + source: 'apilint', + message: '"basePath" value MUST be a relative URI Referencing starting with a leading slash (/).', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintValueRegex', + linterParams: ['^/(?:[^/s][^s]*)?$'], + target: 'basePath', + marker: 'value', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default basePathPatternLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/consumes--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/consumes--type.ts new file mode 100644 index 0000000000..3378a73f7d --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/consumes--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const consumesTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_CONSUMES_TYPE, + source: 'apilint', + message: "'consumes' should be an array of strings", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintArrayOfType', + linterParams: ['string'], + marker: 'key', + target: 'consumes', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default consumesTypeLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/definitions--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/definitions--type.ts new file mode 100644 index 0000000000..9ce20338a4 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/definitions--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const definitionsTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_DEFINITIONS_TYPE, + source: 'apilint', + message: '"definitions" must be an object', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['definitions'], + marker: 'value', + target: 'definitions', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default definitionsTypeLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/external-docs--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/external-docs--type.ts new file mode 100644 index 0000000000..fe585f6ebe --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/external-docs--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const externalDocsTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_EXTERNAL_DOCS_TYPE, + source: 'apilint', + message: '"externalDocs" must be an object', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['externalDocumentation'], + marker: 'value', + target: 'externalDocs', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default externalDocsTypeLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/host--pattern.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/host--pattern.ts new file mode 100644 index 0000000000..3ae8149524 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/host--pattern.ts @@ -0,0 +1,23 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const hostPatternLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_HOST_PATTERN, + source: 'apilint', + message: + '"host" must be name or IP. Must not not include the scheme nor sub-paths and MAY include a port.', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintValueRegex', + linterParams: [ + '^(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+(?:[A-Za-z]{2,6}|[A-Za-z]{2,4}\\.[A-Za-z]{2,4})|localhost|(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}|::(?:[A-Fa-f0-9]{1,4}:){0,5}[A-Fa-f0-9]{1,4}|[A-Fa-f0-9]{1,4}::(?:[A-Fa-f0-9]{1,4}:){0,5}[A-Fa-f0-9]{1,4}|(?:[A-Fa-f0-9]{1,4}:){2}:?(?:[A-Fa-f0-9]{1,4}:){0,4}[A-Fa-f0-9]{1,4}|(?:[A-Fa-f0-9]{1,4}:){1,3}:?(?:[A-Fa-f0-9]{1,4}:){0,3}[A-Fa-f0-9]{1,4}|(?:[A-Fa-f0-9]{1,4}:){1,2}:?((?:[A-Fa-f0-9]{1,4}:){0,2}[A-Fa-f0-9]{1,4})|(?:::)(?:[A-Fa-f0-9]{1,4}:){1,7}[A-Fa-f0-9]{1,4})(?::[0-9]{1,5})?$', + ], + target: 'host', + marker: 'value', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default hostPatternLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/index.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/index.ts index e5a90a6b9d..f3f43911fe 100644 --- a/packages/apidom-ls/src/config/openapi/swagger/lint/index.ts +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/index.ts @@ -1,5 +1,41 @@ import allowedFieldsLint from './allowed-fields'; +import infoTypeLint from './info--type'; +import infoRequiredLint from './info--required'; +import hostPatternLint from './host--pattern'; +import basePathPatternLint from './base-path--pattern'; +import schemesTypeLint from './schemes--type'; +import schemesEqualsLint from './schemes--equals'; +import consumesTypeLint from './consumes--type'; +import producesTypeLint from './produces--type'; +import pathsTypeLint from './paths--type'; +import pathsRequiredLint from './paths--required'; +import definitionsTypeLint from './definitions--type'; +import parametersTypeLint from './parameters--type'; +import responsesTypeLint from './responses--type'; +import securityDefinitionsTypeLint from './security-definitions--type'; +import securityTypeLint from './security--type'; +import tagsTypeLint from './tags--type'; +import externalDocsTypeLint from './external-docs--type'; -const lints = [allowedFieldsLint]; +const lints = [ + allowedFieldsLint, + infoTypeLint, + infoRequiredLint, + hostPatternLint, + basePathPatternLint, + schemesTypeLint, + schemesEqualsLint, + consumesTypeLint, + producesTypeLint, + pathsTypeLint, + pathsRequiredLint, + definitionsTypeLint, + parametersTypeLint, + responsesTypeLint, + securityDefinitionsTypeLint, + securityTypeLint, + tagsTypeLint, + externalDocsTypeLint, +]; export default lints; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/info--required.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/info--required.ts new file mode 100644 index 0000000000..fcd0efd262 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/info--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const infoRequiredLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_INFO_REQUIRED, + source: 'apilint', + message: "should always have a 'info' section", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['info'], + marker: 'key', + data: { + quickFix: [ + { + message: "add 'info' section", + action: 'addChild', + snippetYaml: 'info: \n \n', + snippetJson: '"info": {\n \n },\n', + }, + ], + }, + targetSpecs: OpenAPI2, +}; + +export default infoRequiredLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/info--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/info--type.ts new file mode 100644 index 0000000000..8bb022dff7 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/info--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const infoTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_INFO_TYPE, + source: 'apilint', + message: 'info must be an object', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['info'], + marker: 'value', + target: 'info', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default infoTypeLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/parameters--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/parameters--type.ts new file mode 100644 index 0000000000..0d16aa06aa --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/parameters--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const parametersTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_PARAMETERS_TYPE, + source: 'apilint', + message: '"parameters" must be an object', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['parametersDefinitions'], + marker: 'value', + target: 'parameters', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default parametersTypeLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/paths--required.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/paths--required.ts new file mode 100644 index 0000000000..58c7b0953d --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/paths--required.ts @@ -0,0 +1,28 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const pathsRequiredLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_PATHS_REQUIRED, + source: 'apilint', + message: "should always have a 'paths' section", + severity: DiagnosticSeverity.Error, + linterFunction: 'hasRequiredField', + linterParams: ['paths'], + marker: 'key', + data: { + quickFix: [ + { + message: "add 'paths' section", + action: 'addChild', + snippetYaml: 'paths: \n \n', + snippetJson: '"paths": {\n \n },\n', + }, + ], + }, + targetSpecs: OpenAPI2, +}; + +export default pathsRequiredLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/paths--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/paths--type.ts new file mode 100644 index 0000000000..506a4ab438 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/paths--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const pathsTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_PATHS_TYPE, + source: 'apilint', + message: 'paths must be an object', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['paths'], + marker: 'value', + target: 'paths', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default pathsTypeLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/produces--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/produces--type.ts new file mode 100644 index 0000000000..f0b5bcb588 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/produces--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const producesTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_PRODUCES_TYPE, + source: 'apilint', + message: "'produces' should be an array of strings", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintArrayOfType', + linterParams: ['string'], + marker: 'key', + target: 'produces', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default producesTypeLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/responses--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/responses--type.ts new file mode 100644 index 0000000000..b38c8858fe --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/responses--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const responsesTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_RESPONSES_TYPE, + source: 'apilint', + message: '"responses" must be an object', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['responsesDefinitions'], + marker: 'value', + target: 'responses', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default responsesTypeLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/schemes--equals.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/schemes--equals.ts new file mode 100644 index 0000000000..aa7b985009 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/schemes--equals.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const schemesEqualsLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_SCHEMES_EQUALS, + source: 'apilint', + message: "'schemes' must be list of allowed values", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintValueOrArray', + linterParams: [['http', 'https', 'ws', 'wss']], + marker: 'key', + target: 'schemes', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default schemesEqualsLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/schemes--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/schemes--type.ts new file mode 100644 index 0000000000..2c99a02257 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/schemes--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const schemesTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_SCHEMES_TYPE, + source: 'apilint', + message: "'schemes' should be an array of strings", + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintArrayOfType', + linterParams: ['string'], + marker: 'key', + target: 'schemes', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default schemesTypeLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/security--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/security--type.ts new file mode 100644 index 0000000000..e844dcd284 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/security--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const securityTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_SECURITY_TYPE, + source: 'apilint', + message: '"security" must be an array of Security Requirement Objects', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintArrayOfElementsOrClasses', + linterParams: [['securityRequirement']], + marker: 'value', + target: 'security', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default securityTypeLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/security-definitions--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/security-definitions--type.ts new file mode 100644 index 0000000000..5eae43b564 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/security-definitions--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const securityDefinitionsTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_SECURITY_DEFINITIONS_TYPE, + source: 'apilint', + message: '"securityDefinitions" must be an object', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintElementOrClass', + linterParams: ['securityDefinitions'], + marker: 'value', + target: 'securityDefinitions', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default securityDefinitionsTypeLint; diff --git a/packages/apidom-ls/src/config/openapi/swagger/lint/tags--type.ts b/packages/apidom-ls/src/config/openapi/swagger/lint/tags--type.ts new file mode 100644 index 0000000000..f307ecaf75 --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/swagger/lint/tags--type.ts @@ -0,0 +1,20 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2 } from '../../target-specs'; + +const tagsTypeLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_SWAGGER_FIELD_SECURITY_TYPE, + source: 'apilint', + message: '"tags" must be an array of Tag Objects', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintArrayOfElementsOrClasses', + linterParams: [['tag']], + marker: 'value', + target: 'tags', + data: {}, + targetSpecs: OpenAPI2, +}; + +export default tagsTypeLint; diff --git a/packages/apidom-ls/test/fixtures/validation/oas/valid/oas-2.0-petstore.yaml b/packages/apidom-ls/test/fixtures/validation/oas/valid/oas-2.0-petstore.yaml new file mode 100644 index 0000000000..2bd02b2bb0 --- /dev/null +++ b/packages/apidom-ls/test/fixtures/validation/oas/valid/oas-2.0-petstore.yaml @@ -0,0 +1,700 @@ +swagger: "2.0" +info: + description: "This is a sample server Petstore server. You can find out more about \ Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). \ For this sample, you can use the api key `special-key` to test the authorization \ filters." + version: "1.0.0" + title: "Swagger Petstore 2.0" + termsOfService: "http://swagger.io/terms/" + contact: + email: "apiteam@swagger.io" + license: + name: "Apache 2.0" + url: "http://www.apache.org/licenses/LICENSE-2.0.html" +host: "petstore.swagger.io" +basePath: "/v2" +tags: + - name: "pet" + description: "Everything about your Pets" + externalDocs: + description: "Find out more" + url: "http://swagger.io" + - name: "store" + description: "Access to Petstore orders" + - name: "user" + description: "Operations about user" + externalDocs: + description: "Find out more about our store" + url: "http://swagger.io" +schemes: + - "https" + - "http" +paths: + /pet: + post: + tags: + - "pet" + summary: "Add a new pet to the store" + description: "" + operationId: "addPet" + consumes: + - "application/json" + - "application/xml" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "Pet object that needs to be added to the store" + required: true + schema: + $ref: "#/definitions/Pet" + responses: + "405": + description: "Invalid input" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + put: + tags: + - "pet" + summary: "Update an existing pet" + description: "" + operationId: "updatePet" + consumes: + - "application/json" + - "application/xml" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "Pet object that needs to be added to the store" + required: true + schema: + $ref: "#/definitions/Pet" + responses: + "400": + description: "Invalid ID supplied" + "404": + description: "Pet not found" + "405": + description: "Validation exception" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + /pet/findByStatus: + get: + tags: + - "pet" + summary: "Finds Pets by status" + description: "Multiple status values can be provided with comma separated strings" + operationId: "findPetsByStatus" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "status" + in: "query" + description: "Status values that need to be considered for filter" + required: true + type: "array" + items: + type: "string" + enum: + - "available" + - "pending" + - "sold" + default: "available" + collectionFormat: "multi" + responses: + "200": + description: "successful operation" + schema: + type: "array" + items: + $ref: "#/definitions/Pet" + "400": + description: "Invalid status value" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + /pet/findByTags: + get: + tags: + - "pet" + summary: "Finds Pets by tags" + description: "Muliple tags can be provided with comma separated strings. Use \ tag1, tag2, tag3 for testing." + operationId: "findPetsByTags" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "tags" + in: "query" + description: "Tags to filter by" + required: true + type: "array" + items: + type: "string" + collectionFormat: "multi" + responses: + "200": + description: "successful operation" + schema: + type: "array" + items: + $ref: "#/definitions/Pet" + "400": + description: "Invalid tag value" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + deprecated: true + /pet/{petId}: + get: + tags: + - "pet" + summary: "Find pet by ID" + description: "Returns a single pet" + operationId: "getPetById" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "petId" + in: "path" + description: "ID of pet to return" + required: true + type: "integer" + format: "int64" + responses: + "200": + description: "successful operation" + schema: + $ref: "#/definitions/Pet" + "400": + description: "Invalid ID supplied" + "404": + description: "Pet not found" + security: + - api_key: [] + post: + tags: + - "pet" + summary: "Updates a pet in the store with form data" + description: "" + operationId: "updatePetWithForm" + consumes: + - "application/x-www-form-urlencoded" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "petId" + in: "path" + description: "ID of pet that needs to be updated" + required: true + type: "integer" + format: "int64" + - name: "name" + in: "formData" + description: "Updated name of the pet" + required: false + type: "string" + - name: "status" + in: "formData" + description: "Updated status of the pet" + required: false + type: "string" + responses: + "405": + description: "Invalid input" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + delete: + tags: + - "pet" + summary: "Deletes a pet" + description: "" + operationId: "deletePet" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "api_key" + in: "header" + required: false + type: "string" + - name: "petId" + in: "path" + description: "Pet id to delete" + required: true + type: "integer" + format: "int64" + responses: + "400": + description: "Invalid ID supplied" + "404": + description: "Pet not found" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + /pet/{petId}/uploadImage: + post: + tags: + - "pet" + summary: "uploads an image" + description: "" + operationId: "uploadFile" + consumes: + - "multipart/form-data" + produces: + - "application/json" + parameters: + - name: "petId" + in: "path" + description: "ID of pet to update" + required: true + type: "integer" + format: "int64" + - name: "additionalMetadata" + in: "formData" + description: "Additional data to pass to server" + required: false + type: "string" + - name: "file" + in: "formData" + description: "file to upload" + required: false + type: "file" + responses: + "200": + description: "successful operation" + schema: + $ref: "#/definitions/ApiResponse" + security: + - petstore_auth: + - "write:pets" + - "read:pets" + /store/inventory: + get: + tags: + - "store" + summary: "Returns pet inventories by status" + description: "Returns a map of status codes to quantities" + operationId: "getInventory" + produces: + - "application/json" + parameters: [] + responses: + "200": + description: "successful operation" + schema: + type: "object" + additionalProperties: + type: "integer" + format: "int32" + security: + - api_key: [] + /store/order: + post: + tags: + - "store" + summary: "Place an order for a pet" + description: "" + operationId: "placeOrder" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "order placed for purchasing the pet" + required: true + schema: + $ref: "#/definitions/Order" + responses: + "200": + description: "successful operation" + schema: + $ref: "#/definitions/Order" + "400": + description: "Invalid Order" + /store/order/{orderId}: + get: + tags: + - "store" + summary: "Find purchase order by ID" + description: "For valid response try integer IDs with value >= 1 and <= 10. \ Other values will generated exceptions" + operationId: "getOrderById" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "orderId" + in: "path" + description: "ID of pet that needs to be fetched" + required: true + type: "integer" + maximum: 10.0 + minimum: 1.0 + format: "int64" + responses: + "200": + description: "successful operation" + schema: + $ref: "#/definitions/Order" + "400": + description: "Invalid ID supplied" + "404": + description: "Order not found" + delete: + tags: + - "store" + summary: "Delete purchase order by ID" + description: "For valid response try integer IDs with positive integer value. \ Negative or non-integer values will generate API errors" + operationId: "deleteOrder" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "orderId" + in: "path" + description: "ID of the order that needs to be deleted" + required: true + type: "integer" + minimum: 1.0 + format: "int64" + responses: + "400": + description: "Invalid ID supplied" + "404": + description: "Order not found" + /user: + post: + tags: + - "user" + summary: "Create user" + description: "This can only be done by the logged in user." + operationId: "createUser" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "Created user object" + required: true + schema: + $ref: "#/definitions/User" + responses: + default: + description: "successful operation" + /user/createWithArray: + post: + tags: + - "user" + summary: "Creates list of users with given input array" + description: "" + operationId: "createUsersWithArrayInput" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "List of user object" + required: true + schema: + type: "array" + items: + $ref: "#/definitions/User" + responses: + default: + description: "successful operation" + /user/createWithList: + post: + tags: + - "user" + summary: "Creates list of users with given input array" + description: "" + operationId: "createUsersWithListInput" + produces: + - "application/xml" + - "application/json" + parameters: + - in: "body" + name: "body" + description: "List of user object" + required: true + schema: + type: "array" + items: + $ref: "#/definitions/User" + responses: + default: + description: "successful operation" + /user/login: + get: + tags: + - "user" + summary: "Logs user into the system" + description: "" + operationId: "loginUser" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "username" + in: "query" + description: "The user name for login" + required: true + type: "string" + - name: "password" + in: "query" + description: "The password for login in clear text" + required: true + type: "string" + responses: + "200": + description: "successful operation" + schema: + type: "string" + headers: + X-Rate-Limit: + type: "integer" + format: "int32" + description: "calls per hour allowed by the user" + X-Expires-After: + type: "string" + format: "date-time" + description: "date in UTC when token expires" + "400": + description: "Invalid username/password supplied" + /user/logout: + get: + tags: + - "user" + summary: "Logs out current logged in user session" + description: "" + operationId: "logoutUser" + produces: + - "application/xml" + - "application/json" + parameters: [] + responses: + default: + description: "successful operation" + /user/{username}: + get: + tags: + - "user" + summary: "Get user by user name" + description: "" + operationId: "getUserByName" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "username" + in: "path" + description: "The name that needs to be fetched. Use user1 for testing. " + required: true + type: "string" + responses: + "200": + description: "successful operation" + schema: + $ref: "#/definitions/User" + "400": + description: "Invalid username supplied" + "404": + description: "User not found" + put: + tags: + - "user" + summary: "Updated user" + description: "This can only be done by the logged in user." + operationId: "updateUser" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "username" + in: "path" + description: "name that need to be updated" + required: true + type: "string" + - in: "body" + name: "body" + description: "Updated user object" + required: true + schema: + $ref: "#/definitions/User" + responses: + "400": + description: "Invalid user supplied" + "404": + description: "User not found" + delete: + tags: + - "user" + summary: "Delete user" + description: "This can only be done by the logged in user." + operationId: "deleteUser" + produces: + - "application/xml" + - "application/json" + parameters: + - name: "username" + in: "path" + description: "The name that needs to be deleted" + required: true + type: "string" + responses: + "400": + description: "Invalid username supplied" + "404": + description: "User not found" +securityDefinitions: + petstore_auth: + type: "oauth2" + authorizationUrl: "http://petstore.swagger.io/oauth/dialog" + flow: "implicit" + scopes: + write:pets: "modify pets in your account" + read:pets: "read your pets" + api_key: + type: "apiKey" + name: "api_key" + in: "header" +definitions: + Order: + type: "object" + properties: + id: + type: "integer" + format: "int64" + petId: + type: "integer" + format: "int64" + quantity: + type: "integer" + format: "int32" + shipDate: + type: "string" + format: "date-time" + status: + type: "string" + description: "Order Status" + enum: + - "placed" + - "approved" + - "delivered" + complete: + type: "boolean" + default: false + xml: + name: "Order" + Category: + type: "object" + properties: + id: + type: "integer" + format: "int64" + name: + type: "string" + xml: + name: "Category" + User: + type: "object" + properties: + id: + type: "integer" + format: "int64" + username: + type: "string" + firstName: + type: "string" + lastName: + type: "string" + email: + type: "string" + password: + type: "string" + phone: + type: "string" + userStatus: + type: "integer" + format: "int32" + description: "User Status" + xml: + name: "User" + Tag: + type: "object" + properties: + id: + type: "integer" + format: "int64" + name: + type: "string" + xml: + name: "Tag" + Pet: + type: "object" + required: + - "name" + - "photoUrls" + properties: + id: + type: "integer" + format: "int64" + category: + $ref: "#/definitions/Category" + name: + type: "string" + example: "doggie" + photoUrls: + type: "array" + xml: + name: "photoUrl" + wrapped: true + items: + type: "string" + tags: + type: "array" + xml: + name: "tag" + wrapped: true + items: + $ref: "#/definitions/Tag" + status: + type: "string" + description: "pet status in the store" + enum: + - "available" + - "pending" + - "sold" + xml: + name: "Pet" + ApiResponse: + type: "object" + properties: + code: + type: "integer" + format: "int32" + type: + type: "string" + message: + type: "string" +externalDocs: + description: "Find out more about Swagger" + url: "http://swagger.io" diff --git a/packages/apidom-ls/test/validate.ts b/packages/apidom-ls/test/validate.ts index 17f061c10d..5446403be6 100644 --- a/packages/apidom-ls/test/validate.ts +++ b/packages/apidom-ls/test/validate.ts @@ -3323,20 +3323,54 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - code: 15000, + range: { + start: { line: 0, character: 0 }, + end: { line: 0, character: 5 }, + }, message: 'Object includes not allowed fields', + severity: 1, + code: 15000, + source: 'apilint', + }, + { range: { - end: { - character: 5, - line: 0, - }, - start: { - character: 0, - line: 0, - }, + start: { line: 0, character: 0 }, + end: { line: 0, character: 5 }, + }, + message: "should always have a 'info' section", + severity: 1, + code: 3010101, + source: 'apilint', + data: { + quickFix: [ + { + message: "add 'info' section", + action: 'addChild', + snippetYaml: 'info: \n \n', + snippetJson: '"info": {\n \n },\n', + }, + ], + }, + }, + { + range: { + start: { line: 0, character: 0 }, + end: { line: 0, character: 5 }, }, + message: "should always have a 'paths' section", severity: 1, + code: 3010701, source: 'apilint', + data: { + quickFix: [ + { + message: "add 'paths' section", + action: 'addChild', + snippetYaml: 'paths: \n \n', + snippetJson: '"paths": {\n \n },\n', + }, + ], + }, }, ];