@redocly/openapi-core 1.0.0-beta.95 → 1.0.0-beta.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/config/config-resolvers.d.ts +1 -1
- package/lib/config/config-resolvers.js +31 -2
- package/lib/config/config.d.ts +4 -1
- package/lib/config/config.js +18 -12
- package/lib/format/format.js +2 -1
- package/lib/redocly/index.js +10 -26
- package/lib/redocly/registry-api-types.d.ts +1 -0
- package/lib/redocly/registry-api.d.ts +1 -1
- package/lib/redocly/registry-api.js +2 -1
- package/lib/rules/common/assertions/index.js +1 -1
- package/lib/types/oas2.js +3 -1
- package/lib/types/oas3.js +4 -2
- package/lib/types/oas3_1.js +3 -1
- package/lib/types/redocly-yaml.js +2 -0
- package/lib/typings/openapi.d.ts +2 -0
- package/lib/typings/swagger.d.ts +2 -0
- package/lib/utils.d.ts +0 -1
- package/lib/utils.js +3 -3
- package/package.json +5 -5
- package/src/benchmark/benchmark.js +1 -1
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +18 -1
- package/src/config/__tests__/config-resolvers.test.ts +31 -0
- package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +5 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +7 -0
- package/src/config/config-resolvers.ts +58 -3
- package/src/config/config.ts +21 -12
- package/src/format/format.ts +2 -1
- package/src/redocly/index.ts +12 -41
- package/src/redocly/registry-api-types.ts +1 -0
- package/src/redocly/registry-api.ts +2 -0
- package/src/rules/common/assertions/index.ts +1 -1
- package/src/types/oas2.ts +2 -0
- package/src/types/oas3.ts +3 -1
- package/src/types/oas3_1.ts +2 -0
- package/src/types/redocly-yaml.ts +2 -0
- package/src/typings/openapi.ts +2 -0
- package/src/typings/swagger.ts +2 -0
- package/src/utils.ts +3 -2
- package/tsconfig.tsbuildinfo +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ export declare function resolveApis({ rawConfig, configPath, resolver, }: {
|
|
|
8
8
|
configPath?: string;
|
|
9
9
|
resolver?: BaseResolver;
|
|
10
10
|
}): Promise<Record<string, ResolvedApi>>;
|
|
11
|
-
export declare function resolveLint(
|
|
11
|
+
export declare function resolveLint(lintOpts: {
|
|
12
12
|
lintConfig?: LintRawConfig;
|
|
13
13
|
configPath?: string;
|
|
14
14
|
resolver?: BaseResolver;
|
|
@@ -141,7 +141,7 @@ function resolveApis({ rawConfig, configPath = '', resolver, }) {
|
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
143
|
exports.resolveApis = resolveApis;
|
|
144
|
-
function
|
|
144
|
+
function resolveAndMergeNestedLint({ lintConfig, configPath = '', resolver = new resolve_1.BaseResolver(), }, parentConfigPaths = [], extendPaths = []) {
|
|
145
145
|
var _a, _b, _c;
|
|
146
146
|
return __awaiter(this, void 0, void 0, function* () {
|
|
147
147
|
if (parentConfigPaths.includes(configPath)) {
|
|
@@ -162,7 +162,7 @@ function resolveLint({ lintConfig, configPath = '', resolver = new resolve_1.Bas
|
|
|
162
162
|
? new URL(presetItem, configPath).href
|
|
163
163
|
: path.resolve(path.dirname(configPath), presetItem);
|
|
164
164
|
const extendedLintConfig = yield loadExtendLintConfig(pathItem, resolver);
|
|
165
|
-
return yield
|
|
165
|
+
return yield resolveAndMergeNestedLint({
|
|
166
166
|
lintConfig: extendedLintConfig,
|
|
167
167
|
configPath: pathItem,
|
|
168
168
|
resolver: resolver,
|
|
@@ -175,6 +175,12 @@ function resolveLint({ lintConfig, configPath = '', resolver = new resolve_1.Bas
|
|
|
175
175
|
return Object.assign(Object.assign({}, lint), { extendPaths: (_c = lint.extendPaths) === null || _c === void 0 ? void 0 : _c.filter((path) => path && !ref_utils_1.isAbsoluteUrl(path)), plugins: utils_1.getUniquePlugins(mergedPlugins), recommendedFallback: lintConfig === null || lintConfig === void 0 ? void 0 : lintConfig.recommendedFallback, doNotResolveExamples: lintConfig === null || lintConfig === void 0 ? void 0 : lintConfig.doNotResolveExamples });
|
|
176
176
|
});
|
|
177
177
|
}
|
|
178
|
+
function resolveLint(lintOpts, parentConfigPaths = [], extendPaths = []) {
|
|
179
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
180
|
+
const resolvedLint = yield resolveAndMergeNestedLint(lintOpts, parentConfigPaths, extendPaths);
|
|
181
|
+
return Object.assign(Object.assign({}, resolvedLint), { rules: resolvedLint.rules && groupLintAssertionRules(resolvedLint.rules) });
|
|
182
|
+
});
|
|
183
|
+
}
|
|
178
184
|
exports.resolveLint = resolveLint;
|
|
179
185
|
function resolvePreset(presetName, plugins) {
|
|
180
186
|
var _a;
|
|
@@ -211,3 +217,26 @@ function getMergedLintRawConfig(configLint, apiLint) {
|
|
|
211
217
|
const resultLint = Object.assign(Object.assign(Object.assign({}, configLint), apiLint), { rules: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.rules), apiLint === null || apiLint === void 0 ? void 0 : apiLint.rules), oas2Rules: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas2Rules), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas2Rules), oas3_0Rules: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_0Rules), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_0Rules), oas3_1Rules: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_1Rules), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_1Rules), preprocessors: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.preprocessors), apiLint === null || apiLint === void 0 ? void 0 : apiLint.preprocessors), oas2Preprocessors: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas2Preprocessors), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas2Preprocessors), oas3_0Preprocessors: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_0Preprocessors), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_0Preprocessors), oas3_1Preprocessors: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_1Preprocessors), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_1Preprocessors), decorators: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.decorators), apiLint === null || apiLint === void 0 ? void 0 : apiLint.decorators), oas2Decorators: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas2Decorators), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas2Decorators), oas3_0Decorators: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_0Decorators), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_0Decorators), oas3_1Decorators: Object.assign(Object.assign({}, configLint === null || configLint === void 0 ? void 0 : configLint.oas3_1Decorators), apiLint === null || apiLint === void 0 ? void 0 : apiLint.oas3_1Decorators), recommendedFallback: (apiLint === null || apiLint === void 0 ? void 0 : apiLint.extends) ? false : configLint.recommendedFallback });
|
|
212
218
|
return resultLint;
|
|
213
219
|
}
|
|
220
|
+
function groupLintAssertionRules(rules) {
|
|
221
|
+
if (!rules) {
|
|
222
|
+
return rules;
|
|
223
|
+
}
|
|
224
|
+
// Create a new record to avoid mutating original
|
|
225
|
+
const transformedRules = {};
|
|
226
|
+
// Collect assertion rules
|
|
227
|
+
const assertions = [];
|
|
228
|
+
for (const [ruleKey, rule] of Object.entries(rules)) {
|
|
229
|
+
if (ruleKey.startsWith('assert/') && typeof rule === 'object' && rule !== null) {
|
|
230
|
+
const assertion = rule;
|
|
231
|
+
assertions.push(Object.assign(Object.assign({}, assertion), { assertionId: ruleKey.replace('assert/', '') }));
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
// If it's not an assertion, keep it as is
|
|
235
|
+
transformedRules[ruleKey] = rule;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (assertions.length > 0) {
|
|
239
|
+
transformedRules.assertions = assertions;
|
|
240
|
+
}
|
|
241
|
+
return transformedRules;
|
|
242
|
+
}
|
package/lib/config/config.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { NormalizedProblem } from '../walk';
|
|
2
3
|
import { OasVersion, OasMajorVersion, Oas2RuleSet, Oas3RuleSet } from '../oas-types';
|
|
3
4
|
import type { NodeType } from '../types';
|
|
4
5
|
import type { DecoratorConfig, Plugin, PreprocessorConfig, Region, ResolveConfig, ResolvedApi, ResolvedConfig, ResolvedLintConfig, RuleConfig } from './types';
|
|
6
|
+
export declare const env: NodeJS.ProcessEnv;
|
|
5
7
|
export declare const IGNORE_FILE = ".redocly.lint-ignore.yaml";
|
|
6
8
|
export declare const DEFAULT_REGION = "us";
|
|
7
9
|
export declare const DOMAINS: {
|
|
8
|
-
|
|
10
|
+
us: string;
|
|
11
|
+
eu: string;
|
|
9
12
|
};
|
|
10
13
|
export declare const AVAILABLE_REGIONS: Region[];
|
|
11
14
|
export declare class LintConfig {
|
package/lib/config/config.js
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Config = exports.LintConfig = exports.AVAILABLE_REGIONS = exports.DOMAINS = exports.DEFAULT_REGION = exports.IGNORE_FILE = void 0;
|
|
3
|
+
exports.Config = exports.LintConfig = exports.AVAILABLE_REGIONS = exports.DOMAINS = exports.DEFAULT_REGION = exports.IGNORE_FILE = exports.env = void 0;
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const js_yaml_1 = require("../js-yaml");
|
|
7
7
|
const utils_1 = require("../utils");
|
|
8
8
|
const oas_types_1 = require("../oas-types");
|
|
9
9
|
const utils_2 = require("./utils");
|
|
10
|
+
// Alias environment here so this file can work in browser environments too.
|
|
11
|
+
exports.env = typeof process !== 'undefined' ? process.env || {} : {};
|
|
10
12
|
exports.IGNORE_FILE = '.redocly.lint-ignore.yaml';
|
|
11
13
|
const IGNORE_BANNER = `# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
|
|
12
14
|
`# See https://redoc.ly/docs/cli/ for more information.\n`;
|
|
13
15
|
exports.DEFAULT_REGION = 'us';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
19
|
-
// FIXME: temporary fix for our lab environments
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
function getDomains() {
|
|
17
|
+
const domains = {
|
|
18
|
+
us: 'redocly.com',
|
|
19
|
+
eu: 'eu.redocly.com',
|
|
20
|
+
};
|
|
21
|
+
// FIXME: temporary fix for our lab environments
|
|
22
|
+
const domain = exports.env.REDOCLY_DOMAIN;
|
|
23
|
+
if (domain === null || domain === void 0 ? void 0 : domain.endsWith('.redocly.host')) {
|
|
24
|
+
domains[domain.split('.')[0]] = domain;
|
|
25
|
+
}
|
|
26
|
+
if (domain === 'redoc.online') {
|
|
27
|
+
domains[domain] = domain;
|
|
28
|
+
}
|
|
29
|
+
return domains;
|
|
25
30
|
}
|
|
31
|
+
exports.DOMAINS = getDomains();
|
|
26
32
|
exports.AVAILABLE_REGIONS = Object.keys(exports.DOMAINS);
|
|
27
33
|
class LintConfig {
|
|
28
34
|
constructor(rawConfig, configFile) {
|
package/lib/format/format.js
CHANGED
|
@@ -5,6 +5,7 @@ const path = require("path");
|
|
|
5
5
|
const colorette_1 = require("colorette");
|
|
6
6
|
const coreVersion = require('../../package.json').version;
|
|
7
7
|
const codeframes_1 = require("./codeframes");
|
|
8
|
+
const config_1 = require("../config");
|
|
8
9
|
const ERROR_MESSAGE = {
|
|
9
10
|
INVALID_SEVERITY_LEVEL: 'Invalid severity level; accepted values: error or warn',
|
|
10
11
|
};
|
|
@@ -131,7 +132,7 @@ function formatProblems(problems, opts) {
|
|
|
131
132
|
? Object.assign(Object.assign({}, p.from), { source: {
|
|
132
133
|
ref: path.relative(cwd, ((_a = p.from) === null || _a === void 0 ? void 0 : _a.source.absoluteRef) || cwd),
|
|
133
134
|
} }) : undefined });
|
|
134
|
-
if (
|
|
135
|
+
if (config_1.env.FORMAT_JSON_WITH_CODEFRAMES) {
|
|
135
136
|
const location = p.location[0]; // TODO: support multiple locations
|
|
136
137
|
const loc = codeframes_1.getLineColLocation(location);
|
|
137
138
|
problem.codeframe = codeframes_1.getCodeframe(loc, color);
|
package/lib/redocly/index.js
CHANGED
|
@@ -18,27 +18,21 @@ const registry_api_1 = require("./registry-api");
|
|
|
18
18
|
const config_1 = require("../config/config");
|
|
19
19
|
const utils_1 = require("../utils");
|
|
20
20
|
const TOKEN_FILENAME = '.redocly-config.json';
|
|
21
|
-
let REDOCLY_DOMAIN; // workaround for the isRedoclyRegistryURL, see more below
|
|
22
21
|
class RedoclyClient {
|
|
23
22
|
constructor(region) {
|
|
24
23
|
this.accessTokens = {};
|
|
25
24
|
this.region = this.loadRegion(region);
|
|
26
25
|
this.loadTokens();
|
|
27
|
-
this.domain = region ? config_1.DOMAINS[region] :
|
|
28
|
-
|
|
29
|
-
* We can't use process.env here because it is replaced by a const in some client-side bundles,
|
|
30
|
-
* which breaks assignment.
|
|
31
|
-
*/
|
|
32
|
-
REDOCLY_DOMAIN = this.domain; // isRedoclyRegistryURL depends on the value to be set
|
|
26
|
+
this.domain = region ? config_1.DOMAINS[region] : config_1.env.REDOCLY_DOMAIN || config_1.DOMAINS[config_1.DEFAULT_REGION];
|
|
27
|
+
config_1.env.REDOCLY_DOMAIN = this.domain; // isRedoclyRegistryURL depends on the value to be set
|
|
33
28
|
this.registryApi = new registry_api_1.RegistryApi(this.accessTokens, this.region);
|
|
34
29
|
}
|
|
35
30
|
loadRegion(region) {
|
|
36
31
|
if (region && !config_1.DOMAINS[region]) {
|
|
37
|
-
|
|
38
|
-
process.exit(1);
|
|
32
|
+
throw new Error(`Invalid argument: region in config file.\nGiven: ${colorette_1.green(region)}, choices: "us", "eu".`);
|
|
39
33
|
}
|
|
40
|
-
if (
|
|
41
|
-
return (config_1.AVAILABLE_REGIONS.find((region) => config_1.DOMAINS[region] ===
|
|
34
|
+
if (config_1.env.REDOCLY_DOMAIN) {
|
|
35
|
+
return (config_1.AVAILABLE_REGIONS.find((region) => config_1.DOMAINS[region] === config_1.env.REDOCLY_DOMAIN) || config_1.DEFAULT_REGION);
|
|
42
36
|
}
|
|
43
37
|
return region || config_1.DEFAULT_REGION;
|
|
44
38
|
}
|
|
@@ -54,13 +48,7 @@ class RedoclyClient {
|
|
|
54
48
|
}
|
|
55
49
|
getAuthorizationHeader() {
|
|
56
50
|
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
-
|
|
58
|
-
// print this only if there is token but invalid
|
|
59
|
-
if (token && !this.isAuthorizedWithRedoclyByRegion()) {
|
|
60
|
-
process.stderr.write(`${colorette_1.yellow('Warning:')} invalid Redocly API key. Use "npx @redocly/openapi-cli login" to provide your API key\n`);
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
return token;
|
|
51
|
+
return this.accessTokens[this.region];
|
|
64
52
|
});
|
|
65
53
|
}
|
|
66
54
|
// </backward compatibility: portal>
|
|
@@ -76,8 +64,8 @@ class RedoclyClient {
|
|
|
76
64
|
[this.region]: credentials.token,
|
|
77
65
|
})));
|
|
78
66
|
}
|
|
79
|
-
if (
|
|
80
|
-
this.setAccessTokens(Object.assign(Object.assign({}, this.accessTokens), { [this.region]:
|
|
67
|
+
if (config_1.env.REDOCLY_AUTHORIZATION) {
|
|
68
|
+
this.setAccessTokens(Object.assign(Object.assign({}, this.accessTokens), { [this.region]: config_1.env.REDOCLY_AUTHORIZATION }));
|
|
81
69
|
}
|
|
82
70
|
}
|
|
83
71
|
getAllTokens() {
|
|
@@ -133,19 +121,16 @@ class RedoclyClient {
|
|
|
133
121
|
login(accessToken, verbose = false) {
|
|
134
122
|
return __awaiter(this, void 0, void 0, function* () {
|
|
135
123
|
const credentialsPath = path_1.resolve(os_1.homedir(), TOKEN_FILENAME);
|
|
136
|
-
process.stdout.write(colorette_1.gray('\n Logging in...\n'));
|
|
137
124
|
try {
|
|
138
125
|
yield this.verifyToken(accessToken, this.region, verbose);
|
|
139
126
|
}
|
|
140
127
|
catch (err) {
|
|
141
|
-
|
|
142
|
-
process.exit(1);
|
|
128
|
+
throw new Error('Authorization failed. Please check if you entered a valid API key.');
|
|
143
129
|
}
|
|
144
130
|
const credentials = Object.assign(Object.assign({}, this.readCredentialsFile(credentialsPath)), { [this.region]: accessToken, token: accessToken });
|
|
145
131
|
this.accessTokens = credentials;
|
|
146
132
|
this.registryApi.setAccessTokens(credentials);
|
|
147
133
|
fs_1.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2));
|
|
148
|
-
process.stdout.write(colorette_1.green(' Authorization confirmed. ✅\n\n'));
|
|
149
134
|
});
|
|
150
135
|
}
|
|
151
136
|
logout() {
|
|
@@ -153,12 +138,11 @@ class RedoclyClient {
|
|
|
153
138
|
if (fs_1.existsSync(credentialsPath)) {
|
|
154
139
|
fs_1.unlinkSync(credentialsPath);
|
|
155
140
|
}
|
|
156
|
-
process.stdout.write('Logged out from the Redocly account. ✋\n');
|
|
157
141
|
}
|
|
158
142
|
}
|
|
159
143
|
exports.RedoclyClient = RedoclyClient;
|
|
160
144
|
function isRedoclyRegistryURL(link) {
|
|
161
|
-
const domain =
|
|
145
|
+
const domain = config_1.env.REDOCLY_DOMAIN || config_1.DOMAINS[config_1.DEFAULT_REGION];
|
|
162
146
|
const legacyDomain = domain === 'redocly.com' ? 'redoc.ly' : domain;
|
|
163
147
|
if (!link.startsWith(`https://api.${domain}/registry/`) &&
|
|
164
148
|
!link.startsWith(`https://api.${legacyDomain}/registry/`)) {
|
|
@@ -13,5 +13,5 @@ export declare class RegistryApi {
|
|
|
13
13
|
organizations: string[];
|
|
14
14
|
}>;
|
|
15
15
|
prepareFileUpload({ organizationId, name, version, filesHash, filename, isUpsert, }: RegistryApiTypes.PrepareFileuploadParams): Promise<RegistryApiTypes.PrepareFileuploadOKResponse>;
|
|
16
|
-
pushApi({ organizationId, name, version, rootFilePath, filePaths, branch, isUpsert, }: RegistryApiTypes.PushApiParams): Promise<void>;
|
|
16
|
+
pushApi({ organizationId, name, version, rootFilePath, filePaths, branch, isUpsert, isPublic, }: RegistryApiTypes.PushApiParams): Promise<void>;
|
|
17
17
|
}
|
|
@@ -80,7 +80,7 @@ class RegistryApi {
|
|
|
80
80
|
throw new Error('Could not prepare file upload');
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
|
-
pushApi({ organizationId, name, version, rootFilePath, filePaths, branch, isUpsert, }) {
|
|
83
|
+
pushApi({ organizationId, name, version, rootFilePath, filePaths, branch, isUpsert, isPublic, }) {
|
|
84
84
|
return __awaiter(this, void 0, void 0, function* () {
|
|
85
85
|
const response = yield this.request(`/${organizationId}/${name}/${version}`, {
|
|
86
86
|
method: 'PUT',
|
|
@@ -93,6 +93,7 @@ class RegistryApi {
|
|
|
93
93
|
filePaths,
|
|
94
94
|
branch,
|
|
95
95
|
isUpsert,
|
|
96
|
+
isPublic,
|
|
96
97
|
}),
|
|
97
98
|
}, this.region);
|
|
98
99
|
if (response.ok) {
|
|
@@ -7,7 +7,7 @@ const Assertions = (opts) => {
|
|
|
7
7
|
let visitors = [];
|
|
8
8
|
// As 'Assertions' has an array of asserts,
|
|
9
9
|
// that array spreads into an 'opts' object on init rules phase here
|
|
10
|
-
// https://github.com/Redocly/
|
|
10
|
+
// https://github.com/Redocly/redocly-cli/blob/master/packages/core/src/config/config.ts#L311
|
|
11
11
|
// that is why we need to iterate through 'opts' values;
|
|
12
12
|
// before - filter only object 'opts' values
|
|
13
13
|
const assertions = Object.values(opts).filter((opt) => typeof opt === 'object' && opt !== null);
|
package/lib/types/oas2.js
CHANGED
|
@@ -80,7 +80,8 @@ const Operation = {
|
|
|
80
80
|
deprecated: { type: 'boolean' },
|
|
81
81
|
security: _1.listOf('SecurityRequirement'),
|
|
82
82
|
'x-codeSamples': _1.listOf('XCodeSample'),
|
|
83
|
-
'x-code-samples': _1.listOf('XCodeSample'),
|
|
83
|
+
'x-code-samples': _1.listOf('XCodeSample'),
|
|
84
|
+
'x-hideTryItPanel': { type: 'boolean' },
|
|
84
85
|
},
|
|
85
86
|
required: ['responses'],
|
|
86
87
|
};
|
|
@@ -275,6 +276,7 @@ const Schema = {
|
|
|
275
276
|
xml: 'Xml',
|
|
276
277
|
externalDocs: 'ExternalDocs',
|
|
277
278
|
example: { isExample: true },
|
|
279
|
+
'x-tags': { type: 'array', items: { type: 'string' } },
|
|
278
280
|
},
|
|
279
281
|
};
|
|
280
282
|
const SchemaProperties = {
|
package/lib/types/oas3.js
CHANGED
|
@@ -149,7 +149,8 @@ const Operation = {
|
|
|
149
149
|
deprecated: { type: 'boolean' },
|
|
150
150
|
callbacks: _1.mapOf('Callback'),
|
|
151
151
|
'x-codeSamples': _1.listOf('XCodeSample'),
|
|
152
|
-
'x-code-samples': _1.listOf('XCodeSample'),
|
|
152
|
+
'x-code-samples': _1.listOf('XCodeSample'),
|
|
153
|
+
'x-hideTryItPanel': { type: 'boolean' },
|
|
153
154
|
},
|
|
154
155
|
required: ['responses'],
|
|
155
156
|
};
|
|
@@ -292,6 +293,7 @@ const Schema = {
|
|
|
292
293
|
xml: 'Xml',
|
|
293
294
|
example: { isExample: true },
|
|
294
295
|
deprecated: { type: 'boolean' },
|
|
296
|
+
'x-tags': { type: 'array', items: { type: 'string' } },
|
|
295
297
|
},
|
|
296
298
|
};
|
|
297
299
|
const Xml = {
|
|
@@ -344,7 +346,7 @@ const ImplicitFlow = {
|
|
|
344
346
|
scopes: { type: 'object', additionalProperties: { type: 'string' } },
|
|
345
347
|
authorizationUrl: { type: 'string' },
|
|
346
348
|
},
|
|
347
|
-
required: ['authorizationUrl', 'scopes']
|
|
349
|
+
required: ['authorizationUrl', 'scopes'],
|
|
348
350
|
};
|
|
349
351
|
const PasswordFlow = {
|
|
350
352
|
properties: {
|
package/lib/types/oas3_1.js
CHANGED
|
@@ -71,7 +71,8 @@ const Operation = {
|
|
|
71
71
|
deprecated: { type: 'boolean' },
|
|
72
72
|
callbacks: _1.mapOf('Callback'),
|
|
73
73
|
'x-codeSamples': _1.listOf('XCodeSample'),
|
|
74
|
-
'x-code-samples': _1.listOf('XCodeSample'),
|
|
74
|
+
'x-code-samples': _1.listOf('XCodeSample'),
|
|
75
|
+
'x-hideTryItPanel': { type: 'boolean' },
|
|
75
76
|
},
|
|
76
77
|
};
|
|
77
78
|
const Schema = {
|
|
@@ -164,6 +165,7 @@ const Schema = {
|
|
|
164
165
|
deprecated: { type: 'boolean' },
|
|
165
166
|
const: null,
|
|
166
167
|
$comment: { type: 'string' },
|
|
168
|
+
'x-tags': { type: 'array', items: { type: 'string' } },
|
|
167
169
|
},
|
|
168
170
|
};
|
|
169
171
|
const SecurityScheme = {
|
|
@@ -414,6 +414,7 @@ const ConfigReferenceDocs = {
|
|
|
414
414
|
hideSchemaPattern: { type: 'boolean' },
|
|
415
415
|
hideSchemaTitles: { type: 'boolean' },
|
|
416
416
|
hideSingleRequestSampleTab: { type: 'boolean' },
|
|
417
|
+
hideTryItPanel: { type: 'boolean' },
|
|
417
418
|
htmlTemplate: { type: 'string' },
|
|
418
419
|
jsonSampleExpandLevel: { type: 'string' },
|
|
419
420
|
labels: 'ConfigLabels',
|
|
@@ -433,6 +434,7 @@ const ConfigReferenceDocs = {
|
|
|
433
434
|
routingBasePath: { type: 'string' },
|
|
434
435
|
samplesTabsMaxCount: { type: 'number' },
|
|
435
436
|
schemaExpansionLevel: { type: 'string' },
|
|
437
|
+
schemaDefinitionsTagName: { type: 'string' },
|
|
436
438
|
scrollYOffset: { type: 'string' },
|
|
437
439
|
searchAutoExpand: { type: 'boolean' },
|
|
438
440
|
searchFieldLevelBoost: { type: 'number' },
|
package/lib/typings/openapi.d.ts
CHANGED
|
@@ -72,6 +72,7 @@ export interface Oas3Operation {
|
|
|
72
72
|
servers?: Oas3Server[];
|
|
73
73
|
'x-codeSamples'?: Oas3XCodeSample[];
|
|
74
74
|
'x-code-samples'?: Oas3XCodeSample[];
|
|
75
|
+
'x-hideTryItPanel'?: boolean;
|
|
75
76
|
}
|
|
76
77
|
export interface Oas3Parameter {
|
|
77
78
|
name: string;
|
|
@@ -144,6 +145,7 @@ export interface Oas3Schema {
|
|
|
144
145
|
enum?: any[];
|
|
145
146
|
example?: any;
|
|
146
147
|
xml?: Oas3Xml;
|
|
148
|
+
'x-tags'?: string[];
|
|
147
149
|
}
|
|
148
150
|
export interface Oas3_1Schema extends Oas3Schema {
|
|
149
151
|
examples?: any[];
|
package/lib/typings/swagger.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ export interface Oas2Operation {
|
|
|
71
71
|
security?: Oas2SecurityRequirement[];
|
|
72
72
|
'x-codeSamples'?: Oas2XCodeSample[];
|
|
73
73
|
'x-code-samples'?: Oas2XCodeSample[];
|
|
74
|
+
'x-hideTryItPanel'?: boolean;
|
|
74
75
|
}
|
|
75
76
|
export declare type Oas2Parameter = Oas2BodyParameter | Oas2SimpleParameter;
|
|
76
77
|
export interface Oas2BodyParameter {
|
|
@@ -166,6 +167,7 @@ export interface Oas2Schema {
|
|
|
166
167
|
enum?: any[];
|
|
167
168
|
example?: any;
|
|
168
169
|
xml?: Oas2Xml;
|
|
170
|
+
'x-tags'?: string[];
|
|
169
171
|
}
|
|
170
172
|
export declare type Oas2ParameterLocation = 'query' | 'header' | 'path' | 'formData';
|
|
171
173
|
export interface Oas2Responses {
|
package/lib/utils.d.ts
CHANGED
|
@@ -22,7 +22,6 @@ export declare function readFileFromUrl(url: string, config: HttpResolveConfig):
|
|
|
22
22
|
body: any;
|
|
23
23
|
mimeType: any;
|
|
24
24
|
}>;
|
|
25
|
-
export declare function match(url: string, pattern: string): boolean;
|
|
26
25
|
export declare function pickObjectProps<T extends Record<string, unknown>>(object: T, keys: Array<string>): T;
|
|
27
26
|
export declare function omitObjectProps<T extends Record<string, unknown>>(object: T, keys: Array<string>): T;
|
|
28
27
|
export declare function splitCamelCaseIntoWords(str: string): Set<string>;
|
package/lib/utils.js
CHANGED
|
@@ -9,12 +9,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.
|
|
12
|
+
exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.notUndefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
|
|
13
13
|
const fs = require("fs");
|
|
14
14
|
const minimatch = require("minimatch");
|
|
15
15
|
const node_fetch_1 = require("node-fetch");
|
|
16
16
|
const pluralize = require("pluralize");
|
|
17
17
|
const js_yaml_1 = require("./js-yaml");
|
|
18
|
+
const config_1 = require("./config");
|
|
18
19
|
var js_yaml_2 = require("./js-yaml");
|
|
19
20
|
Object.defineProperty(exports, "parseYaml", { enumerable: true, get: function () { return js_yaml_2.parseYaml; } });
|
|
20
21
|
Object.defineProperty(exports, "stringifyYaml", { enumerable: true, get: function () { return js_yaml_2.stringifyYaml; } });
|
|
@@ -56,7 +57,7 @@ function readFileFromUrl(url, config) {
|
|
|
56
57
|
for (const header of config.headers) {
|
|
57
58
|
if (match(url, header.matches)) {
|
|
58
59
|
headers[header.name] =
|
|
59
|
-
header.envVariable !== undefined ?
|
|
60
|
+
header.envVariable !== undefined ? config_1.env[header.envVariable] || '' : header.value;
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
const req = yield (config.customFetch || node_fetch_1.default)(url, {
|
|
@@ -76,7 +77,6 @@ function match(url, pattern) {
|
|
|
76
77
|
}
|
|
77
78
|
return minimatch(url, pattern);
|
|
78
79
|
}
|
|
79
|
-
exports.match = match;
|
|
80
80
|
function pickObjectProps(object, keys) {
|
|
81
81
|
return Object.fromEntries(keys.filter((key) => key in object).map((key) => [key, object[key]]));
|
|
82
82
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/openapi-core",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.98",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"engines": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
|
-
"url": "https://github.com/Redocly/
|
|
13
|
+
"url": "https://github.com/Redocly/redocly-cli.git"
|
|
14
14
|
},
|
|
15
15
|
"browser": {
|
|
16
16
|
"fs": false,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"os": false,
|
|
19
19
|
"node-fetch": false
|
|
20
20
|
},
|
|
21
|
-
"homepage": "https://github.com/Redocly/
|
|
21
|
+
"homepage": "https://github.com/Redocly/redocly-cli",
|
|
22
22
|
"keywords": [
|
|
23
23
|
"linter",
|
|
24
24
|
"OpenAPI",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"js-levenshtein": "^1.1.6",
|
|
40
40
|
"js-yaml": "^4.1.0",
|
|
41
41
|
"lodash.isequal": "^4.5.0",
|
|
42
|
-
"minimatch": "^
|
|
42
|
+
"minimatch": "^5.0.1",
|
|
43
43
|
"node-fetch": "^2.6.1",
|
|
44
44
|
"pluralize": "^8.0.0",
|
|
45
45
|
"yaml-ast-parser": "0.0.43"
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@types/js-levenshtein": "^1.1.0",
|
|
49
49
|
"@types/js-yaml": "^4.0.3",
|
|
50
50
|
"@types/lodash.isequal": "^4.5.5",
|
|
51
|
-
"@types/minimatch": "^3.0.
|
|
51
|
+
"@types/minimatch": "^3.0.5",
|
|
52
52
|
"@types/node-fetch": "^2.5.7",
|
|
53
53
|
"@types/pluralize": "^0.0.29",
|
|
54
54
|
"typescript": "^4.0.5"
|
|
@@ -29,7 +29,7 @@ function prepareRevision(revision) {
|
|
|
29
29
|
|
|
30
30
|
// Returns the complete git hash for a given git revision reference.
|
|
31
31
|
const hash = exec(`git rev-parse "${revision}"`);
|
|
32
|
-
const dir = path.join(os.tmpdir(), '
|
|
32
|
+
const dir = path.join(os.tmpdir(), 'redocly-cli-benchmark', hash);
|
|
33
33
|
if (fs.existsSync(dir)) {
|
|
34
34
|
fs.rmdirSync(dir, { recursive: true});
|
|
35
35
|
}
|
|
@@ -103,7 +103,24 @@ Object {
|
|
|
103
103
|
"preprocessors": Object {},
|
|
104
104
|
"recommendedFallback": undefined,
|
|
105
105
|
"rules": Object {
|
|
106
|
-
"assertions":
|
|
106
|
+
"assertions": Array [
|
|
107
|
+
Object {
|
|
108
|
+
"assertionId": "path-item-get-defined",
|
|
109
|
+
"defined": true,
|
|
110
|
+
"message": "Every path item must have a GET operation.",
|
|
111
|
+
"property": "get",
|
|
112
|
+
"subject": "PathItem",
|
|
113
|
+
},
|
|
114
|
+
Object {
|
|
115
|
+
"assertionId": "tag-description",
|
|
116
|
+
"message": "Tag description must be at least 13 characters and end with a full stop.",
|
|
117
|
+
"minLength": 13,
|
|
118
|
+
"pattern": "/\\\\.$/",
|
|
119
|
+
"property": "description",
|
|
120
|
+
"severity": "error",
|
|
121
|
+
"subject": "Tag",
|
|
122
|
+
},
|
|
123
|
+
],
|
|
107
124
|
"boolean-parameter-prefixes": "error",
|
|
108
125
|
"info-contact": "off",
|
|
109
126
|
"info-description": "warn",
|
|
@@ -130,6 +130,37 @@ describe('resolveLint', () => {
|
|
|
130
130
|
expect(lint).toMatchSnapshot();
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
+
it('should correctly merge assertions from nested config', async () => {
|
|
134
|
+
const lintConfig = {
|
|
135
|
+
extends: ['local-config-with-file.yaml'],
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const lint = await resolveLint({
|
|
139
|
+
lintConfig,
|
|
140
|
+
configPath,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
expect(Array.isArray(lint.rules?.assertions)).toEqual(true);
|
|
144
|
+
expect(lint.rules?.assertions).toMatchObject( [
|
|
145
|
+
{
|
|
146
|
+
subject: 'PathItem',
|
|
147
|
+
property: 'get',
|
|
148
|
+
message: 'Every path item must have a GET operation.',
|
|
149
|
+
defined: true,
|
|
150
|
+
assertionId: 'path-item-get-defined'
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
subject: 'Tag',
|
|
154
|
+
property: 'description',
|
|
155
|
+
message: 'Tag description must be at least 13 characters and end with a full stop.',
|
|
156
|
+
severity: 'error',
|
|
157
|
+
minLength: 13,
|
|
158
|
+
pattern: '/\\.$/',
|
|
159
|
+
assertionId: 'tag-description'
|
|
160
|
+
}
|
|
161
|
+
])
|
|
162
|
+
});
|
|
163
|
+
|
|
133
164
|
it('should resolve extends with url file config witch contains path to nested config', async () => {
|
|
134
165
|
const lintConfig = {
|
|
135
166
|
// This points to ./fixtures/resolve-remote-configs/remote-config.yaml
|
|
@@ -2,6 +2,13 @@ lint:
|
|
|
2
2
|
rules:
|
|
3
3
|
no-invalid-media-type-examples: warn
|
|
4
4
|
operation-4xx-response: off
|
|
5
|
+
assert/tag-description:
|
|
6
|
+
subject: Tag
|
|
7
|
+
property: description
|
|
8
|
+
message: Tag description must be at least 13 characters and end with a full stop.
|
|
9
|
+
severity: error
|
|
10
|
+
minLength: 13
|
|
11
|
+
pattern: /\.$/
|
|
5
12
|
plugins:
|
|
6
13
|
- plugin.js
|
|
7
14
|
- api/plugin.js
|