Skip to content

Commit

Permalink
feat(ls): add lint rule for OpenAPI Parameter defined in path template (
Browse files Browse the repository at this point in the history
#3571)

Refs #3546
  • Loading branch information
kowalczyk-krzysztof authored Jan 4, 2024
1 parent df7ab18 commit 2ab2840
Show file tree
Hide file tree
Showing 8 changed files with 391 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/apidom-ls/src/config/codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +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_IN_PATH_TEMPLATE = 3102000,

OPENAPI2_ITEMS = 3110000,
OPENAPI2_ITEMS_FIELD_TYPE_EQUALS = 3110100,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { DiagnosticSeverity } from 'vscode-languageserver-types';

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

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: 'apilintOpenAPIParameterInPathTemplate',
marker: 'value',
targetSpecs: [...OpenAPI2, ...OpenAPI3],
};

export default inPathTemplateLint;
2 changes: 2 additions & 0 deletions packages/apidom-ls/src/config/openapi/parameter/lint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +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 inPathTemplateLint from './in-path-template';

const lints = [
nameTypeLint,
Expand Down Expand Up @@ -84,6 +85,7 @@ const lints = [
allowedFields2_0Lint,
allowedFields3_0Lint,
allowedFields3_1Lint,
inPathTemplateLint,
];

export default lints;
38 changes: 37 additions & 1 deletion packages/apidom-ls/src/services/validation/linter-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -1068,4 +1069,39 @@ export const standardLinterfunctions: FunctionItem[] = [
return true;
},
},
{
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) return true;

const pathItemElement = element.parent.parent.parent;
const isPathItemPartOfPathTemplating = isStringElement(pathItemElement.meta.get('path'));

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);

return parts.some(
([name, value]) => name === 'template-expression-param-name' && value === parameterName,
);
}
return true;
},
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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 bar id
operationId: deleteBar
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
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 bar id
operationId: deleteBar
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
/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"

Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
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 bar id
operationId: deleteBar
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
/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"
Loading

0 comments on commit 2ab2840

Please sign in to comment.