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: teach rulesets to accept exceptions #939

Merged
merged 3 commits into from
Mar 10, 2020
Merged

feat: teach rulesets to accept exceptions #939

merged 3 commits into from
Mar 10, 2020

Conversation

nulltoken
Copy link
Contributor

@nulltoken nulltoken commented Jan 23, 2020

Fixes #747.

Checklist

Does this PR introduce a breaking change?

  • Yes
  • No

@@ -97,6 +101,7 @@ const createRulesetProcessor = (
if (extendedRuleset !== null) {
mergeRules(rules, extendedRuleset.rules, parentSeverity);
Object.assign(functions, extendedRuleset.functions);
mergeExceptions(baseUri, exceptions, extendedRuleset.exceptions);
Copy link
Contributor

@P0lip P0lip Jan 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Food for thoughts, but I'm pretty sure we'll want to filter out certain exceptions.
For instance,

{
  "file/#": ["foo-rule"],
  "file/#.bar": ["foo-rule"]
}

This could be expressed in a single exception "file/#": ["foo-rule"].
We'll have fewer paths to query later if we narrow them down here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@P0lip Is that a use case so common that it should be included in that PR or should we wait until we get some feedback that maintaining exceptions is too tedious?

@m-mohr
Copy link
Contributor

m-mohr commented Jan 27, 2020

I like this a lot. I guess it would make #923 obsolete.

@nulltoken nulltoken force-pushed the ntk/exceptions branch 4 times, most recently from 78b6c8f to 07e3314 Compare February 3, 2020 19:22
@nulltoken nulltoken force-pushed the ntk/exceptions branch 8 times, most recently from 64fa0df to a748cbb Compare February 15, 2020 04:22
@nulltoken
Copy link
Contributor Author

nulltoken commented Feb 15, 2020

@philsturgeon @P0lip Here's a first alpha version of the PR.

repro/one.yaml

openapi: 3.0.0
info:
  version: 1.0.0
  title: Stoplight
paths: {}

repro/ruleset.yaml

extends: spectral:oas

except:
  "one.yaml#":
    - oas3-api-servers
  "./one.yaml#/info":  # Beside relative ones, also supports rooted paths ;-)
    - info-contact

cli usage

$ yarn cli lint repro/one.yaml -r repro/ruleset.yaml
OpenAPI 3.x detected

f:/spectral/repro/one.yaml
 1:1  warning  openapi-tags      OpenAPI object should have non-empty `tags` array.
 2:6  warning  info-description  OpenAPI object info `description` must be present and non-empty string.

✖ 2 problems (0 errors, 2 warnings, 0 infos, 0 hints)

I'm going to start working on the doco.
Still wrestling a bit with a harness related issue, but should be ready for a first review pass.

/cc @m-mohr Would you agree to give it a try against your own use cases, I'd be delighted to get some feedback!

@nulltoken nulltoken self-assigned this Feb 15, 2020
src/utils/sortResults.ts Outdated Show resolved Hide resolved
src/runner.ts Outdated Show resolved Hide resolved
src/runner.ts Outdated
): IRuleResult[] => {
const results: IRuleResult[] = [];

// TODO:
// Ruleset
// - To be investigated: How to cope with STDIN as a source and exception relative locations?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@P0lip I haven't started to dig into that. Any idea?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@P0lip As I'm not sure how to build a ruleset that would be able to reference a document passed through STDIN, I'm wondering if issuing a warning wouldn't be the best short-term approach.

When the ruleset contains exceptions
And the document is passed as STDIN
A warning would be issued notifying that exceptions handling isn't supported in that mode and that the run will ignore them.

Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you STDIN a file, it may well still have $ref's, and those $ref's would still have exceptions working just the same right? It'd just be the initial "entry file" being passed in as STDIN, and that could have things ignored with {ENTRY FILE OR ROOT IDENTIFIER}#/components and not mentioning a filename? Or do we need some sort of wildcard for saying "this is the first/main/root/entry file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philsturgeon I'm not even sure how a $ref containing a relative file location would be resolved in that context and I can't find any test in the codebase about that use case. Does it consider the current working directory as the root to be used?

@P0lip Any idea?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, the document path would be equal to the output of process.cwd().
I wouldn't really bother too much though, as it's kind of expected you provide a dereferenced document as STDIN.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah CWD.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 It took me some time (as Windows and tty don't play well together) but I managed to find a way to honor exceptions in a stdin context.

@P0lip @philsturgeon Could you please take a look at the proposal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a side note, that more or less compelled me to find a sustainable way to develop in a xplat way from a Windows computer. I'm going to open a series of tiny pull requests to make contributing from Windows a bit easier.

src/__tests__/linter.jest.test.ts Outdated Show resolved Hide resolved
@nulltoken
Copy link
Contributor Author

nulltoken commented Feb 15, 2020

@P0lip As this builds upon #966, maybe reviewing first #966 (and maybe (with a little luck 🤞 ) getting it merged 😉 ) would reduce the diff noise in this one and make it slightly easier to review.

@danielgtaylor
Copy link
Contributor

I like this approach, but what do you think of also supporting something in the OpenAPI itself, e.g. something like this:

openapi: 3.0.0
info:
  version: 1.0.0
  title: Stoplight
  x-linter-ignore:
    - info-contact
x-linter-ignore:
  - oas3-api-servers
paths: {}

Then the exceptions can be explicitly listed in the API description document rather than having to link two places via paths in separate documents, and it prevents the need for an almost empty .spectral.yaml when using a shared set of rules.

You'd have to decide if the rules apply to all children as well, e.g. if ignoring a camel-casing rule on a path would apply to all parameters for all operations under that path.

@nulltoken nulltoken force-pushed the ntk/exceptions branch 3 times, most recently from ca194cd to b0b778b Compare February 28, 2020 06:41
@philsturgeon
Copy link
Contributor

@danielgtaylor I'd like to keep the scope of this PR to ruleset based ignore, but we could have another issue discussing the pros and cons of letting API description authors mark their own descriptions with ignores.

It could be seen as a bit cheeky, trying to squeak an API with "naughty things" past automated API governance, but... make a case for it in a new issue and we'll see! :D

@nulltoken nulltoken force-pushed the ntk/exceptions branch 2 times, most recently from 052eec4 to 4fc6f28 Compare March 2, 2020 19:22
@@ -249,6 +249,51 @@ The example above will run all of the rules defined in the `spectral:oas` rulese

- [Rules relevant to OpenAPI v2 and v3](../reference/openapi-rules.md)

## Selectively silencing some results

From time to time, you want to ignore some specific results without turning off
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please can we try to pick a word and stick to it? Using both "silencing" and "ignoring" is ok, but then we talk about exceptions.

Can we either ignore, or except? I think personally think ignore is more clear, and in line with .eslintignore, .gitignore, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philsturgeon Fixed. Do you like it better?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is great! I'll give it a lick of paint after so you can focus on writing amazing code instead of mucking around with minor tweaks.


`except` describes a map of locations (expressed as Json paths) and rules that should be ignored.

Locations be can either described as relatively to the ruleset or absolute paths.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Locations be can either described as relatively to the ruleset or absolute paths.
Locations be can either described as relative to the ruleset or absolute paths.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extends: spectral:oas

except:
"<STDIN>#":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to mess around with this. I think putting something in the ruleset which demands knowledge of exactly how it's being called is going to be more confusion than its worth.

I would expect to see people writing #/info, or models/awful.json to make it shut up about issues in info, or a particularly awful model. If that info ignore just so happens to quieten up two different infos im not sure people will be that upset.

Let's try and avoid overthinking things, and leave room for more improvements later if we get feedback that they're required. I don't think entrance files are the biggest concern for people, and if they are a more specific exception can be written, so this will possibly not come up for a long time - if ever. 🎲

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philsturgeon Thanks for the detailed feedback!

The current implementation was designed to help make one of the initial requirements happen (from #747 (comment))

image

As such, the current syntax expects a file and a non ambiguous path within it that should be ignored.

Of course, the model can be extended to support more "handwritten" scenarios:

  • models/awful.json: [ rule1, rule2]: Would ignore rule1 and rule2 results wheverever in the file
  • #/info": [rule1, rule2]: Would ignore rule1 and rule2 results in any processed document provided they originate from a path that's #/info or below (eg. #/info/sub/path/1). This would be in line with @P0lip's comment

Would that fit you better?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok if that’s the goal just ignore STDIN entirely and support files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philsturgeon Dropped. STDIN and laxer syntax will be tracked through separate issues to keep that PR on scope.

Copy link
Contributor

@P0lip P0lip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one extra note on STDIN, but looking really good besides that.

set.add(r);
});

target[normalizedLocation] = [...set].sort();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
target[normalizedLocation] = [...set].sort();
target[normalizedLocation] = [...set].sort((a, b) => a.localeCompare(b));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@P0lip Fixed

const buildErrorMessagePrefix = ($ref: string, rulesetUri?: string): string => {
let prefix = '';

if (rulesetUri !== undefined) prefix += `in ruleset \`${rulesetUri}\`, `;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (rulesetUri !== undefined) prefix += `in ruleset \`${rulesetUri}\`, `;
if (rulesetUri !== void 0) prefix += `in ruleset \`${rulesetUri}\`, `;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@P0lip Fixed

src/rulesets/mergers/exceptions.ts Outdated Show resolved Hide resolved
except:
"<STDIN>#":
- oas3-api-servers
"<STDIN>#/info":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, not a fan of it tbh.
How about ./#info? This would be more universal IMHO, as you could also target the input document, i.e. it'd target foo.yaml in when the following command spectral lint foo.yaml is executed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been recommending we just ditch STDIN support entirely it's not worth the hassle generally speaking we should plan features before we get people working on them :D

This is trying to solve the Studio use case, and if people ask for this we can bring it in, but until then its scope creep.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@P0lip @philsturgeon STDIN support has been dropped. A warning is now generated when the document is consumed from stdin and the ruleset contains exceptions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is trying to solve the Studio use case, and if people ask for this we can bring it in, but until then its scope creep.

Studio should not be affected too much, since it doesn't use STDIN.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@P0lip that's what im trying to say. We are trying to get this working for the studio use case, which doesnt need STDIN, so we don't need to worry about STDIN here.

P0lip
P0lip previously approved these changes Mar 5, 2020
Copy link
Contributor

@P0lip P0lip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good to me.

src/document.ts Outdated
@@ -6,6 +6,8 @@ import { IParser } from './parsers/types';
import { IRuleResult } from './types';
import { startsWithProtocol } from './utils';

export const StdIn: string = '<STDIN>';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const StdIn: string = '<STDIN>';
export const STDIN: string = '<STDIN>';

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@P0lip Fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rule exceptions
5 participants