chore: rename features.openapi and features.mockServer to theme (#995)

This commit is contained in:
Anton Kozachuk
2023-01-23 15:06:53 +02:00
committed by GitHub
parent 8fa9a359f1
commit 0f8b2ad1bd
13 changed files with 189 additions and 134 deletions

View File

@@ -36,10 +36,10 @@ export function getObjectOrJSON(
}
break;
default: {
if (config.configFile) {
if (config) {
process.stderr.write(`Found ${config.configFile} and using theme.openapi options`);
return config['features.openapi']; // FIXME: rename features.openapi to theme.openapi after global renaming inside repo.
return config.theme.openapi ? config.theme.openapi : {};
}
return {};
}

View File

@@ -137,11 +137,11 @@ export async function previewDocs(
styleguide.skipPreprocessors(argv['skip-preprocessor']);
styleguide.skipDecorators(argv['skip-decorator']);
const referenceDocs = resolvedConfig['features.openapi'];
const referenceDocs = resolvedConfig.theme?.openapi;
redocOptions = {
...referenceDocs,
useCommunityEdition: argv['use-community-edition'] || referenceDocs.useCommunityEdition,
licenseKey: process.env.REDOCLY_LICENSE_KEY || referenceDocs.licenseKey,
useCommunityEdition: argv['use-community-edition'] || referenceDocs?.useCommunityEdition,
licenseKey: process.env.REDOCLY_LICENSE_KEY || referenceDocs?.licenseKey,
};
return resolvedConfig;
}

View File

@@ -241,8 +241,8 @@ async function collectFilesToUpload(api: string, config: Config) {
if (config.configFile) {
// All config file paths including the root one
files.push(...[...new Set(config.styleguide.extendPaths)].map((f) => getFileEntry(f)));
if (config['features.openapi'].htmlTemplate) {
const dir = getFolder(config['features.openapi'].htmlTemplate);
if (config.theme?.openapi?.htmlTemplate) {
const dir = getFolder(config.theme.openapi.htmlTemplate);
const fileList = getFilesList(dir, []);
files.push(...fileList.map((f) => getFileEntry(f)));
}

View File

@@ -416,7 +416,7 @@ yargs
})
.check((argv: any) => {
if (argv.theme && !argv.theme?.openapi)
throw Error('Invalid option: features.openapi not set');
throw Error('Invalid option: theme.openapi not set');
return true;
}),
async (argv) => {

View File

@@ -68,7 +68,8 @@ describe('lint', () => {
assertions:
local/checkWordsCount:
min: 3
features.openapi:
theme:
openapi:
showConsole: true
layout:
scope: section
@@ -78,7 +79,8 @@ describe('lint', () => {
backgroundColor: '#263238'
links:
color: '#6CC496'
features.openapi:
theme:
openapi:
showConsole: true
layout:
scope: section
@@ -99,70 +101,28 @@ describe('lint', () => {
"from": undefined,
"location": Array [
Object {
"pointer": "#/atures.openapi",
"pointer": "#/eme",
"reportOnKey": true,
"source": "",
},
],
"message": "Property \`atures.openapi\` is not expected here.",
"message": "Property \`eme\` is not expected here.",
"ruleId": "configuration spec",
"severity": "error",
"suggest": Array [
"features.openapi",
"theme",
],
},
Object {
"from": undefined,
"location": Array [
Object {
"pointer": "#/showConsole",
"pointer": "#/openapi",
"reportOnKey": true,
"source": "",
},
],
"message": "Property \`showConsole\` is not expected here.",
"ruleId": "configuration spec",
"severity": "error",
"suggest": Array [],
},
Object {
"from": undefined,
"location": Array [
Object {
"pointer": "#/layout",
"reportOnKey": true,
"source": "",
},
],
"message": "Property \`layout\` is not expected here.",
"ruleId": "configuration spec",
"severity": "error",
"suggest": Array [],
},
Object {
"from": undefined,
"location": Array [
Object {
"pointer": "#/routingStrategy",
"reportOnKey": true,
"source": "",
},
],
"message": "Property \`routingStrategy\` is not expected here.",
"ruleId": "configuration spec",
"severity": "error",
"suggest": Array [],
},
Object {
"from": undefined,
"location": Array [
Object {
"pointer": "#/theme",
"reportOnKey": true,
"source": "",
},
],
"message": "Property \`theme\` is not expected here.",
"message": "Property \`openapi\` is not expected here.",
"ruleId": "configuration spec",
"severity": "error",
"suggest": Array [],
@@ -181,20 +141,6 @@ describe('lint', () => {
"severity": "error",
"suggest": Array [],
},
Object {
"from": undefined,
"location": Array [
Object {
"pointer": "#/features.openapi/layout",
"reportOnKey": false,
"source": "",
},
],
"message": "\`layout\` can be one of the following only: \\"stacked\\", \\"three-panel\\".",
"ruleId": "configuration spec",
"severity": "error",
"suggest": Array [],
},
]
`);
});
@@ -234,7 +180,7 @@ describe('lint', () => {
`);
});
it('lintConfig should work with legacy fields', async () => {
it('lintConfig should work with legacy fields - referenceDocs', async () => {
const document = parseYamlToDocument(
outdent`
apis:

View File

@@ -44,8 +44,10 @@ const testConfig: Config = {
preprocessors: { oas2: {}, oas3_0: {}, oas3_1: {} },
decorators: { oas2: {}, oas3_0: {}, oas3_1: {} },
} as unknown as StyleguideConfig,
'features.openapi': {},
'features.mockServer': {},
theme: {
openapi: {},
mockServer: {},
},
resolve: { http: { headers: [] } },
organization: 'redocly-test',
files: [],
@@ -66,8 +68,6 @@ describe('getMergedConfig', () => {
},
},
"configFile": "redocly.yaml",
"features.mockServer": Object {},
"features.openapi": Object {},
"files": Array [],
"organization": "redocly-test",
"rawConfig": Object {
@@ -81,8 +81,6 @@ describe('getMergedConfig', () => {
},
},
},
"features.mockServer": Object {},
"features.openapi": Object {},
"files": Array [],
"organization": "redocly-test",
"styleguide": Object {
@@ -92,6 +90,7 @@ describe('getMergedConfig', () => {
"operation-summary": "warn",
},
},
"theme": Object {},
},
"region": undefined,
"resolve": Object {
@@ -139,6 +138,7 @@ describe('getMergedConfig', () => {
},
},
},
"theme": Object {},
}
`);
});
@@ -164,8 +164,6 @@ describe('getMergedConfig', () => {
},
},
"configFile": "redocly.yaml",
"features.mockServer": Object {},
"features.openapi": Object {},
"files": Array [],
"organization": "redocly-test",
"rawConfig": Object {
@@ -179,8 +177,6 @@ describe('getMergedConfig', () => {
},
},
},
"features.mockServer": Object {},
"features.openapi": Object {},
"files": Array [],
"organization": "redocly-test",
"styleguide": Object {
@@ -192,6 +188,7 @@ describe('getMergedConfig', () => {
"operation-summary": "error",
},
},
"theme": Object {},
},
"region": undefined,
"resolve": Object {
@@ -244,6 +241,7 @@ describe('getMergedConfig', () => {
},
},
},
"theme": Object {},
}
`);
});

View File

@@ -41,9 +41,11 @@ const rawTestConfig: RawConfig = {
resolve: {
http: { headers: [{ matches: '*', name: 'all', envVariable: 'all' }] },
},
'features.openapi': {
theme: {
openapi: {
disableSidebar: true,
},
},
};
const flatTestConfig: FlatRawConfig = {
@@ -63,9 +65,11 @@ const flatTestConfig: FlatRawConfig = {
http: { headers: [{ matches: '*', name: 'all', envVariable: 'all' }] },
doNotResolveExamples: true,
},
'features.openapi': {
theme: {
openapi: {
disableSidebar: true,
},
},
};
describe('transformConfig', () => {
@@ -85,12 +89,15 @@ describe('transformConfig', () => {
},
},
},
"features.openapi": undefined,
"styleguide": Object {
"rules": Object {
"operation-4xx-response": "warn",
},
},
"theme": Object {
"mockServer": Object {},
"openapi": Object {},
},
}
`);
});
@@ -110,12 +117,15 @@ describe('transformConfig', () => {
"root": "file.yaml",
},
},
"features.openapi": undefined,
"styleguide": Object {
"rules": Object {
"operation-4xx-response": "warn",
},
},
"theme": Object {
"mockServer": Object {},
"openapi": Object {},
},
}
`);
});
@@ -140,4 +150,55 @@ describe('transformConfig', () => {
`Do not use 'lint', 'styleguide' and flat syntax together. \nSee more about the configuration in the docs: https://redocly.com/docs/cli/configuration/ \n`
);
});
it('should throw an error if both `features.openapi` and `theme` syntaxes used together', () => {
const testRawConfig: RawConfig & DeprecatedInRawConfig = {
apis: {
'test@v1': {
root: 'root.yaml',
styleguide: {
extends: ['recommended'],
rules: { 'operation-2xx-response': 'error' },
},
},
},
'features.openapi': {
disableSidebar: true,
},
theme: {
openapi: {
disableSidebar: true,
},
},
};
expect(() => utils.transformConfig(testRawConfig)).toThrowError(
`Do not use 'features.openapi' field. Use 'theme.openapi' instead. `
);
});
it('should transform referenceDocs config into theme.openapi', () => {
const testRawConfig: RawConfig & DeprecatedInRawConfig = {
referenceDocs: {
disableSidebar: true,
},
};
expect(utils.transformConfig(testRawConfig)).toEqual({
apis: undefined,
styleguide: undefined,
theme: { mockServer: {}, openapi: { disableSidebar: true } },
});
});
it('should transform "features.openapi" config into theme.openapi', () => {
const testRawConfig: RawConfig & DeprecatedInRawConfig = {
'features.openapi': {
disableSidebar: true,
},
};
expect(utils.transformConfig(testRawConfig)).toEqual({
apis: undefined,
'features.openapi': {
disableSidebar: true,
},
styleguide: undefined,
theme: { mockServer: {}, openapi: { disableSidebar: true } },
});
});
});

