Skip to content

Commit cb15c26

Browse files
committed
Add reference property to context.report() for external documentation
Add the `reference` property to the `context.report()` method for external documentation links. Fix #1052 * **Documentation**: Update `docs/custom-plugins/custom-rules.md` to include the `reference` property in the `context.report()` method documentation. * **New Rule**: Add `packages/core/src/rules/arazzo/reference-property.ts` to implement a new rule for validating the `reference` property in the report object. * **Rule Integration**: Modify `packages/core/src/rules/arazzo/index.ts` to import and add the new `reference-property` rule to the existing rules. * **Unit Tests**: Add `packages/core/src/rules/arazzo/__tests__/reference-property.test.ts` to test the presence and format validation of the `reference` property and error reporting for missing or incorrect `reference` property.
1 parent e1c065b commit cb15c26

File tree

4 files changed

+130
-0
lines changed

4 files changed

+130
-0
lines changed

docs/custom-plugins/custom-rules.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ The main method used is `context.report()`, which publishes a warning or error (
9191
- `location` - {Location} (optional) An object specifying the location of the problem. Can be constructed using location object methods.
9292
- `suggest` - {string[]} (optional) - "Did you mean" suggestion.
9393
- `from` - {Location} (optional) - Referenced by location.
94+
- `reference` - {string} (optional) - A URI reference to external documentation supporting the design decision of a particular rule.
9495

9596
You may use the message alone:
9697

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { outdent } from 'outdent';
2+
import { lintDocument } from '../../../lint';
3+
import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils';
4+
import { BaseResolver } from '../../../resolve';
5+
6+
describe('Arazzo reference-property', () => {
7+
const document = parseYamlToDocument(
8+
outdent`
9+
arazzo: '1.0.1'
10+
info:
11+
title: Cool API
12+
version: 1.0.0
13+
description: A cool API
14+
sourceDescriptions:
15+
- name: museum-api
16+
type: openapi
17+
url: openapi.yaml
18+
workflows:
19+
- workflowId: get-museum-hours
20+
description: This workflow demonstrates how to get the museum opening hours and buy tickets.
21+
parameters:
22+
- in: header
23+
name: Authorization
24+
value: Basic Og==
25+
steps:
26+
- stepId: get-museum-hours
27+
description: >-
28+
Get museum hours by resolving request details with getMuseumHours operationId from openapi.yaml description.
29+
operationId: museum-api.getMuseumHours
30+
successCriteria:
31+
- condition: $statusCode == 200
32+
`,
33+
'arazzo.yaml'
34+
);
35+
36+
it('should report an error when the `reference` property is invalid', async () => {
37+
const results = await lintDocument({
38+
externalRefResolver: new BaseResolver(),
39+
document,
40+
config: await makeConfig({
41+
rules: { 'reference-property': 'error' },
42+
}),
43+
});
44+
45+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
46+
[
47+
{
48+
"location": [
49+
{
50+
"pointer": "#/workflows/0/steps/0/successCriteria/0",
51+
"reportOnKey": false,
52+
"source": "arazzo.yaml",
53+
},
54+
],
55+
"message": "The \`reference\` property must be a valid URI.",
56+
"ruleId": "reference-property",
57+
"severity": "error",
58+
"suggest": [],
59+
},
60+
]
61+
`);
62+
});
63+
64+
it('should not report an error when the `reference` property is valid', async () => {
65+
const validDocument = parseYamlToDocument(
66+
outdent`
67+
arazzo: '1.0.1'
68+
info:
69+
title: Cool API
70+
version: 1.0.0
71+
description: A cool API
72+
sourceDescriptions:
73+
- name: museum-api
74+
type: openapi
75+
url: openapi.yaml
76+
workflows:
77+
- workflowId: get-museum-hours
78+
description: This workflow demonstrates how to get the museum opening hours and buy tickets.
79+
parameters:
80+
- in: header
81+
name: Authorization
82+
value: Basic Og==
83+
steps:
84+
- stepId: get-museum-hours
85+
description: >-
86+
Get museum hours by resolving request details with getMuseumHours operationId from openapi.yaml description.
87+
operationId: museum-api.getMuseumHours
88+
successCriteria:
89+
- condition: $statusCode == 200
90+
reference: https://www.redocly.com
91+
`,
92+
'arazzo.yaml'
93+
);
94+
95+
const results = await lintDocument({
96+
externalRefResolver: new BaseResolver(),
97+
document: validDocument,
98+
config: await makeConfig({
99+
rules: { 'reference-property': 'error' },
100+
}),
101+
});
102+
103+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
104+
});
105+
});

packages/core/src/rules/arazzo/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { StepOnFailureUnique } from './step-onFailure-unique';
1313
import { RequestBodyReplacementsUnique } from './requestBody-replacements-unique';
1414
import { NoCriteriaXpath } from '../spot/no-criteria-xpath';
1515
import { CriteriaUnique } from './criteria-unique';
16+
import { ReferenceProperty } from './reference-property';
1617

1718
import type { Arazzo1Rule } from '../../visitors';
1819
import type { Arazzo1RuleSet } from '../../oas-types';
@@ -33,6 +34,7 @@ export const rules: Arazzo1RuleSet<'built-in'> = {
3334
'requestBody-replacements-unique': RequestBodyReplacementsUnique,
3435
'no-criteria-xpath': NoCriteriaXpath,
3536
'criteria-unique': CriteriaUnique,
37+
'reference-property': ReferenceProperty,
3638
};
3739

3840
export const preprocessors = {};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { Arazzo1Rule } from '../../visitors';
2+
import type { UserContext } from '../../walk';
3+
4+
export const ReferenceProperty: Arazzo1Rule = () => {
5+
return {
6+
Report: {
7+
enter(report, { report: ctxReport, location }: UserContext) {
8+
const reference = report?.reference;
9+
if (reference !== undefined) {
10+
try {
11+
new URL(reference);
12+
} catch (_) {
13+
ctxReport({
14+
message: 'The `reference` property must be a valid URI.',
15+
location: location.child(['reference']),
16+
});
17+
}
18+
}
19+
},
20+
},
21+
};
22+
};

0 commit comments

Comments
 (0)