Skip to content

Commit d639855

Browse files
feat: add ref not used lint rule
1 parent 1eb1a38 commit d639855

File tree

7 files changed

+85
-0
lines changed

7 files changed

+85
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,7 @@ enum ApilintCodes {
843843

844844
OPENAPI2_REFERENCE = 3240000,
845845
OPENAPI2_REFERENCE_FIELD_$REF_FORMAT_URI = 3240100,
846+
OPENAPI2_REFERENCE_NOT_USED = 3240300,
846847

847848
OPENAPI3_0 = 5000000,
848849

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { DiagnosticSeverity } from 'vscode-languageserver-types';
2+
3+
import ApilintCodes from '../../../codes.ts';
4+
import { LinterMeta } from '../../../../apidom-language-types.ts';
5+
import { OpenAPI2, OpenAPI3, OpenAPI31 } from '../../../openapi/target-specs.ts';
6+
7+
const $refNotUsedLint: LinterMeta = {
8+
code: ApilintCodes.OPENAPI2_REFERENCE_NOT_USED,
9+
source: 'apilint',
10+
message: 'Definition was declared but never used in document',
11+
severity: DiagnosticSeverity.Warning,
12+
linterFunction: 'apilintReferenceNotUsed',
13+
linterParams: ['string'],
14+
marker: 'key',
15+
data: {},
16+
targetSpecs: [...OpenAPI2, ...OpenAPI3, ...OpenAPI31],
17+
};
18+
19+
export default $refNotUsedLint;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,15 @@ import uniqueItemsNonArrayLint from './unique-items--non-array.ts';
8181
import uniqueItemsTypeLint from './unique-items--type.ts';
8282
import writeOnlyTypeLint from './write-only--type.ts';
8383
import exampleDeprecatedLint from './example--deprecated.ts';
84+
import $refNotUsedLint from './$ref--not-used.ts';
8485

8586
const schemaLints = [
8687
allowedFieldsOpenAPI2_0Lint,
8788
allowedFieldsOpenAPI3_0Lint,
8889
$idFormatURILint,
8990
$refValidLint,
9091
$refNoSiblingsLint,
92+
$refNotUsedLint,
9193
additionalItemsNonArrayLint,
9294
additionalItemsTypeLint,
9395
additionalItemsTypeOpenAPI3_1__AsyncAPI2Lint,

packages/apidom-ls/src/config/openapi/schema/lint.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import uniqueItemsNonArrayLint from '../../common/schema/lint/unique-items--non-
7575
import uniqueItemsTypeLint from '../../common/schema/lint/unique-items--type.ts';
7676
import writeOnlyTypeLint from '../../common/schema/lint/write-only--type.ts';
7777
import exampleDeprecatedLint from '../../common/schema/lint/example--deprecated.ts';
78+
import $refNotUsedLint from '../../common/schema/lint/$ref--not-used.ts';
7879
import { OpenAPI31 } from '../target-specs.ts';
7980

8081
const schemaLints = [
@@ -154,6 +155,7 @@ const schemaLints = [
154155
uniqueItemsTypeLint,
155156
writeOnlyTypeLint,
156157
exampleDeprecatedLint,
158+
$refNotUsedLint,
157159
];
158160

159161
export default schemaLints;

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,22 @@ export const standardLinterfunctions: FunctionItem[] = [
11131113
return true;
11141114
},
11151115
},
1116+
{
1117+
functionName: 'apilintReferenceNotUsed',
1118+
function: (element) => {
1119+
const api = root(element);
1120+
const isReferenceElement = (el: Element) => el.element === 'reference';
1121+
const referenceElements = filter((el) => {
1122+
return isReferenceElement(el);
1123+
}, api);
1124+
const referenceNames = referenceElements.map((refElement: Element) =>
1125+
// @ts-expect-error
1126+
toValue(refElement.get('$ref')).split('/').at(-1),
1127+
);
1128+
// @ts-expect-error
1129+
return referenceNames.includes(toValue(element.parent.key));
1130+
},
1131+
},
11161132
{
11171133
functionName: 'apilintOpenAPIParameterInPathTemplate',
11181134
function: (element: Element) => {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
openapi: 3.0.0
2+
info:
3+
title: 'test'
4+
version: 1.0.0
5+
paths:
6+
/test:
7+
get:
8+
responses:
9+
default:
10+
description: test
11+
components:
12+
schemas:
13+
notUsedSchema:
14+
type: object

packages/apidom-ls/test/validate.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3001,6 +3001,37 @@ describe('apidom-ls-validate', function () {
30013001
languageService.terminate();
30023002
});
30033003

3004+
it('oas / yaml - ref is defined but not used', async function () {
3005+
const validationContext: ValidationContext = {
3006+
comments: DiagnosticSeverity.Error,
3007+
maxNumberOfProblems: 100,
3008+
relatedInformation: false,
3009+
};
3010+
3011+
const spec = fs
3012+
.readFileSync(path.join(__dirname, 'fixtures', 'validation', 'oas', 'ref-not-used.yaml'))
3013+
.toString();
3014+
const doc: TextDocument = TextDocument.create('foo://bar/ref-not-used.yaml', 'yaml', 0, spec);
3015+
3016+
const languageService: LanguageService = getLanguageService(contextNoSchema);
3017+
3018+
const result = await languageService.doValidation(doc, validationContext);
3019+
result[0].code = 'test';
3020+
const expected: Diagnostic[] = [
3021+
{
3022+
range: { start: { line: 12, character: 4 }, end: { line: 12, character: 17 } },
3023+
message: 'Definition was declared but never used in document',
3024+
severity: 2,
3025+
code: 'test',
3026+
source: 'apilint',
3027+
data: {},
3028+
},
3029+
];
3030+
assert.deepEqual(result, expected as Diagnostic[]);
3031+
3032+
languageService.terminate();
3033+
});
3034+
30043035
it('oas / yaml - test editor issue 3626 / inidrect ref', async function () {
30053036
const validationContext: ValidationContext = {
30063037
comments: DiagnosticSeverity.Error,

0 commit comments

Comments
 (0)