From 7710ef2fb865bf17d7a414ebcae57a7e9f1c4330 Mon Sep 17 00:00:00 2001 From: Krzysztof Kowalczyk Date: Thu, 21 Dec 2023 01:37:35 +0100 Subject: [PATCH 1/7] feat(apidom-ls): lint parameter defined in path template --- packages/apidom-ls/src/config/codes.ts | 3 + .../config/openapi/parameter/lint/index.ts | 2 + .../lint/is-defined-within-path-template.ts | 23 ++++ .../services/validation/linter-functions.ts | 26 ++++ ...eter-defined-within-path-template-2-0.yaml | 39 ++++++ ...eter-defined-within-path-template-3-0.yaml | 40 ++++++ ...eter-defined-within-path-template-3-1.yaml | 40 ++++++ packages/apidom-ls/test/validate.ts | 126 ++++++++++++++++++ 8 files changed, 299 insertions(+) create mode 100644 packages/apidom-ls/src/config/openapi/parameter/lint/is-defined-within-path-template.ts create mode 100644 packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml create mode 100644 packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml create mode 100644 packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml diff --git a/packages/apidom-ls/src/config/codes.ts b/packages/apidom-ls/src/config/codes.ts index 1aa0dd003a..4f9be6323f 100644 --- a/packages/apidom-ls/src/config/codes.ts +++ b/packages/apidom-ls/src/config/codes.ts @@ -701,6 +701,9 @@ enum ApilintCodes { OPENAPI2_OPERATION_FIELD_SECURITY_TYPE = 3081300, OPENAPI2_OPERATION_FIELD_SECURITY_ITEMS_TYPE, + OPENAPI2_PARAMETER = 3090000, + OPENAPI2_PARAMETER_FIELD_IS_DEFINED_WITHIN_PATH_TEMPLATE = 3090100, + OPENAPI3_0 = 5000000, OPENAPI3_0_OPENAPI_VALUE_PATTERN_3_0_0 = 5000100, diff --git a/packages/apidom-ls/src/config/openapi/parameter/lint/index.ts b/packages/apidom-ls/src/config/openapi/parameter/lint/index.ts index fc233c0fb3..f67817a8f7 100644 --- a/packages/apidom-ls/src/config/openapi/parameter/lint/index.ts +++ b/packages/apidom-ls/src/config/openapi/parameter/lint/index.ts @@ -21,6 +21,7 @@ import examplesMutuallyExclusiveLint from './examples--mutually-exclusive'; import contentValuesTypeLint from './content--values-type'; import contentAllowedFields3_0Lint from './content--allowed-fields-3-0'; import contentAllowedFields3_1Lint from './content--allowed-fields-3-1'; +import isDefinedWithinPathTemplate from './is-defined-within-path-template'; const lints = [ nameTypeLint, @@ -46,6 +47,7 @@ const lints = [ requiredFieldsLint, allowedFields3_0Lint, allowedFields3_1Lint, + isDefinedWithinPathTemplate, ]; export default lints; diff --git a/packages/apidom-ls/src/config/openapi/parameter/lint/is-defined-within-path-template.ts b/packages/apidom-ls/src/config/openapi/parameter/lint/is-defined-within-path-template.ts new file mode 100644 index 0000000000..80fa136eac --- /dev/null +++ b/packages/apidom-ls/src/config/openapi/parameter/lint/is-defined-within-path-template.ts @@ -0,0 +1,23 @@ +import { DiagnosticSeverity } from 'vscode-languageserver-types'; + +import ApilintCodes from '../../../codes'; +import { LinterMeta } from '../../../../apidom-language-types'; +import { OpenAPI2, OpenAPI3 } from '../../target-specs'; + +const isDefinedWithinPathTemplate: LinterMeta = { + code: ApilintCodes.OPENAPI2_PARAMETER_FIELD_IS_DEFINED_WITHIN_PATH_TEMPLATE, + source: 'apilint', + message: 'parameter is not defined within path template', + severity: DiagnosticSeverity.Error, + linterFunction: 'apilintOpenAPIParameterFieldIsDefinedWithinPathTemplate', + marker: 'value', + targetSpecs: [...OpenAPI2, ...OpenAPI3], + conditions: [ + { + function: 'apilintOpenAPIPathTemplateWellFormed', + params: [true], + }, + ], +}; + +export default isDefinedWithinPathTemplate; diff --git a/packages/apidom-ls/src/services/validation/linter-functions.ts b/packages/apidom-ls/src/services/validation/linter-functions.ts index b453b8ac6f..0c9aa4d02a 100644 --- a/packages/apidom-ls/src/services/validation/linter-functions.ts +++ b/packages/apidom-ls/src/services/validation/linter-functions.ts @@ -1069,4 +1069,30 @@ export const standardLinterfunctions: FunctionItem[] = [ return true; }, }, + { + functionName: 'apilintOpenAPIParameterFieldIsDefinedWithinPathTemplate', + function: (element: Element) => { + if (element.element === 'parameter') { + const allowedLocations = ['path', 'query']; + const parameterName = toValue((element as ObjectElement).get('name')); + const parameterLocation = toValue((element as ObjectElement).get('in')); + + const isAncestorOfOperationElement = (el: Element): boolean => + el.parent.parent.parent.element === 'operation'; + + if (isAncestorOfOperationElement(element)) { + const pathTemplate: string = toValue( + (element.parent.parent.parent.parent.parent.parent as MemberElement).key, + ); + + return ( + pathTemplate.includes(parameterName) && allowedLocations.includes(parameterLocation) + ); + + // TODO: handle when parameter is not ancestor of operation element + } + } + return true; + }, + }, ]; diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml new file mode 100644 index 0000000000..ad473f3aee --- /dev/null +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml @@ -0,0 +1,39 @@ +swagger: '2.0' +info: + title: Foo + version: 0.1.0 +parameters: + test_id: + name: test_id + in: path + required: true + schema: + type: string + format: uuid + title: Test Id +paths: + /foo/{bar_id}: + delete: + summary: Delete foo bar id + operationId: deleteFooBar + parameters: + - name: foo_id + in: path + required: true + schema: + type: string + format: uuid + title: Foo Id + - name: bar_id + in: path + required: true + schema: + type: string + format: uuid + title: Bar Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml new file mode 100644 index 0000000000..7052584be3 --- /dev/null +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml @@ -0,0 +1,40 @@ +openapi: 3.0.0 +info: + title: Foo + version: 0.1.0 +components: + parameters: + test_id: + name: test_id + in: path + required: true + schema: + type: string + format: uuid + title: Test Id +paths: + /foo/{bar_id}: + delete: + summary: Delete foo bar id + operationId: deleteFooBar + parameters: + - name: foo_id + in: path + required: true + schema: + type: string + format: uuid + title: Foo Id + - name: bar_id + in: path + required: true + schema: + type: string + format: uuid + title: Bar Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml new file mode 100644 index 0000000000..97c5d0ea9a --- /dev/null +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml @@ -0,0 +1,40 @@ +openapi: 3.1.0 +info: + title: Foo + version: 0.1.0 +components: + parameters: + test_id: + name: test_id + in: path + required: true + schema: + type: string + format: uuid + title: Test Id +paths: + /foo/{bar_id}: + delete: + summary: Delete foo bar id + operationId: deleteFooBar + parameters: + - name: foo_id + in: path + required: true + schema: + type: string + format: uuid + title: Foo Id + - name: bar_id + in: path + required: true + schema: + type: string + format: uuid + title: Bar Id + responses: + '200': + description: Successful Response + content: + application/json: + schema: {} diff --git a/packages/apidom-ls/test/validate.ts b/packages/apidom-ls/test/validate.ts index 38084135b7..45e49c3151 100644 --- a/packages/apidom-ls/test/validate.ts +++ b/packages/apidom-ls/test/validate.ts @@ -3549,4 +3549,130 @@ describe('apidom-ls-validate', function () { languageService.terminate(); }); + + it('oas 2.0 / yaml - parameter object should be defined within path template', async function () { + const validationContext: ValidationContext = { + comments: DiagnosticSeverity.Error, + maxNumberOfProblems: 100, + relatedInformation: false, + }; + + const spec = fs + .readFileSync( + path.join( + __dirname, + 'fixtures', + 'validation', + 'oas', + 'parameter-defined-within-path-template-2-0.yaml', + ), + ) + .toString(); + const doc: TextDocument = TextDocument.create( + 'foo://bar/parameter-defined-within-path-template-2-0.yaml', + 'yaml', + 0, + spec, + ); + + const languageService: LanguageService = getLanguageService(contextNoSchema); + + const result = await languageService.doValidation(doc, validationContext); + const expected: Diagnostic[] = [ + { + range: { start: { line: 19, character: 10 }, end: { line: 25, character: 25 } }, + message: 'parameter is not defined within path template', + severity: 1, + code: 3090100, + source: 'apilint', + }, + ]; + assert.deepEqual(result, expected as Diagnostic[]); + + languageService.terminate(); + }); + + it('oas 3.0 / yaml - parameter object should be defined within path template', async function () { + const validationContext: ValidationContext = { + comments: DiagnosticSeverity.Error, + maxNumberOfProblems: 100, + relatedInformation: false, + }; + + const spec = fs + .readFileSync( + path.join( + __dirname, + 'fixtures', + 'validation', + 'oas', + 'parameter-defined-within-path-template-3-0.yaml', + ), + ) + .toString(); + const doc: TextDocument = TextDocument.create( + 'foo://bar/parameter-defined-within-path-template-3-0.yaml', + 'yaml', + 0, + spec, + ); + + const languageService: LanguageService = getLanguageService(contextNoSchema); + + const result = await languageService.doValidation(doc, validationContext); + const expected: Diagnostic[] = [ + { + range: { start: { line: 20, character: 10 }, end: { line: 26, character: 25 } }, + message: 'parameter is not defined within path template', + severity: 1, + code: 3090100, + source: 'apilint', + }, + ]; + assert.deepEqual(result, expected as Diagnostic[]); + + languageService.terminate(); + }); + + it('oas 3.1 / yaml - parameter object should be defined within path template', async function () { + const validationContext: ValidationContext = { + comments: DiagnosticSeverity.Error, + maxNumberOfProblems: 100, + relatedInformation: false, + }; + + const spec = fs + .readFileSync( + path.join( + __dirname, + 'fixtures', + 'validation', + 'oas', + 'parameter-defined-within-path-template-3-1.yaml', + ), + ) + .toString(); + const doc: TextDocument = TextDocument.create( + 'foo://bar/parameter-defined-within-path-template-3-1.yaml', + 'yaml', + 0, + spec, + ); + + const languageService: LanguageService = getLanguageService(contextNoSchema); + + const result = await languageService.doValidation(doc, validationContext); + const expected: Diagnostic[] = [ + { + range: { start: { line: 20, character: 10 }, end: { line: 26, character: 25 } }, + message: 'parameter is not defined within path template', + severity: 1, + code: 3090100, + source: 'apilint', + }, + ]; + assert.deepEqual(result, expected as Diagnostic[]); + + languageService.terminate(); + }); }); From 8c5af8cfdef51de7ef7dc2dc685a61c9b9fe4190 Mon Sep 17 00:00:00 2001 From: Krzysztof Kowalczyk Date: Fri, 22 Dec 2023 11:38:30 +0100 Subject: [PATCH 2/7] feat(apidom-ls): fix oas-3.0-petsotre.yaml --- .../src/services/validation/linter-functions.ts | 8 +++++--- .../validation/oas/valid/oas-3.0-petstore.yaml | 14 +++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/apidom-ls/src/services/validation/linter-functions.ts b/packages/apidom-ls/src/services/validation/linter-functions.ts index 0c9aa4d02a..1d747aaee3 100644 --- a/packages/apidom-ls/src/services/validation/linter-functions.ts +++ b/packages/apidom-ls/src/services/validation/linter-functions.ts @@ -1085,9 +1085,11 @@ export const standardLinterfunctions: FunctionItem[] = [ (element.parent.parent.parent.parent.parent.parent as MemberElement).key, ); - return ( - pathTemplate.includes(parameterName) && allowedLocations.includes(parameterLocation) - ); + if (!allowedLocations.includes(parameterLocation)) { + return true; + } + + return pathTemplate.includes(parameterName); // TODO: handle when parameter is not ancestor of operation element } diff --git a/packages/apidom-ls/test/fixtures/validation/oas/valid/oas-3.0-petstore.yaml b/packages/apidom-ls/test/fixtures/validation/oas/valid/oas-3.0-petstore.yaml index 8497a89124..b77889633a 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/valid/oas-3.0-petstore.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/valid/oas-3.0-petstore.yaml @@ -6,11 +6,11 @@ info: Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach! You can now help us improve the API whether it's by making changes to the definition itself or to the code. That way, with time, we can improve the API in general, and expose some of the new features in OAS3. - + Some useful links: - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore) - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) - + termsOfService: http://swagger.io/terms/ contact: email: apiteam@swagger.io @@ -112,7 +112,7 @@ paths: - petstore_auth: - write:pets - read:pets - /pet/findByStatus: + /pet/findByStatus?status=status: get: tags: - pet @@ -152,7 +152,7 @@ paths: - petstore_auth: - write:pets - read:pets - /pet/findByTags: + /pet/findByTags?tags=tags: get: tags: - pet @@ -189,7 +189,7 @@ paths: - petstore_auth: - write:pets - read:pets - /pet/{petId}: + /pet/{petId}?name=name&status=status: get: tags: - pet @@ -281,7 +281,7 @@ paths: - petstore_auth: - write:pets - read:pets - /pet/{petId}/uploadImage: + /pet/{petId}/uploadImage?additionalMetadata=additionalMetadata: post: tags: - pet @@ -468,7 +468,7 @@ paths: $ref: '#/components/schemas/User' default: description: successful operation - /user/login: + /user/login?username=username&password=password: get: tags: - user From d8d8c9810ff7910737b09a32b2f82a33d95157f1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kowalczyk Date: Tue, 2 Jan 2024 15:56:54 +0100 Subject: [PATCH 3/7] feat(apidom-ls): after comment changes --- .../services/validation/linter-functions.ts | 38 +++++++++-------- ...eter-defined-within-path-template-2-0.yaml | 41 ++++++++----------- ...eter-defined-within-path-template-3-0.yaml | 40 +++++++----------- ...eter-defined-within-path-template-3-1.yaml | 40 +++++++----------- .../oas/valid/oas-3.0-petstore.yaml | 14 +++---- packages/apidom-ls/test/validate.ts | 12 +++--- 6 files changed, 81 insertions(+), 104 deletions(-) diff --git a/packages/apidom-ls/src/services/validation/linter-functions.ts b/packages/apidom-ls/src/services/validation/linter-functions.ts index 759301a6b8..47419a8c7f 100644 --- a/packages/apidom-ls/src/services/validation/linter-functions.ts +++ b/packages/apidom-ls/src/services/validation/linter-functions.ts @@ -9,9 +9,10 @@ import { ArraySlice, ObjectElement, isArrayElement, + includesClasses, } from '@swagger-api/apidom-core'; import { CompletionItem } from 'vscode-languageserver-types'; -import { test, resolve } from 'openapi-path-templating'; +import { test, resolve, parse } from 'openapi-path-templating'; // eslint-disable-next-line import/no-cycle import { @@ -1072,27 +1073,30 @@ export const standardLinterfunctions: FunctionItem[] = [ functionName: 'apilintOpenAPIParameterFieldIsDefinedWithinPathTemplate', function: (element: Element) => { if (element.element === 'parameter') { - const allowedLocations = ['path', 'query']; - const parameterToValue: ObjectElement = toValue(element); - const parameterName = parameterToValue.get('name'); - const parameterLocation = parameterToValue.get('in'); + const parameterName = toValue((element as ObjectElement).get('name')); + const parameterLocation = toValue((element as ObjectElement).get('in')); + const isInPathItemElement = + isArrayElement(element.parent) && + includesClasses(['path-item-parameters'], element.parent); - const isChildOfOperationElement = (el: Element): boolean => - el.parent.parent.parent.element === 'operation'; + if (parameterLocation !== 'path' || !isInPathItemElement) { + return true; + } - if (isChildOfOperationElement(element)) { - const pathTemplate: string = toValue( - (element.parent.parent.parent.parent.parent.parent as MemberElement).key, - ); + const pathItemElement = element.parent.parent.parent; - if (!allowedLocations.includes(parameterLocation)) { - return true; - } + const pathTemplateASTIncludesParameter = (ast: [string, string][]) => + ast.findIndex( + ([name, value]) => name === 'template-expression-param-name' && value === parameterName, + ) > -1; - return pathTemplate.includes(parameterName); + const pathTemplate = toValue(pathItemElement.meta.get('path')); + const parseResult = parse(pathTemplate); + const parts: [string, string][] = []; - // TODO: handle case when parameter is not child of operation element - } + parseResult.ast.translate(parts); + + return pathTemplateASTIncludesParameter(parts); } return true; }, diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml index ad473f3aee..4da1a304a4 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml @@ -2,38 +2,31 @@ swagger: '2.0' info: title: Foo version: 0.1.0 -parameters: - test_id: - name: test_id - in: path - required: true - schema: - type: string - format: uuid - title: Test Id paths: /foo/{bar_id}: delete: summary: Delete foo bar id operationId: deleteFooBar - parameters: - - name: foo_id - in: path - required: true - schema: - type: string - format: uuid - title: Foo Id - - name: bar_id - in: path - required: true - schema: - type: string - format: uuid - title: Bar Id responses: '200': description: Successful Response content: application/json: schema: {} + parameters: + - name: foo_id + in: path + required: true + type: string + schema: + type: string + format: uuid + title: Foo Id + - name: bar_id + in: path + required: true + type: string + schema: + type: string + format: uuid + title: Foo Id diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml index 7052584be3..f81f343e96 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml @@ -2,39 +2,29 @@ openapi: 3.0.0 info: title: Foo version: 0.1.0 -components: - parameters: - test_id: - name: test_id - in: path - required: true - schema: - type: string - format: uuid - title: Test Id paths: /foo/{bar_id}: delete: summary: Delete foo bar id operationId: deleteFooBar - parameters: - - name: foo_id - in: path - required: true - schema: - type: string - format: uuid - title: Foo Id - - name: bar_id - in: path - required: true - schema: - type: string - format: uuid - title: Bar Id responses: '200': description: Successful Response content: application/json: schema: {} + parameters: + - name: foo_id + in: path + required: true + schema: + type: string + format: uuid + title: Foo Id + - name: bar_id + in: path + required: true + schema: + type: string + format: uuid + title: Bar Id diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml index 97c5d0ea9a..10ba12c621 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml @@ -2,39 +2,29 @@ openapi: 3.1.0 info: title: Foo version: 0.1.0 -components: - parameters: - test_id: - name: test_id - in: path - required: true - schema: - type: string - format: uuid - title: Test Id paths: /foo/{bar_id}: delete: summary: Delete foo bar id operationId: deleteFooBar - parameters: - - name: foo_id - in: path - required: true - schema: - type: string - format: uuid - title: Foo Id - - name: bar_id - in: path - required: true - schema: - type: string - format: uuid - title: Bar Id responses: '200': description: Successful Response content: application/json: schema: {} + parameters: + - name: foo_id + in: path + required: true + schema: + type: string + format: uuid + title: Foo Id + - name: bar_id + in: path + required: true + schema: + type: string + format: uuid + title: Bar Id diff --git a/packages/apidom-ls/test/fixtures/validation/oas/valid/oas-3.0-petstore.yaml b/packages/apidom-ls/test/fixtures/validation/oas/valid/oas-3.0-petstore.yaml index b77889633a..8497a89124 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/valid/oas-3.0-petstore.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/valid/oas-3.0-petstore.yaml @@ -6,11 +6,11 @@ info: Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach! You can now help us improve the API whether it's by making changes to the definition itself or to the code. That way, with time, we can improve the API in general, and expose some of the new features in OAS3. - + Some useful links: - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore) - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) - + termsOfService: http://swagger.io/terms/ contact: email: apiteam@swagger.io @@ -112,7 +112,7 @@ paths: - petstore_auth: - write:pets - read:pets - /pet/findByStatus?status=status: + /pet/findByStatus: get: tags: - pet @@ -152,7 +152,7 @@ paths: - petstore_auth: - write:pets - read:pets - /pet/findByTags?tags=tags: + /pet/findByTags: get: tags: - pet @@ -189,7 +189,7 @@ paths: - petstore_auth: - write:pets - read:pets - /pet/{petId}?name=name&status=status: + /pet/{petId}: get: tags: - pet @@ -281,7 +281,7 @@ paths: - petstore_auth: - write:pets - read:pets - /pet/{petId}/uploadImage?additionalMetadata=additionalMetadata: + /pet/{petId}/uploadImage: post: tags: - pet @@ -468,7 +468,7 @@ paths: $ref: '#/components/schemas/User' default: description: successful operation - /user/login?username=username&password=password: + /user/login: get: tags: - user diff --git a/packages/apidom-ls/test/validate.ts b/packages/apidom-ls/test/validate.ts index 45229175ce..1926f37276 100644 --- a/packages/apidom-ls/test/validate.ts +++ b/packages/apidom-ls/test/validate.ts @@ -3580,10 +3580,10 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 19, character: 10 }, end: { line: 25, character: 25 } }, + range: { start: { line: 16, character: 8 }, end: { line: 23, character: 23 } }, message: 'parameter is not defined within path template', severity: 1, - code: 3090100, + code: 3102000, source: 'apilint', }, ]; @@ -3622,10 +3622,10 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 20, character: 10 }, end: { line: 26, character: 25 } }, + range: { start: { line: 16, character: 8 }, end: { line: 22, character: 23 } }, message: 'parameter is not defined within path template', severity: 1, - code: 3090100, + code: 3102000, source: 'apilint', }, ]; @@ -3664,10 +3664,10 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 20, character: 10 }, end: { line: 26, character: 25 } }, + range: { start: { line: 16, character: 8 }, end: { line: 22, character: 23 } }, message: 'parameter is not defined within path template', severity: 1, - code: 3090100, + code: 3102000, source: 'apilint', }, ]; From 4192c5c1d5cd83bb0c58af353e3c8a53a6f70c98 Mon Sep 17 00:00:00 2001 From: Krzysztof Kowalczyk Date: Wed, 3 Jan 2024 15:39:26 +0100 Subject: [PATCH 4/7] feat(apidom-ls): implementation changes --- .../services/validation/linter-functions.ts | 36 +++++++++++++------ ...eter-defined-within-path-template-2-0.yaml | 9 +++++ ...eter-defined-within-path-template-3-0.yaml | 8 +++++ ...eter-defined-within-path-template-3-1.yaml | 8 +++++ packages/apidom-ls/test/validate.ts | 36 +++++++++++++++++-- 5 files changed, 84 insertions(+), 13 deletions(-) diff --git a/packages/apidom-ls/src/services/validation/linter-functions.ts b/packages/apidom-ls/src/services/validation/linter-functions.ts index 47419a8c7f..1751e617e1 100644 --- a/packages/apidom-ls/src/services/validation/linter-functions.ts +++ b/packages/apidom-ls/src/services/validation/linter-functions.ts @@ -1078,25 +1078,41 @@ export const standardLinterfunctions: FunctionItem[] = [ const isInPathItemElement = isArrayElement(element.parent) && includesClasses(['path-item-parameters'], element.parent); + const isChildOfOperationElement = element.parent.parent.parent.element === 'operation'; - if (parameterLocation !== 'path' || !isInPathItemElement) { - return true; - } - - const pathItemElement = element.parent.parent.parent; + const getAST = (pathTemplate: string) => { + const parseResult = parse(pathTemplate); + const parts: [string, string][] = []; + parseResult.ast.translate(parts); + return parts; + }; const pathTemplateASTIncludesParameter = (ast: [string, string][]) => ast.findIndex( ([name, value]) => name === 'template-expression-param-name' && value === parameterName, ) > -1; - const pathTemplate = toValue(pathItemElement.meta.get('path')); - const parseResult = parse(pathTemplate); - const parts: [string, string][] = []; + const getPathTemplate = (): string => { + if (isInPathItemElement) { + return toValue(element.parent.parent.parent.meta.get('path')); + } + if (isChildOfOperationElement) { + return toValue( + (element.parent.parent.parent.parent.parent.parent as MemberElement).key, + ); + } + return ''; + }; + + if (parameterLocation !== 'path') { + return true; + } - parseResult.ast.translate(parts); + if (isInPathItemElement || isChildOfOperationElement) { + return pathTemplateASTIncludesParameter(getAST(getPathTemplate())); + } - return pathTemplateASTIncludesParameter(parts); + return true; } return true; }, diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml index 4da1a304a4..ff120fda83 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml @@ -7,6 +7,15 @@ paths: delete: summary: Delete foo bar id operationId: deleteFooBar + parameters: + - name: test_id + in: path + required: true + type: string + schema: + type: string + format: uuid + title: Test Id responses: '200': description: Successful Response diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml index f81f343e96..9158f0fe31 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml @@ -7,6 +7,14 @@ paths: delete: summary: Delete foo bar id operationId: deleteFooBar + parameters: + - name: test_id + in: path + required: true + schema: + type: string + format: uuid + title: Test Id responses: '200': description: Successful Response diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml index 10ba12c621..0b74d9c7e6 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml @@ -7,6 +7,14 @@ paths: delete: summary: Delete foo bar id operationId: deleteFooBar + parameters: + - name: test_id + in: path + required: true + schema: + type: string + format: uuid + title: Test Id responses: '200': description: Successful Response diff --git a/packages/apidom-ls/test/validate.ts b/packages/apidom-ls/test/validate.ts index 1926f37276..c60288a43a 100644 --- a/packages/apidom-ls/test/validate.ts +++ b/packages/apidom-ls/test/validate.ts @@ -3580,7 +3580,14 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 16, character: 8 }, end: { line: 23, character: 23 } }, + range: { start: { line: 10, character: 8 }, end: { line: 17, character: 24 } }, + message: 'parameter is not defined within path template', + severity: 1, + code: 3102000, + source: 'apilint', + }, + { + range: { start: { line: 25, character: 8 }, end: { line: 32, character: 23 } }, message: 'parameter is not defined within path template', severity: 1, code: 3102000, @@ -3622,7 +3629,14 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 16, character: 8 }, end: { line: 22, character: 23 } }, + range: { start: { line: 10, character: 8 }, end: { line: 16, character: 24 } }, + message: 'parameter is not defined within path template', + severity: 1, + code: 3102000, + source: 'apilint', + }, + { + range: { start: { line: 24, character: 8 }, end: { line: 30, character: 23 } }, message: 'parameter is not defined within path template', severity: 1, code: 3102000, @@ -3664,12 +3678,28 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 16, character: 8 }, end: { line: 22, character: 23 } }, + range: { start: { line: 10, character: 8 }, end: { line: 16, character: 24 } }, message: 'parameter is not defined within path template', severity: 1, code: 3102000, source: 'apilint', }, + { + code: 3102000, + message: 'parameter is not defined within path template', + range: { + end: { + character: 23, + line: 30, + }, + start: { + character: 8, + line: 24, + }, + }, + severity: 1, + source: 'apilint', + }, ]; assert.deepEqual(result, expected as Diagnostic[]); From 12223e364b6e47c63c18d6f884c54f25ab187804 Mon Sep 17 00:00:00 2001 From: Krzysztof Kowalczyk Date: Wed, 3 Jan 2024 17:36:30 +0100 Subject: [PATCH 5/7] feat(apidom-ls): remove Operation.parameter handling --- .../services/validation/linter-functions.ts | 47 +++++++------------ ...eter-defined-within-path-template-2-0.yaml | 9 ---- ...eter-defined-within-path-template-3-0.yaml | 8 ---- ...eter-defined-within-path-template-3-1.yaml | 8 ---- packages/apidom-ls/test/validate.ts | 36 ++------------ 5 files changed, 21 insertions(+), 87 deletions(-) diff --git a/packages/apidom-ls/src/services/validation/linter-functions.ts b/packages/apidom-ls/src/services/validation/linter-functions.ts index 1751e617e1..979fc5c8db 100644 --- a/packages/apidom-ls/src/services/validation/linter-functions.ts +++ b/packages/apidom-ls/src/services/validation/linter-functions.ts @@ -1073,46 +1073,35 @@ export const standardLinterfunctions: FunctionItem[] = [ functionName: 'apilintOpenAPIParameterFieldIsDefinedWithinPathTemplate', function: (element: Element) => { if (element.element === 'parameter') { - const parameterName = toValue((element as ObjectElement).get('name')); const parameterLocation = toValue((element as ObjectElement).get('in')); const isInPathItemElement = isArrayElement(element.parent) && includesClasses(['path-item-parameters'], element.parent); - const isChildOfOperationElement = element.parent.parent.parent.element === 'operation'; - - const getAST = (pathTemplate: string) => { - const parseResult = parse(pathTemplate); - const parts: [string, string][] = []; - parseResult.ast.translate(parts); - return parts; - }; - const pathTemplateASTIncludesParameter = (ast: [string, string][]) => - ast.findIndex( - ([name, value]) => name === 'template-expression-param-name' && value === parameterName, - ) > -1; + if (!isInPathItemElement || parameterLocation !== 'path') { + return true; + } - const getPathTemplate = (): string => { - if (isInPathItemElement) { - return toValue(element.parent.parent.parent.meta.get('path')); - } - if (isChildOfOperationElement) { - return toValue( - (element.parent.parent.parent.parent.parent.parent as MemberElement).key, - ); - } - return ''; - }; + const pathItemElement = element.parent.parent.parent; + const isPathItemPartOfPathTemplating = isStringElement(pathItemElement.meta.get('path')); - if (parameterLocation !== 'path') { + if (!isPathItemPartOfPathTemplating) { return true; } - if (isInPathItemElement || isChildOfOperationElement) { - return pathTemplateASTIncludesParameter(getAST(getPathTemplate())); - } + const pathTemplate = toValue(pathItemElement.meta.get('path')); + const parameterName = toValue((element as ObjectElement).get('name')); - return true; + const parseResult = parse(pathTemplate); + const parts: [string, string][] = []; + parseResult.ast.translate(parts); + + const pathTemplateASTIncludesParameter = (ast: [string, string][]) => + ast.findIndex( + ([name, value]) => name === 'template-expression-param-name' && value === parameterName, + ) > -1; + + return pathTemplateASTIncludesParameter(parts); } return true; }, diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml index ff120fda83..4da1a304a4 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml @@ -7,15 +7,6 @@ paths: delete: summary: Delete foo bar id operationId: deleteFooBar - parameters: - - name: test_id - in: path - required: true - type: string - schema: - type: string - format: uuid - title: Test Id responses: '200': description: Successful Response diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml index 9158f0fe31..f81f343e96 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml @@ -7,14 +7,6 @@ paths: delete: summary: Delete foo bar id operationId: deleteFooBar - parameters: - - name: test_id - in: path - required: true - schema: - type: string - format: uuid - title: Test Id responses: '200': description: Successful Response diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml index 0b74d9c7e6..10ba12c621 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml @@ -7,14 +7,6 @@ paths: delete: summary: Delete foo bar id operationId: deleteFooBar - parameters: - - name: test_id - in: path - required: true - schema: - type: string - format: uuid - title: Test Id responses: '200': description: Successful Response diff --git a/packages/apidom-ls/test/validate.ts b/packages/apidom-ls/test/validate.ts index c60288a43a..1926f37276 100644 --- a/packages/apidom-ls/test/validate.ts +++ b/packages/apidom-ls/test/validate.ts @@ -3580,14 +3580,7 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 10, character: 8 }, end: { line: 17, character: 24 } }, - message: 'parameter is not defined within path template', - severity: 1, - code: 3102000, - source: 'apilint', - }, - { - range: { start: { line: 25, character: 8 }, end: { line: 32, character: 23 } }, + range: { start: { line: 16, character: 8 }, end: { line: 23, character: 23 } }, message: 'parameter is not defined within path template', severity: 1, code: 3102000, @@ -3629,14 +3622,7 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 10, character: 8 }, end: { line: 16, character: 24 } }, - message: 'parameter is not defined within path template', - severity: 1, - code: 3102000, - source: 'apilint', - }, - { - range: { start: { line: 24, character: 8 }, end: { line: 30, character: 23 } }, + range: { start: { line: 16, character: 8 }, end: { line: 22, character: 23 } }, message: 'parameter is not defined within path template', severity: 1, code: 3102000, @@ -3678,28 +3664,12 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 10, character: 8 }, end: { line: 16, character: 24 } }, + range: { start: { line: 16, character: 8 }, end: { line: 22, character: 23 } }, message: 'parameter is not defined within path template', severity: 1, code: 3102000, source: 'apilint', }, - { - code: 3102000, - message: 'parameter is not defined within path template', - range: { - end: { - character: 23, - line: 30, - }, - start: { - character: 8, - line: 24, - }, - }, - severity: 1, - source: 'apilint', - }, ]; assert.deepEqual(result, expected as Diagnostic[]); From 4d2dc7428837e4505965eee0585c2dfd414aa466 Mon Sep 17 00:00:00 2001 From: Krzysztof Kowalczyk Date: Thu, 4 Jan 2024 09:54:19 +0100 Subject: [PATCH 6/7] feat(apidom-ls): improve fixtures --- ...eter-defined-within-path-template-2-0.yaml | 14 +++- ...eter-defined-within-path-template-3-0.yaml | 48 +++++++++++++- ...eter-defined-within-path-template-3-1.yaml | 66 ++++++++++++++++++- packages/apidom-ls/test/validate.ts | 6 +- 4 files changed, 125 insertions(+), 9 deletions(-) diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml index 4da1a304a4..c3ec80bfb4 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-2-0.yaml @@ -2,11 +2,21 @@ swagger: '2.0' info: title: Foo version: 0.1.0 +parameters: + test_id: + name: test_id + in: path + required: true + type: string + schema: + type: string + format: uuid + title: Test Id paths: /foo/{bar_id}: delete: - summary: Delete foo bar id - operationId: deleteFooBar + summary: Delete bar id + operationId: deleteBar responses: '200': description: Successful Response diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml index f81f343e96..35837bb26d 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-0.yaml @@ -2,11 +2,21 @@ openapi: 3.0.0 info: title: Foo version: 0.1.0 +components: + parameters: + test_id: + name: test_id + in: path + required: true + schema: + type: string + format: uuid + title: Test Id paths: /foo/{bar_id}: delete: - summary: Delete foo bar id - operationId: deleteFooBar + summary: Delete bar id + operationId: deleteBar responses: '200': description: Successful Response @@ -28,3 +38,37 @@ paths: type: string format: uuid title: Bar Id + /subscribe: + post: + description: subscribes a client + responses: + '201': + description: subscription successfully created + content: + application/json: + schema: {} + callbacks: + onData: + '{$request.query.callbackUrl}/data': + post: + requestBody: + description: subscription payload + content: + application/json: + schema: + type: object + properties: + userData: + type: string + parameters: + - name: baz_id + in: path + required: true + schema: + type: string + format: uuid + title: Baz Id + responses: + '202': + description: "OK" + diff --git a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml index 10ba12c621..02f49f282b 100644 --- a/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml +++ b/packages/apidom-ls/test/fixtures/validation/oas/parameter-defined-within-path-template-3-1.yaml @@ -2,11 +2,40 @@ openapi: 3.1.0 info: title: Foo version: 0.1.0 +components: + parameters: + test_id: + name: test_id + in: path + required: true + schema: + type: string + format: uuid + title: Test Id +webhooks: + newWebhook: + post: + requestBody: + description: new webook + content: + application/json: + schema: {} + parameters: + - name: hook_id + in: path + required: true + schema: + type: string + format: uuid + title: Hook Id + responses: + "200": + description: OK paths: /foo/{bar_id}: delete: - summary: Delete foo bar id - operationId: deleteFooBar + summary: Delete bar id + operationId: deleteBar responses: '200': description: Successful Response @@ -28,3 +57,36 @@ paths: type: string format: uuid title: Bar Id + /subscribe: + post: + description: subscribes a client + responses: + '201': + description: subscription successfully created + content: + application/json: + schema: {} + callbacks: + onData: + '{$request.query.callbackUrl}/data': + post: + requestBody: + description: subscription payload + content: + application/json: + schema: + type: object + properties: + userData: + type: string + parameters: + - name: baz_id + in: path + required: true + schema: + type: string + format: uuid + title: Baz Id + responses: + '202': + description: "OK" diff --git a/packages/apidom-ls/test/validate.ts b/packages/apidom-ls/test/validate.ts index 1926f37276..821fdb5b58 100644 --- a/packages/apidom-ls/test/validate.ts +++ b/packages/apidom-ls/test/validate.ts @@ -3580,7 +3580,7 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 16, character: 8 }, end: { line: 23, character: 23 } }, + range: { start: { line: 26, character: 8 }, end: { line: 33, character: 23 } }, message: 'parameter is not defined within path template', severity: 1, code: 3102000, @@ -3622,7 +3622,7 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 16, character: 8 }, end: { line: 22, character: 23 } }, + range: { start: { line: 26, character: 8 }, end: { line: 32, character: 23 } }, message: 'parameter is not defined within path template', severity: 1, code: 3102000, @@ -3664,7 +3664,7 @@ describe('apidom-ls-validate', function () { const result = await languageService.doValidation(doc, validationContext); const expected: Diagnostic[] = [ { - range: { start: { line: 16, character: 8 }, end: { line: 22, character: 23 } }, + range: { start: { line: 45, character: 8 }, end: { line: 51, character: 23 } }, message: 'parameter is not defined within path template', severity: 1, code: 3102000, From 277db11273a92ac6285970336fb57a82cc7fe4e9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kowalczyk Date: Thu, 4 Jan 2024 10:04:37 +0100 Subject: [PATCH 7/7] feat(apidom-ls): after comment changes --- packages/apidom-ls/src/config/codes.ts | 2 +- ...n-path-template.ts => in-path-template.ts} | 14 ++++------- .../config/openapi/parameter/lint/index.ts | 4 ++-- .../services/validation/linter-functions.ts | 24 +++++++++---------- 4 files changed, 18 insertions(+), 26 deletions(-) rename packages/apidom-ls/src/config/openapi/parameter/lint/{is-defined-within-path-template.ts => in-path-template.ts} (53%) diff --git a/packages/apidom-ls/src/config/codes.ts b/packages/apidom-ls/src/config/codes.ts index e5f0a86878..1337359327 100644 --- a/packages/apidom-ls/src/config/codes.ts +++ b/packages/apidom-ls/src/config/codes.ts @@ -734,7 +734,7 @@ enum ApilintCodes { OPENAPI2_PARAMETER_FIELD_UNIQUE_ITEMS_TYPE = 3101700, OPENAPI2_PARAMETER_FIELD_ENUM_TYPE = 3101800, OPENAPI2_PARAMETER_FIELD_MULTIPLE_OF_TYPE = 3101900, - OPENAPI2_PARAMETER_FIELD_IS_DEFINED_WITHIN_PATH_TEMPLATE = 3102000, + OPENAPI2_PARAMETER_FIELD_IN_PATH_TEMPLATE = 3102000, OPENAPI3_0 = 5000000, diff --git a/packages/apidom-ls/src/config/openapi/parameter/lint/is-defined-within-path-template.ts b/packages/apidom-ls/src/config/openapi/parameter/lint/in-path-template.ts similarity index 53% rename from packages/apidom-ls/src/config/openapi/parameter/lint/is-defined-within-path-template.ts rename to packages/apidom-ls/src/config/openapi/parameter/lint/in-path-template.ts index 80fa136eac..bcc6e1a751 100644 --- a/packages/apidom-ls/src/config/openapi/parameter/lint/is-defined-within-path-template.ts +++ b/packages/apidom-ls/src/config/openapi/parameter/lint/in-path-template.ts @@ -4,20 +4,14 @@ import ApilintCodes from '../../../codes'; import { LinterMeta } from '../../../../apidom-language-types'; import { OpenAPI2, OpenAPI3 } from '../../target-specs'; -const isDefinedWithinPathTemplate: LinterMeta = { - code: ApilintCodes.OPENAPI2_PARAMETER_FIELD_IS_DEFINED_WITHIN_PATH_TEMPLATE, +const inPathTemplateLint: LinterMeta = { + code: ApilintCodes.OPENAPI2_PARAMETER_FIELD_IN_PATH_TEMPLATE, source: 'apilint', message: 'parameter is not defined within path template', severity: DiagnosticSeverity.Error, - linterFunction: 'apilintOpenAPIParameterFieldIsDefinedWithinPathTemplate', + linterFunction: 'apilintOpenAPIParameterInPathTemplate', marker: 'value', targetSpecs: [...OpenAPI2, ...OpenAPI3], - conditions: [ - { - function: 'apilintOpenAPIPathTemplateWellFormed', - params: [true], - }, - ], }; -export default isDefinedWithinPathTemplate; +export default inPathTemplateLint; diff --git a/packages/apidom-ls/src/config/openapi/parameter/lint/index.ts b/packages/apidom-ls/src/config/openapi/parameter/lint/index.ts index 6c9aaaa1f2..8177ed4343 100644 --- a/packages/apidom-ls/src/config/openapi/parameter/lint/index.ts +++ b/packages/apidom-ls/src/config/openapi/parameter/lint/index.ts @@ -40,7 +40,7 @@ import minLengthTypeLint from './min-length--type'; import uniqueItemsTypeLint from './unique-items--type'; import enumTypeLint from './enum--type'; import multipleOfTypeLint from './multiple-of--type'; -import isDefinedWithinPathTemplate from './is-defined-within-path-template'; +import inPathTemplateLint from './in-path-template'; const lints = [ nameTypeLint, @@ -85,7 +85,7 @@ const lints = [ allowedFields2_0Lint, allowedFields3_0Lint, allowedFields3_1Lint, - isDefinedWithinPathTemplate, + inPathTemplateLint, ]; export default lints; diff --git a/packages/apidom-ls/src/services/validation/linter-functions.ts b/packages/apidom-ls/src/services/validation/linter-functions.ts index 979fc5c8db..17ef0fdc23 100644 --- a/packages/apidom-ls/src/services/validation/linter-functions.ts +++ b/packages/apidom-ls/src/services/validation/linter-functions.ts @@ -1070,38 +1070,36 @@ export const standardLinterfunctions: FunctionItem[] = [ }, }, { - functionName: 'apilintOpenAPIParameterFieldIsDefinedWithinPathTemplate', + functionName: 'apilintOpenAPIParameterInPathTemplate', function: (element: Element) => { if (element.element === 'parameter') { const parameterLocation = toValue((element as ObjectElement).get('in')); + + if (parameterLocation !== 'path') return true; + const isInPathItemElement = isArrayElement(element.parent) && includesClasses(['path-item-parameters'], element.parent); - if (!isInPathItemElement || parameterLocation !== 'path') { - return true; - } + if (!isInPathItemElement) return true; const pathItemElement = element.parent.parent.parent; const isPathItemPartOfPathTemplating = isStringElement(pathItemElement.meta.get('path')); - if (!isPathItemPartOfPathTemplating) { - return true; - } + if (!isPathItemPartOfPathTemplating) return true; const pathTemplate = toValue(pathItemElement.meta.get('path')); const parameterName = toValue((element as ObjectElement).get('name')); const parseResult = parse(pathTemplate); + if (!parseResult.result.success) return true; + const parts: [string, string][] = []; parseResult.ast.translate(parts); - const pathTemplateASTIncludesParameter = (ast: [string, string][]) => - ast.findIndex( - ([name, value]) => name === 'template-expression-param-name' && value === parameterName, - ) > -1; - - return pathTemplateASTIncludesParameter(parts); + return parts.some( + ([name, value]) => name === 'template-expression-param-name' && value === parameterName, + ); } return true; },