Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/fuzzy-crews-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@redocly/openapi-core": patch
"@redocly/cli": patch
---

Resolved an issue where overrides for the severity of configurable scorecard rules were ignored.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ exports[`resolveConfig should ignore minimal from the root and read local file 1
"component-name-unique": "off",
"no-empty-servers": "error",
"no-example-value-and-externalValue": "error",
"no-invalid-media-type-examples": "error",
"no-invalid-media-type-examples": {
"allowAdditionalProperties": false,
"severity": "error",
},
"no-server-example.com": "warn",
"no-server-trailing-slash": "error",
"no-server-variables-empty-enum": "error",
Expand Down Expand Up @@ -208,7 +211,10 @@ exports[`resolveStyleguideConfig should resolve extends with local file config w
"component-name-unique": "off",
"no-empty-servers": "error",
"no-example-value-and-externalValue": "error",
"no-invalid-media-type-examples": "warn",
"no-invalid-media-type-examples": {
"allowAdditionalProperties": false,
"severity": "warn",
},
"no-server-example.com": "warn",
"no-server-trailing-slash": "error",
"no-server-variables-empty-enum": "error",
Expand Down
21 changes: 21 additions & 0 deletions packages/core/src/config/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,24 @@ describe('transformConfig', () => {
});
});
});

describe('mergeExtends', () => {
it('should work with empty extends', () => {
expect(utils.mergeExtends([]).rules).toEqual({});
});

it('should work with configurable rules changing severity', () => {
expect(
utils.mergeExtends([
{
rules: { 'rule/abc': { severity: 'error', subject: 'Operation' } },
},
{
rules: { 'rule/abc': 'warn' },
},
]).rules
).toEqual({
'rule/abc': { severity: 'warn', subject: 'Operation' },
});
});
});
85 changes: 43 additions & 42 deletions packages/core/src/config/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
assignExisting,
assignExistingRules,
assignRules,
isDefined,
isTruthy,
showErrorForDeprecatedField,
Expand Down Expand Up @@ -198,47 +199,47 @@ export function mergeExtends(rulesConfList: ResolvedStyleguideConfig[]) {
);
}

Object.assign(result.rules, rulesConf.rules);
Object.assign(result.oas2Rules, rulesConf.oas2Rules);
assignExisting(result.oas2Rules, rulesConf.rules || {});
Object.assign(result.oas3_0Rules, rulesConf.oas3_0Rules);
assignExisting(result.oas3_0Rules, rulesConf.rules || {});
Object.assign(result.oas3_1Rules, rulesConf.oas3_1Rules);
assignExisting(result.oas3_1Rules, rulesConf.rules || {});
Object.assign(result.async2Rules, rulesConf.async2Rules);
assignExisting(result.async2Rules, rulesConf.rules || {});
Object.assign(result.async3Rules, rulesConf.async3Rules);
assignExisting(result.async3Rules, rulesConf.rules || {});
Object.assign(result.arazzoRules, rulesConf.arazzoRules);
assignExisting(result.arazzoRules, rulesConf.rules || {});

Object.assign(result.preprocessors, rulesConf.preprocessors);
Object.assign(result.oas2Preprocessors, rulesConf.oas2Preprocessors);
assignExisting(result.oas2Preprocessors, rulesConf.preprocessors || {});
Object.assign(result.oas3_0Preprocessors, rulesConf.oas3_0Preprocessors);
assignExisting(result.oas3_0Preprocessors, rulesConf.preprocessors || {});
Object.assign(result.oas3_1Preprocessors, rulesConf.oas3_1Preprocessors);
assignExisting(result.oas3_1Preprocessors, rulesConf.preprocessors || {});
Object.assign(result.async2Preprocessors, rulesConf.async2Preprocessors);
assignExisting(result.async2Preprocessors, rulesConf.preprocessors || {});
Object.assign(result.async3Preprocessors, rulesConf.async3Preprocessors);
assignExisting(result.async3Preprocessors, rulesConf.preprocessors || {});
Object.assign(result.arazzoPreprocessors, rulesConf.arazzoPreprocessors);
assignExisting(result.arazzoPreprocessors, rulesConf.preprocessors || {});

