Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ruleset): introduce documentationUrl property #1242

Merged
merged 13 commits into from
Jul 10, 2020
10 changes: 10 additions & 0 deletions docs/getting-started/rulesets.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,13 @@ Spectral comes with two rulesets included:
- `spectral:asyncapi` - AsyncAPI v2 rules

You can also make your own: read more about [Custom Rulesets](../guides/4-custom-rulesets.md).

## Documentation URL

Optionally provide a documentation URL to your ruleset in order to help end-users find more information about various warnings. Result messages will sometimes be more than enough to explain what the problem is, but it can also be beneficial to explain _why_ a message exists, and this is a great place to do that.

Whatever you link you provide, the rule name will be appended as an anchor.

Given the following `documentationUrl` [`https://stoplight.io/p/docs/gh/stoplightio/spectral/docs/reference/openapi-rules.md`](https://stoplight.io/p/docs/gh/stoplightio/spectral/docs/reference/openapi-rules.md), an example URL for `info-contact` rule would look as follows [`https://stoplight.io/p/docs/gh/stoplightio/spectral/docs/reference/openapi-rules.md#info-contact`](https://stoplight.io/p/docs/gh/stoplightio/spectral/docs/reference/openapi-rules.md#info-contact).

P0lip marked this conversation as resolved.
Show resolved Hide resolved
If no `documentationUrl` is provided, no links will show up, and users will just have to rely on the error messages to figure out how the errors can be fixed.
4 changes: 4 additions & 0 deletions src/meta/ruleset.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"$id": "http://stoplight.io/schemas/ruleset.schema.json",
"type": "object",
"properties": {
"documentationUrl": {
"type": "string",
"format": "url"
},
"rules": {
"type": "object",
"additionalProperties": {
Expand Down
25 changes: 19 additions & 6 deletions src/rulesets/__tests__/validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,46 @@ const invalidRuleset = require('./__fixtures__/invalid-ruleset.json');
const validRuleset = require('./__fixtures__/valid-flat-ruleset.json');

describe('Ruleset Validation', () => {
it('given primitive type should throw', () => {
it('given primitive type, throws', () => {
expect(assertValidRuleset.bind(null, null)).toThrow('Provided ruleset is not an object');
expect(assertValidRuleset.bind(null, 2)).toThrow('Provided ruleset is not an object');
expect(assertValidRuleset.bind(null, 'true')).toThrow('Provided ruleset is not an object');
});

it('given object with no rules and no extends properties should throw', () => {
it('given object with no rules and no extends properties, throws', () => {
expect(assertValidRuleset.bind(null, {})).toThrow('Ruleset must have rules or extends property');
expect(assertValidRuleset.bind(null, { rule: {} })).toThrow('Ruleset must have rules or extends property');
});

it('given object with extends property only should emit no errors', () => {
it('given object with extends property only, emits no errors', () => {
expect(assertValidRuleset.bind(null, { extends: [] })).not.toThrow();
});

it('given object with rules property only should emit no errors', () => {
it('given object with rules property only, emits no errors', () => {
expect(assertValidRuleset.bind(null, { rules: {} })).not.toThrow();
});

it('given invalid ruleset should throw', () => {
it('given invalid ruleset, throws', () => {
expect(assertValidRuleset.bind(null, invalidRuleset)).toThrow(ValidationError);
});

it('given valid ruleset should emit no errors', () => {
it('given valid ruleset should, emits no errors', () => {
expect(assertValidRuleset.bind(null, validRuleset)).not.toThrow();
});

it.each([false, 2, null, 'foo', '12.foo.com'])('given invalid %s documentationUrl, throws', documentationUrl => {
expect(assertValidRuleset.bind(null, { documentationUrl, rules: {} })).toThrow(ValidationError);
});

it('recognizes valid documentationUrl', () => {
expect(
assertValidRuleset.bind(null, {
documentationUrl: 'https://stoplight.io/p/docs/gh/stoplightio/spectral/docs/reference/openapi-rules.md',
extends: ['spectral:oas'],
}),
).not.toThrow();
});

it.each(['error', 'warn', 'info', 'hint', 'off'])('recognizes human-readable %s severity', severity => {
expect(
assertValidRuleset.bind(null, {
Expand Down
3 changes: 2 additions & 1 deletion src/rulesets/asyncapi/index.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"documentationUrl": "https://stoplight.io/p/docs/gh/stoplightio/spectral/docs/reference/asyncapi-rules.md",
"formats": [
"asyncapi2"
],
Expand Down Expand Up @@ -398,4 +399,4 @@
}
}
}
}
}
1 change: 1 addition & 0 deletions src/rulesets/oas/index.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"documentationUrl": "https://stoplight.io/p/docs/gh/stoplightio/spectral/docs/reference/openapi-rules.md",
"formats": ["oas2", "oas3"],
"functions": [
"oasDocumentSchema",
Expand Down