From fb72fc479ef117b9fdfc385eee68f37b8ed215aa Mon Sep 17 00:00:00 2001 From: casserni Date: Mon, 3 Dec 2018 14:30:51 -0600 Subject: [PATCH] fix: allowing passing in ruleStore to run instead of using this._ --- src/__tests__/spectral.test.ts | 67 ++++++++++++++++++++++++++++++++++ src/index.ts | 47 ++++++++++++++++-------- 2 files changed, 99 insertions(+), 15 deletions(-) diff --git a/src/__tests__/spectral.test.ts b/src/__tests__/spectral.test.ts index 17fbc611d..6ef70b06e 100644 --- a/src/__tests__/spectral.test.ts +++ b/src/__tests__/spectral.test.ts @@ -177,6 +177,73 @@ Array [ expect(results.length).toEqual(1); }); + // Assures: https://stoplightio.atlassian.net/browse/SL-788 + test('run with rulesets overrides ruleset on run, not permenantly', () => { + const spec = { + hello: 'world', + }; + + const rulesets: IRuleset[] = [ + { + rules: { + format: { + test: { + type: RuleType.STYLE, + function: RuleFunction.TRUTHY, + path: '$', + enabled: false, + severity: RuleSeverity.ERROR, + summary: '', + input: { + properties: 'nonexistant-property', + }, + }, + }, + }, + }, + ]; + + const overrideRulesets: IRuleset[] = [ + { + rules: { + format: { + test: true, + }, + }, + }, + ]; + + const s = new Spectral({ rulesets }); + + // run again with an override config + const run1 = s.run({ target: spec, spec: 'format', rulesets: overrideRulesets }); + + expect(s.getRules('format')).toMatchInlineSnapshot(` +Array [ + Object { + "apply": [Function], + "format": "format", + "name": "test", + "rule": Object { + "enabled": false, + "function": "truthy", + "input": Object { + "properties": "nonexistant-property", + }, + "path": "$", + "severity": "error", + "summary": "", + "type": "style", + }, + }, +] +`); + + const run2 = s.run({ target: spec, spec: 'format' }); + + expect(run1).not.toEqual(run2); + }); + test('getRules returns a flattened list of rules filtered by format', () => { const rulesets: IRuleset[] = [ { diff --git a/src/index.ts b/src/index.ts index fd1d55b3c..d9225628a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ import * as jp from 'jsonpath'; import { PathComponent } from 'jsonpath'; import { compact, flatten } from 'lodash'; -import { functions } from './functions'; +import { functions as defaultFunctions } from './functions'; import * as types from './types'; interface IFunctionStore { @@ -91,24 +91,27 @@ export class Spectral { public setRules(rulesets: types.IRuleset[]) { this._rulesets = merge([], rulesets); - this._functions = this._rulesetsToFunctions(this._rulesets); - this._rulesByIndex = this._rulesetsToRules(this._rulesets); + this._functions = { ...defaultFunctions, ...this._rulesetsToFunctions(this._rulesets) }; + this._rulesByIndex = this._rulesetsToRules(this._rulesets, this._rulesByIndex, this._functions); } public run(opts: IRunOpts): types.IRuleResult[] { const { target, rulesets = [] } = opts; + let ruleStore = this._rulesByIndex; + if (rulesets.length) { - this.setRules(rulesets); + const functionStore = { ...this._functions, ...this._rulesetsToFunctions(rulesets) }; + ruleStore = this._rulesetsToRules(rulesets, ruleStore, functionStore); } - return target ? this.runAllLinters(opts) : []; + return target ? this.runAllLinters(ruleStore, opts) : []; } - private runAllLinters(opts: IRunOpts): types.IRuleResult[] { + private runAllLinters(ruleStore: IRuleStore, opts: IRunOpts): types.IRuleResult[] { return flatten( compact( - values(this._rulesByIndex).map((ruleEntry: IRuleEntry) => { + values(ruleStore).map((ruleEntry: IRuleEntry) => { if ( !ruleEntry.rule.enabled || (opts.type && ruleEntry.rule.type !== opts.type) || @@ -178,7 +181,10 @@ export class Spectral { return ruleEntry.apply(opt); } - private _parseRuleDefinition(name: string, rule: types.Rule, format: string): IRuleEntry { + private _parseRuleDefinition( + { name, format, rule }: { name: string; format: string; rule: types.Rule }, + functionStore: IFunctionStore = {} + ): IRuleEntry { const ruleIndex = this.toRuleIndex(name, format); try { jp.parse(rule.path); @@ -186,7 +192,7 @@ export class Spectral { throw new SyntaxError(`Invalid JSON path for rule '${ruleIndex}': ${rule.path}\n\n${e}`); } - const ruleFunc = this._functions[rule.function]; + const ruleFunc = functionStore[rule.function] || this._functions[rule.function]; if (!ruleFunc) { throw new SyntaxError(`Function does not exist for rule '${ruleIndex}': ${rule.function}`); } @@ -203,7 +209,11 @@ export class Spectral { return `${ruleFormat}-${ruleName}`; } - private _rulesetToRules(ruleset: types.IRuleset, internalRuleStore: IRuleStore): IRuleStore { + private _rulesetToRules( + ruleset: types.IRuleset, + internalRuleStore: IRuleStore, + functionStore?: IFunctionStore + ): IRuleStore { const formats = ruleset.rules; for (const format in formats) { if (!formats.hasOwnProperty(format)) continue; @@ -225,7 +235,10 @@ export class Spectral { internalRuleStore[ruleIndex].rule.enabled = r; } else if (typeof r === 'object' && !Array.isArray(r)) { // rule definition - internalRuleStore[ruleIndex] = this._parseRuleDefinition(ruleName, r, format); + internalRuleStore[ruleIndex] = this._parseRuleDefinition( + { name: ruleName, rule: r, format }, + functionStore + ); } else { throw new Error(`Unknown rule definition format: ${r}`); } @@ -235,18 +248,22 @@ export class Spectral { return internalRuleStore; } - private _rulesetsToRules(rulesets: types.IRuleset[]): IRuleStore { - const rules: IRuleStore = merge({}, this._rulesByIndex); + private _rulesetsToRules( + rulesets: types.IRuleset[], + ruleStore?: IRuleStore, + functionStore?: IFunctionStore + ): IRuleStore { + const rules: IRuleStore = merge({}, ruleStore); for (const ruleset of rulesets) { - merge(rules, this._rulesetToRules(ruleset, rules)); + merge(rules, this._rulesetToRules(ruleset, rules, functionStore)); } return rules; } private _rulesetsToFunctions(rulesets: types.IRuleset[]): IFunctionStore { - let funcs = { ...functions }; + let funcs = {}; for (const ruleset of rulesets) { if (ruleset.functions) {