Object.assign(result.decorators, rulesConf.decorators);
Object.assign(result.oas2Decorators, rulesConf.oas2Decorators);
assignExisting(result.oas2Decorators, rulesConf.decorators || {});
Object.assign(result.oas3_0Decorators, rulesConf.oas3_0Decorators);
assignExisting(result.oas3_0Decorators, rulesConf.decorators || {});
Object.assign(result.oas3_1Decorators, rulesConf.oas3_1Decorators);
assignExisting(result.oas3_1Decorators, rulesConf.decorators || {});
Object.assign(result.async2Decorators, rulesConf.async2Decorators);
assignExisting(result.async2Decorators, rulesConf.decorators || {});
Object.assign(result.async3Decorators, rulesConf.async3Decorators);
assignExisting(result.async3Decorators, rulesConf.decorators || {});
Object.assign(result.arazzoDecorators, rulesConf.arazzoDecorators);
assignExisting(result.arazzoDecorators, rulesConf.decorators || {});
assignRules(result.rules, rulesConf.rules);
assignRules(result.oas2Rules, rulesConf.oas2Rules);
assignExistingRules(result.oas2Rules, rulesConf.rules);
assignRules(result.oas3_0Rules, rulesConf.oas3_0Rules);
assignExistingRules(result.oas3_0Rules, rulesConf.rules || {});
assignRules(result.oas3_1Rules, rulesConf.oas3_1Rules);
assignExistingRules(result.oas3_1Rules, rulesConf.rules || {});
assignRules(result.async2Rules, rulesConf.async2Rules);
assignExistingRules(result.async2Rules, rulesConf.rules || {});
assignRules(result.async3Rules, rulesConf.async3Rules);
assignExistingRules(result.async3Rules, rulesConf.rules || {});
assignRules(result.arazzoRules, rulesConf.arazzoRules);
assignExistingRules(result.arazzoRules, rulesConf.rules || {});

assignRules(result.preprocessors, rulesConf.preprocessors);
assignRules(result.oas2Preprocessors, rulesConf.oas2Preprocessors);
assignExistingRules(result.oas2Preprocessors, rulesConf.preprocessors || {});
assignRules(result.oas3_0Preprocessors, rulesConf.oas3_0Preprocessors);
assignExistingRules(result.oas3_0Preprocessors, rulesConf.preprocessors || {});
assignRules(result.oas3_1Preprocessors, rulesConf.oas3_1Preprocessors);
assignExistingRules(result.oas3_1Preprocessors, rulesConf.preprocessors || {});
assignRules(result.async2Preprocessors, rulesConf.async2Preprocessors);
assignExistingRules(result.async2Preprocessors, rulesConf.preprocessors || {});
assignRules(result.async3Preprocessors, rulesConf.async3Preprocessors);
assignExistingRules(result.async3Preprocessors, rulesConf.preprocessors || {});
assignRules(result.arazzoPreprocessors, rulesConf.arazzoPreprocessors);
assignExistingRules(result.arazzoPreprocessors, rulesConf.preprocessors || {});

assignRules(result.decorators, rulesConf.decorators);
assignRules(result.oas2Decorators, rulesConf.oas2Decorators);
assignExistingRules(result.oas2Decorators, rulesConf.decorators || {});
assignRules(result.oas3_0Decorators, rulesConf.oas3_0Decorators);
assignExistingRules(result.oas3_0Decorators, rulesConf.decorators || {});
assignRules(result.oas3_1Decorators, rulesConf.oas3_1Decorators);
assignExistingRules(result.oas3_1Decorators, rulesConf.decorators || {});
assignRules(result.async2Decorators, rulesConf.async2Decorators);
assignExistingRules(result.async2Decorators, rulesConf.decorators || {});
assignRules(result.async3Decorators, rulesConf.async3Decorators);
assignExistingRules(result.async3Decorators, rulesConf.decorators || {});
assignRules(result.arazzoDecorators, rulesConf.arazzoDecorators);
assignExistingRules(result.arazzoDecorators, rulesConf.decorators || {});

result.plugins!.push(...(rulesConf.plugins || []));
result.pluginPaths!.push(...(rulesConf.pluginPaths || []));
Expand Down
25 changes: 23 additions & 2 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,30 @@ export function isNotString<T>(value: string | T): value is T {
return !isString(value);
}

export function assignExisting<T>(target: Record<string, T>, obj: Record<string, T>) {
export const assignRules = <T extends string | { severity?: string }>(
target: Record<string, T>,
obj?: Record<string, T>
) => {
if (!obj) return;
for (const k of Object.keys(obj)) {
if (target.hasOwnProperty(k)) {
if (typeof target[k] === 'object' && target[k] !== null && typeof obj[k] === 'string') {
target[k].severity = obj[k];
} else {
target[k] = obj[k];
}
}
};

export function assignExistingRules<T extends string | { severity?: string }>(
target: Record<string, T>,
obj?: Record<string, T>
) {
if (!obj) return;
for (const k of Object.keys(obj)) {
if (!target.hasOwnProperty(k)) continue;
if (typeof target[k] === 'object' && target[k] !== null && typeof obj[k] === 'string') {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use isPlainObject.

target[k].severity = obj[k];
} else {
target[k] = obj[k];
}
}
Expand Down