View File

@@ -18,6 +18,7 @@ import type {
ResolvedStyleguideConfig,
RuleConfig,
RuleSettings,
ThemeRawConfig,
} from './types';
import { getResolveConfig } from './utils';
@@ -304,15 +305,13 @@ export class Config {
resolve: ResolveConfig;
licenseKey?: string;
region?: Region;
'features.openapi': Record<string, any>;
'features.mockServer'?: Record<string, any>;
theme: ThemeRawConfig;
organization?: string;
files: string[];
constructor(public rawConfig: ResolvedConfig, public configFile?: string) {
this.apis = rawConfig.apis || {};
this.styleguide = new StyleguideConfig(rawConfig.styleguide || {}, configFile);
this['features.openapi'] = rawConfig['features.openapi'] || {};
this['features.mockServer'] = rawConfig['features.mockServer'] || {};
this.theme = rawConfig.theme || {};
this.resolve = getResolveConfig(rawConfig?.resolve);
this.region = rawConfig.region;
this.organization = rawConfig.organization;

View File

@@ -145,16 +145,16 @@ export type DeprecatedInRawConfig = {
styleguide?: StyleguideRawConfig;
referenceDocs?: Record<string, any>;
apis?: Record<string, Api & DeprecatedInApi>;
};
} & DeprecatedFeaturesConfig;
export type Api = {
root: string;
styleguide?: ApiStyleguideRawConfig;
} & FeaturesConfig;
} & ThemeConfig;
export type DeprecatedInApi = {
lint?: ApiStyleguideRawConfig;
};
} & DeprecatedFeaturesConfig;
export type ResolvedApi = Omit<Api, 'styleguide'> & {
styleguide: ResolvedStyleguideConfig;
@@ -168,7 +168,7 @@ export type RawConfig = {
region?: Region;
organization?: string;
files?: string[];
} & FeaturesConfig;
} & ThemeConfig;
export type FlatApi = Omit<Api, 'styleguide'> &
Omit<ApiStyleguideRawConfig, 'doNotResolveExamples'>;
@@ -177,18 +177,27 @@ export type FlatRawConfig = Omit<RawConfig, 'styleguide' | 'resolve' | 'apis'> &
Omit<StyleguideRawConfig, 'doNotResolveExamples'> & {
resolve?: RawResolveConfig;
apis?: Record<string, FlatApi>;
};
} & ThemeRawConfig;
export type ResolvedConfig = Omit<RawConfig, 'apis' | 'styleguide'> & {
apis: Record<string, ResolvedApi>;
styleguide: ResolvedStyleguideConfig;
};
type FeaturesConfig = {
type DeprecatedFeaturesConfig = {
'features.openapi'?: Record<string, any>;
'features.mockServer'?: Record<string, any>;
};
export type ThemeConfig = {
theme?: ThemeRawConfig;
};
export type ThemeRawConfig = {
openapi?: Record<string, any>;
mockServer?: Record<string, any>;
};
export type RulesFields =
| 'rules'
| 'oas2Rules'

View File

@@ -19,6 +19,7 @@ import type {
ResolvedStyleguideConfig,
RulesFields,
StyleguideRawConfig,
ThemeConfig,
} from './types';
import { logger, colorize } from '../logger';
@@ -225,13 +226,9 @@ export function getMergedConfig(config: Config, apiName?: string): Config {
extendPaths,
pluginPaths,
},
'features.openapi': {
...config['features.openapi'],
...config.apis[apiName]?.['features.openapi'],
},
'features.mockServer': {
...config['features.mockServer'],
...config.apis[apiName]?.['features.mockServer'],
theme: {
...config.rawConfig.theme,
...config.apis[apiName]?.theme,
},
files: [...config.files, ...(config.apis?.[apiName]?.files ?? [])],
// TODO: merge everything else here
@@ -241,10 +238,11 @@ export function getMergedConfig(config: Config, apiName?: string): Config {
: config;
}
function checkForDeprecatedFields(
export function checkForDeprecatedFields(
deprecatedField: keyof (DeprecatedInRawConfig & RawConfig),
updatedField: keyof FlatRawConfig | undefined,
rawConfig: DeprecatedInRawConfig & RawConfig & FlatRawConfig
rawConfig: DeprecatedInRawConfig & RawConfig & FlatRawConfig,
updatedObject: keyof FlatRawConfig | undefined
): void {
const isDeprecatedFieldInApis =
rawConfig.apis &&
@@ -261,8 +259,12 @@ function checkForDeprecatedFields(
showErrorForDeprecatedField(deprecatedField, updatedField);
}
if (rawConfig[deprecatedField] && updatedObject && rawConfig[updatedObject]) {
showErrorForDeprecatedField(deprecatedField, updatedField, updatedObject);
}
if (rawConfig[deprecatedField] || isDeprecatedFieldInApis) {
showWarningForDeprecatedField(deprecatedField, updatedField);
showWarningForDeprecatedField(deprecatedField, updatedField, updatedObject);
}
}
@@ -271,16 +273,18 @@ export function transformConfig(
): RawConfig {
const migratedFields: [
keyof (DeprecatedInRawConfig & RawConfig),
keyof FlatRawConfig | undefined
keyof FlatRawConfig | undefined,
keyof ThemeConfig | undefined
][] = [
['apiDefinitions', 'apis'],
['referenceDocs', 'features.openapi'],
['lint', undefined],
['styleguide', undefined],
['apiDefinitions', 'apis', undefined],
['referenceDocs', 'openapi', 'theme'],
['lint', undefined, undefined],
['styleguide', undefined, undefined],
['features.openapi', 'openapi', 'theme'],
];
for (const [deprecatedField, updatedField] of migratedFields) {
checkForDeprecatedFields(deprecatedField, updatedField, rawConfig);
for (const [deprecatedField, updatedField, updatedObject] of migratedFields) {
checkForDeprecatedFields(deprecatedField, updatedField, rawConfig, updatedObject);
}
const { apis, apiDefinitions, referenceDocs, lint, ...rest } = rawConfig;
@@ -288,7 +292,17 @@ export function transformConfig(
const { styleguideConfig, rawConfigRest } = extractFlatConfig(rest);
return {
'features.openapi': referenceDocs,
theme: {
openapi: {
...referenceDocs,
...rawConfig['features.openapi'],
...rawConfig.theme?.openapi,
},
mockServer: {
...rawConfig['features.mockServer'],
...rawConfig.theme?.mockServer,
},
},
apis: transformApis(apis) || transformApiDefinitionsToApis(apiDefinitions),
styleguide: styleguideConfig || lint,
...rawConfigRest,

View File

@@ -136,7 +136,11 @@ export function getAdditionalPropertiesOption(opts: Record<string, any>): boolea
}
if (opts.allowAdditionalProperties !== undefined) {
showErrorForDeprecatedField('disallowAdditionalProperties', 'allowAdditionalProperties');
showErrorForDeprecatedField(
'disallowAdditionalProperties',
'allowAdditionalProperties',
undefined
);
}
showWarningForDeprecatedField('disallowAdditionalProperties', 'allowAdditionalProperties');

View File

@@ -154,8 +154,9 @@ const ConfigRoot: NodeType = {
organization: { type: 'string' },
apis: 'ConfigApis',
...RootConfigStyleguide.properties,
'features.openapi': 'ConfigReferenceDocs',
'features.mockServer': 'ConfigMockServer',
theme: 'ConfigRootTheme',
'features.openapi': 'ConfigReferenceDocs', // deprecated
'features.mockServer': 'ConfigMockServer', // deprecated
region: { enum: ['us', 'eu'] },
resolve: {
properties: {
@@ -189,8 +190,9 @@ const ConfigApisProperties: NodeType = {
lint: 'ConfigStyleguide', // deprecated
styleguide: 'ConfigStyleguide', // deprecated
...ConfigStyleguide.properties,
'features.openapi': 'ConfigReferenceDocs',
'features.mockServer': 'ConfigMockServer',
'features.openapi': 'ConfigReferenceDocs', // deprecated
'features.mockServer': 'ConfigMockServer', // deprecated
theme: 'ConfigRootTheme',
files: {
type: 'array',
items: {
@@ -212,6 +214,13 @@ const ConfigHTTP: NodeType = {
},
};
const ConfigRootTheme: NodeType = {
properties: {
openapi: 'ConfigReferenceDocs',
mockServer: 'ConfigMockServer',
},
};
const Rules: NodeType = {
properties: {},
additionalProperties: (value: unknown, key: string) => {
@@ -947,6 +956,7 @@ export const ConfigTypes: Record<string, NodeType> = {
ConfigSidebarLinks,
CommonConfigSidebarLinks,
ConfigTheme,
ConfigRootTheme,
AssertDefinition,
ThemeColors,
CommonThemeColors,

View File

@@ -211,18 +211,28 @@ export function doesYamlFileExist(filePath: string): boolean {
);
}
export function showWarningForDeprecatedField(deprecatedField: string, updatedField?: string) {
export function showWarningForDeprecatedField(
deprecatedField: string,
updatedField?: string,
updatedObject?: string
) {
logger.warn(
`The '${colorize.red(deprecatedField)}' field is deprecated. ${
updatedField ? `Use ${colorize.green(updatedField)} instead. ` : ''
updatedField
? `Use ${colorize.green(getUpdatedFieldName(updatedField, updatedObject))} instead. `
: ''
}Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`
);
}
export function showErrorForDeprecatedField(deprecatedField: string, updatedField?: string) {
export function showErrorForDeprecatedField(
deprecatedField: string,
updatedField?: string,
updatedObject?: string
) {
throw new Error(
`Do not use '${deprecatedField}' field. ${
updatedField ? `Use '${updatedField}' instead. ` : ''
updatedField ? `Use '${getUpdatedFieldName(updatedField, updatedObject)}' instead. ` : ''
}\n`
);
}
@@ -260,3 +270,7 @@ export function nextTick() {
setTimeout(resolve);
});
}
function getUpdatedFieldName(updatedField: string, updatedObject?: string) {
return `${typeof updatedObject !== 'undefined' ? `${updatedObject}.` : ''}${updatedField}`;
}