-
-
Notifications
You must be signed in to change notification settings - Fork 240
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(core): scoring data customizable #2353
base: develop
Are you sure you want to change the base?
feat(core): scoring data customizable #2353
Conversation
From PagoNxt Trade, we are very excited to share this functionality with the community! |
406fe0f
to
2e037f5
Compare
cc4f5a9
to
35b9256
Compare
5c0bc4b
to
9d44008
Compare
9d44008
to
aa3cbc2
Compare
Hey! |
cf3ae99
to
761c65a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking pretty neat!
What do you think @mnaumanali94?
packages/cli/src/commands/lint.ts
Outdated
@@ -127,6 +135,11 @@ const lintCommand: CommandModule = { | |||
description: 'path/URL to a ruleset file', | |||
type: 'string', | |||
}, | |||
'scoring-config': { | |||
alias: 's', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tbh this option is rather quite specific, therefore I'm not sure whether an alias for it makes that much sense.
Would just keep it without an alias for now.
scoringFile = path.join(process.cwd(), scoringFile); | ||
} | ||
|
||
const scoringConfig: ScoringConfig = JSON.parse(fs.readFileSync(scoringFile, 'utf-8')) as ScoringConfig; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const scoringConfig: ScoringConfig = JSON.parse(fs.readFileSync(scoringFile, 'utf-8')) as ScoringConfig; | |
const scoringConfig: ScoringConfig = JSON.parse(fs.promises.readFile(scoringFile, 'utf8')); |
I'd also say we should impose some validation.
We could define a JSON Schema for the format and use Ajv for that purpose.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've got your suggestion
For the validation ... We guess that could be a duplicated validation, with type defined on cli/src/formatters/types.ts should be enough to validate input file, but, if anybody thinks like you about this, we could add this
@@ -60,6 +61,92 @@ Here you can build a [custom ruleset](../getting-started/3-rulesets.md), or exte | |||
- [OpenAPI ruleset](../reference/openapi-rules.md) | |||
- [AsyncAPI ruleset](../reference/asyncapi-rules.md) | |||
|
|||
## Scoring the API |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pamgoodrich do you want to have a quick look at the docs here?
packages/cli/src/formatters/json.ts
Outdated
let scoringText = ''; | ||
if (options.scoringConfig !== void 0) { | ||
if (options.scoringConfig.customScoring !== undefined) { | ||
spectralVersion = `${options.scoringConfig.customScoring} ${version as string}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think including a version makes much sense.
The result does not directly depend on the version of the CLI client, but on the ruleset you use, so seems like we could just not include it?
const cliui = require('cliui'); | ||
let output = '\n'; | ||
if (options.scoringConfig !== void 0) { | ||
if (options.scoringConfig.customScoring !== void 0) { | ||
output += `${options.scoringConfig.customScoring}${version as string}\n`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto ^
docs/guides/2-cli.md
Outdated
- scoringLetter : An object with key/value pairs with scoring letter and scoring percentage, that the result must be greater , for this letter | ||
- threshold : A number with minimum percentage value to provide valid the file we are checking | ||
- warningsSubtract : A boolean to setup if accumulate the result types to less the scoring percentage or stop counting on most critical result types | ||
- uniqueErrors : A boolean to setup a count with unique errors or with all of them |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should define what a unique error would be.
Something like "An error is considered unique if its code and message have not been seen yet" or something similar.
@@ -0,0 +1,16 @@ | |||
import { IRuleResult } from '@stoplight/spectral-core'; | |||
|
|||
export const uniqueErrors = (results: IRuleResult[]): IRuleResult[] => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const uniqueErrors = (results: IRuleResult[]): IRuleResult[] => { | |
export const getUniqueErrors = (results: IRuleResult[]): IRuleResult[] => { |
if (scoring >= options.scoringConfig.threshold) { | ||
output += chalk['green'].bold(`\u2716 PASSED!\n`); | ||
} else { | ||
output += chalk['red'].bold(`\u2716 NOT PASSED!\n`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
output += chalk['red'].bold(`\u2716 NOT PASSED!\n`); | |
output += chalk['red'].bold(`\u2716 FAILED!\n`); |
if (scoring >= options.scoringConfig.threshold) { | ||
output += chalk['green'].bold(`\u2716 PASSED!\n`); | ||
} else { | ||
output += chalk['red'].bold(`\u2716 NOT PASSED!\n`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
output += chalk['red'].bold(`\u2716 NOT PASSED!\n`); | |
output += chalk['red'].bold(`\u2716 FAILED!\n`); |
47bac35
to
01004ea
Compare
…parameter scoring threshold not reached returns exit code 1, other returns 0 scoring data tests and readme documentation added
@pamgoodrich all your suggestions have been applied. Thanks! |
This is interesting! Thanks for the mention. I can see that the scoring approach mentioned here is making some assumptions:
The approach that we have implemented in https://github.com/apigee/registry is flexible with the goal to accommodate scores for anything and not only lint results. Hence, we made use of CEL expressions so that users can define their own formula for calculating the score. While that kind of flexibility was one of the goals for us, I can see that it can be an overkill for lint specific scoring. Couple of thoughts on the current approach:
|
Thanks for your input. Here you have our answers. Usually scoring is done starting with 100% and substracting a value depending on the issue severity (error, warning,...) this is a common practice for scoring. We think is a complete overkill to go for CELs What can be a more flexible range? We see this as previous sentence (a percentage) About the possible negative, we will run some tests to validate that it can't happen The purpose of letter scoring is to show a nicer scoring than a raw number. Letter scoring comes as anything you like A+, A, B, C ... See qualys ssl tests results here: https://www.ssllabs.com/ssltest/ The number or percentage is required by the algorithm to be able to substract values We can make letter scoring optional if you don't provide the configuration of it. So results will be always percentaje and optionally letter if you provide the letter scoring configuration.
threshold : A number with minimum percentage value to provide valid the file we are checking. Any scoring below this thresold will mark the API as a failure in the scoring. warningsSubtract, we are going to change its name to onlySubtractHigherSeverityLevel and adaprt the documentation for more clarity. onlySubtractHigherSeverityLevel : A boolean to decide if only the higher severity level who appears in the results for the API to analize, are subtracted from scoring or every severity level are subtracted from scoring. See sample: true API with Errors and Warnings, only Errors substract from scoring false API with Errors and Warnings, Errors and Warnings substracts from scoring
If you don't provide scoringConfig there will be no scoring. As spectral does now. Does this work better? |
1b682b7
to
aeb85a4
Compare
Hi .. do you have any update? @P0lip @pamgoodrich @mnaumanali94 |
- scoringSubtract : An object with key/value pair objects for every result level we want to subtract percentage, with the percentage to subtract from number of results on every result type | ||
- scoringLetter : An object with key/value pairs with scoring letter and scoring percentage, that the result must be greater, for this letter | ||
- threshold : A number with minimum percentage value to provide valid the file we are checking. Any scoring below this thresold will mark the API as a failure in the scoring. | ||
- onlySubtractHigherSeverityLevel : A boolean to decide if only the higher severity level who appears in the results for the API to analize, are subtracted from scoring or every severity level are subtracted from scoring. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
analize-> analyze
@mnaumanali94 do you have any update? We need this PR merged |
dc9d7f4
to
44c40e2
Compare
02ec0d4
to
84faec8
Compare
9e92f34
to
6d09915
Compare
dc90b7a
to
c22f408
Compare
Checklist
Does this PR introduce a breaking change?
Additional context
This PR add a command line parameter to add a config file for results configuration which adds a scoring calculated and qualified with this results and a limit for passing