@redocly/openapi-core 1.0.0-beta.68 → 1.0.0-beta.72
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/__tests__/lint.test.ts +1 -1
- package/__tests__/login.test.ts +17 -0
- package/lib/bundle.d.ts +4 -0
- package/lib/bundle.js +9 -3
- package/lib/config/all.js +2 -0
- package/lib/config/config.d.ts +10 -0
- package/lib/config/config.js +7 -1
- package/lib/config/load.js +17 -8
- package/lib/index.d.ts +2 -2
- package/lib/lint.js +2 -0
- package/lib/redocly/index.d.ts +26 -20
- package/lib/redocly/index.js +83 -214
- package/lib/redocly/registry-api-types.d.ts +28 -0
- package/lib/redocly/registry-api-types.js +2 -0
- package/lib/redocly/registry-api.d.ts +14 -0
- package/lib/redocly/registry-api.js +105 -0
- package/lib/rules/common/no-invalid-parameter-examples.d.ts +1 -0
- package/lib/rules/common/no-invalid-parameter-examples.js +25 -0
- package/lib/rules/common/no-invalid-schema-examples.d.ts +1 -0
- package/lib/rules/common/no-invalid-schema-examples.js +23 -0
- package/lib/rules/common/paths-kebab-case.js +1 -1
- package/lib/rules/common/registry-dependencies.js +4 -7
- package/lib/rules/oas2/index.d.ts +2 -0
- package/lib/rules/oas2/index.js +4 -0
- package/lib/rules/oas3/index.js +4 -0
- package/lib/rules/oas3/no-invalid-media-type-examples.js +5 -26
- package/lib/rules/utils.d.ts +3 -0
- package/lib/rules/utils.js +26 -1
- package/lib/typings/openapi.d.ts +3 -0
- package/lib/utils.d.ts +1 -0
- package/lib/utils.js +5 -1
- package/lib/walk.d.ts +2 -0
- package/lib/walk.js +7 -0
- package/package.json +1 -1
- package/src/bundle.ts +25 -3
- package/src/config/__tests__/load.test.ts +35 -0
- package/src/config/all.ts +2 -0
- package/src/config/config.ts +11 -0
- package/src/config/load.ts +20 -9
- package/src/index.ts +2 -8
- package/src/lint.ts +2 -0
- package/src/redocly/__tests__/redocly-client.test.ts +120 -0
- package/src/redocly/index.ts +101 -227
- package/src/redocly/registry-api-types.ts +31 -0
- package/src/redocly/registry-api.ts +110 -0
- package/src/rules/common/__tests__/paths-kebab-case.test.ts +23 -0
- package/src/rules/common/no-invalid-parameter-examples.ts +36 -0
- package/src/rules/common/no-invalid-schema-examples.ts +27 -0
- package/src/rules/common/paths-kebab-case.ts +1 -1
- package/src/rules/common/registry-dependencies.ts +6 -8
- package/src/rules/oas2/index.ts +4 -0
- package/src/rules/oas3/index.ts +4 -0
- package/src/rules/oas3/no-invalid-media-type-examples.ts +16 -36
- package/src/rules/utils.ts +43 -2
- package/src/typings/openapi.ts +4 -0
- package/src/utils.ts +5 -1
- package/src/walk.ts +10 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/redocly/query.d.ts +0 -4
- package/lib/redocly/query.js +0 -44
- package/src/redocly/query.ts +0 -38
package/src/config/load.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
|
-
|
|
3
2
|
import { RedoclyClient } from '../redocly';
|
|
4
3
|
import { loadYaml } from '../utils';
|
|
5
|
-
import { Config, RawConfig } from './config';
|
|
4
|
+
import { Config, DOMAINS, RawConfig, Region } from './config';
|
|
6
5
|
|
|
7
6
|
import { defaultPlugin } from './builtIn';
|
|
8
7
|
|
|
@@ -25,19 +24,31 @@ export async function loadConfig(configPath?: string, customExtends?: string[]):
|
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
const redoclyClient = new RedoclyClient();
|
|
28
|
-
|
|
27
|
+
const tokens = await redoclyClient.getTokens();
|
|
28
|
+
|
|
29
|
+
if (tokens.length) {
|
|
29
30
|
if (!rawConfig.resolve) rawConfig.resolve = {};
|
|
30
31
|
if (!rawConfig.resolve.http) rawConfig.resolve.http = {};
|
|
31
|
-
rawConfig.resolve.http.headers = [
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
rawConfig.resolve.http.headers = [...(rawConfig.resolve.http.headers ?? [])];
|
|
33
|
+
|
|
34
|
+
for (const item of tokens) {
|
|
35
|
+
const domain = DOMAINS[item.region as Region];
|
|
36
|
+
rawConfig.resolve.http.headers.push({
|
|
37
|
+
matches: `https://api.${domain}/registry/**`,
|
|
34
38
|
name: 'Authorization',
|
|
35
39
|
envVariable: undefined,
|
|
36
|
-
value:
|
|
40
|
+
value: item.token,
|
|
37
41
|
},
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
//support redocly.com domain for future compatibility
|
|
43
|
+
...(item.region === 'us' ? [{
|
|
44
|
+
matches: `https://api.redocly.com/registry/**`,
|
|
45
|
+
name: 'Authorization',
|
|
46
|
+
envVariable: undefined,
|
|
47
|
+
value: item.token,
|
|
48
|
+
}] : []));
|
|
49
|
+
}
|
|
40
50
|
}
|
|
51
|
+
|
|
41
52
|
return new Config(
|
|
42
53
|
{
|
|
43
54
|
...rawConfig,
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ export { StatsAccumulator, StatsName } from './typings/common';
|
|
|
17
17
|
export { normalizeTypes } from './types';
|
|
18
18
|
export { Stats } from './rules/other/stats';
|
|
19
19
|
|
|
20
|
-
export { Config, LintConfig, RawConfig, IGNORE_FILE } from './config/config';
|
|
20
|
+
export { Config, LintConfig, RawConfig, IGNORE_FILE, Region } from './config/config';
|
|
21
21
|
export { loadConfig } from './config/load';
|
|
22
22
|
export { RedoclyClient } from './redocly';
|
|
23
23
|
export {
|
|
@@ -46,11 +46,5 @@ export {
|
|
|
46
46
|
|
|
47
47
|
export { getAstNodeByPointer, getLineColLocation } from './format/codeframes';
|
|
48
48
|
export { formatProblems, OutputFormat, getTotals, Totals } from './format/format';
|
|
49
|
-
export {
|
|
50
|
-
lint,
|
|
51
|
-
lint as validate,
|
|
52
|
-
lintDocument,
|
|
53
|
-
lintFromString,
|
|
54
|
-
lintConfig,
|
|
55
|
-
} from './lint';
|
|
49
|
+
export { lint, lint as validate, lintDocument, lintFromString, lintConfig } from './lint';
|
|
56
50
|
export { bundle, bundleDocument } from './bundle';
|
package/src/lint.ts
CHANGED
|
@@ -72,6 +72,7 @@ export async function lintDocument(opts: {
|
|
|
72
72
|
const ctx: WalkContext = {
|
|
73
73
|
problems: [],
|
|
74
74
|
oasVersion: oasVersion,
|
|
75
|
+
visitorsData: {},
|
|
75
76
|
};
|
|
76
77
|
|
|
77
78
|
const preprocessors = initRules(rules as any, config, 'preprocessors', oasVersion);
|
|
@@ -101,6 +102,7 @@ export async function lintConfig(opts: {
|
|
|
101
102
|
const ctx: WalkContext = {
|
|
102
103
|
problems: [],
|
|
103
104
|
oasVersion: OasVersion.Version3_0,
|
|
105
|
+
visitorsData: {},
|
|
104
106
|
};
|
|
105
107
|
const config = new LintConfig({
|
|
106
108
|
plugins: [defaultPlugin],
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { RedoclyClient } from '../index';
|
|
2
|
+
|
|
3
|
+
describe('RedoclyClient', () => {
|
|
4
|
+
const REDOCLY_DOMAIN_US = 'redoc.ly';
|
|
5
|
+
const REDOCLY_DOMAIN_EU = 'eu.redocly.com';
|
|
6
|
+
const REDOCLY_AUTHORIZATION_TOKEN = 'redocly-auth-token';
|
|
7
|
+
const testRedoclyDomain = 'redoclyDomain.com';
|
|
8
|
+
const testToken = 'test-token';
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
delete process.env.REDOCLY_DOMAIN;
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should resolve the US domain by default', () => {
|
|
15
|
+
const client = new RedoclyClient();
|
|
16
|
+
expect(client.domain).toBe(REDOCLY_DOMAIN_US);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should resolve domain from RedoclyDomain env', () => {
|
|
20
|
+
process.env.REDOCLY_DOMAIN = testRedoclyDomain;
|
|
21
|
+
const client = new RedoclyClient();
|
|
22
|
+
expect(client.domain).toBe(testRedoclyDomain);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should resolve a domain by US region', () => {
|
|
26
|
+
const client = new RedoclyClient('us');
|
|
27
|
+
expect(client.domain).toBe(REDOCLY_DOMAIN_US);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should resolve a domain by EU region', () => {
|
|
31
|
+
const client = new RedoclyClient('eu');
|
|
32
|
+
expect(client.domain).toBe(REDOCLY_DOMAIN_EU);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should resolve domain by EU region prioritizing flag over env variable', () => {
|
|
36
|
+
process.env.REDOCLY_DOMAIN = testRedoclyDomain;
|
|
37
|
+
const client = new RedoclyClient('eu');
|
|
38
|
+
expect(client.domain).toBe(REDOCLY_DOMAIN_EU);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should resolve domain by US region prioritizing flag over env variable', () => {
|
|
42
|
+
process.env.REDOCLY_DOMAIN = testRedoclyDomain;
|
|
43
|
+
const client = new RedoclyClient('us');
|
|
44
|
+
expect(client.domain).toBe(REDOCLY_DOMAIN_US);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should resolve domain by US region when REDOCLY_DOMAIN consists EU domain', () => {
|
|
48
|
+
process.env.REDOCLY_DOMAIN = REDOCLY_DOMAIN_EU;
|
|
49
|
+
const client = new RedoclyClient();
|
|
50
|
+
expect(client.getRegion()).toBe('eu');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should resolve valid tokens data', async () => {
|
|
54
|
+
let spy = jest.spyOn(RedoclyClient.prototype, 'readCredentialsFile').mockImplementation(() => {
|
|
55
|
+
return { us: "accessToken", eu: "eu-accessToken" }
|
|
56
|
+
});
|
|
57
|
+
const client = new RedoclyClient();
|
|
58
|
+
const tokens = await client.getValidTokens();
|
|
59
|
+
expect(tokens).toStrictEqual([
|
|
60
|
+
{ region: 'us', token: 'accessToken', valid: true },
|
|
61
|
+
{ region: 'eu', token: 'eu-accessToken', valid: true }
|
|
62
|
+
]);
|
|
63
|
+
spy.mockRestore();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should not call setAccessTokens by default', () => {
|
|
67
|
+
let spy = jest.spyOn(RedoclyClient.prototype, 'readCredentialsFile').mockImplementation(() => ({}));
|
|
68
|
+
jest.spyOn(RedoclyClient.prototype, 'setAccessTokens').mockImplementation();
|
|
69
|
+
const client = new RedoclyClient();
|
|
70
|
+
expect(client.setAccessTokens).not.toHaveBeenCalled()
|
|
71
|
+
spy.mockRestore();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should set correct accessTokens - backward compatibility: default US region', () => {
|
|
75
|
+
let spy = jest.spyOn(RedoclyClient.prototype, 'readCredentialsFile').mockImplementation(() => ({ token: testToken }));
|
|
76
|
+
jest.spyOn(RedoclyClient.prototype, 'setAccessTokens').mockImplementation();
|
|
77
|
+
const client = new RedoclyClient();
|
|
78
|
+
expect(client.setAccessTokens).toBeCalledWith(
|
|
79
|
+
expect.objectContaining({ us: testToken })
|
|
80
|
+
);
|
|
81
|
+
spy.mockRestore();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should set correct accessTokens - backward compatibility: EU region', () => {
|
|
85
|
+
let spy = jest.spyOn(RedoclyClient.prototype, 'readCredentialsFile').mockImplementation(() => ({ token: testToken }));
|
|
86
|
+
jest.spyOn(RedoclyClient.prototype, 'setAccessTokens').mockImplementation();
|
|
87
|
+
const client = new RedoclyClient('eu');
|
|
88
|
+
expect(client.setAccessTokens).toBeCalledWith(
|
|
89
|
+
expect.objectContaining({ eu: testToken })
|
|
90
|
+
);
|
|
91
|
+
spy.mockRestore();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should set correct accessTokens - REDOCLY_AUTHORIZATION env', () => {
|
|
95
|
+
process.env.REDOCLY_AUTHORIZATION = REDOCLY_AUTHORIZATION_TOKEN;
|
|
96
|
+
let spy = jest.spyOn(RedoclyClient.prototype, 'readCredentialsFile').mockImplementation();
|
|
97
|
+
jest.spyOn(RedoclyClient.prototype, 'setAccessTokens').mockImplementation();
|
|
98
|
+
const client = new RedoclyClient();
|
|
99
|
+
expect(client.setAccessTokens).toHaveBeenNthCalledWith(1, { "us": REDOCLY_AUTHORIZATION_TOKEN });
|
|
100
|
+
spy.mockRestore();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should set correct accessTokens prioritizing REDOCLY_AUTHORIZATION env over token in file', () => {
|
|
104
|
+
process.env.REDOCLY_AUTHORIZATION = REDOCLY_AUTHORIZATION_TOKEN;
|
|
105
|
+
let spy = jest.spyOn(RedoclyClient.prototype, 'readCredentialsFile').mockImplementation(() => ({ token: testToken }));
|
|
106
|
+
jest.spyOn(RedoclyClient.prototype, 'setAccessTokens').mockImplementation();
|
|
107
|
+
const client = new RedoclyClient();
|
|
108
|
+
expect(client.setAccessTokens).toHaveBeenNthCalledWith(2, { "us": REDOCLY_AUTHORIZATION_TOKEN });
|
|
109
|
+
spy.mockRestore();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should set correct accessTokens prioritizing REDOCLY_AUTHORIZATION env over EU token', () => {
|
|
113
|
+
process.env.REDOCLY_AUTHORIZATION = REDOCLY_AUTHORIZATION_TOKEN;
|
|
114
|
+
let spy = jest.spyOn(RedoclyClient.prototype, 'readCredentialsFile').mockImplementation(() => ({ us: testToken }));
|
|
115
|
+
jest.spyOn(RedoclyClient.prototype, 'setAccessTokens').mockImplementation();
|
|
116
|
+
const client = new RedoclyClient('eu');
|
|
117
|
+
expect(client.setAccessTokens).toHaveBeenNthCalledWith(2, { "eu": REDOCLY_AUTHORIZATION_TOKEN });
|
|
118
|
+
spy.mockRestore();
|
|
119
|
+
});
|
|
120
|
+
});
|
package/src/redocly/index.ts
CHANGED
|
@@ -1,65 +1,115 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
|
|
2
2
|
import { resolve } from 'path';
|
|
3
3
|
import { homedir } from 'os';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { red, green, gray } from 'colorette';
|
|
5
|
+
import { RegistryApi } from './registry-api';
|
|
6
|
+
import { AccessTokens, DEFAULT_REGION, DOMAINS, Region } from '../config/config';
|
|
7
|
+
import { isNotEmptyObject } from '../utils';
|
|
6
8
|
|
|
7
9
|
const TOKEN_FILENAME = '.redocly-config.json';
|
|
8
10
|
|
|
9
11
|
export class RedoclyClient {
|
|
10
|
-
private
|
|
12
|
+
private accessTokens: AccessTokens = {};
|
|
13
|
+
private region: Region;
|
|
14
|
+
domain: string;
|
|
15
|
+
registryApi: RegistryApi;
|
|
16
|
+
|
|
17
|
+
constructor(region?: Region) {
|
|
18
|
+
this.region = this.loadRegion(region);
|
|
19
|
+
this.loadTokens();
|
|
20
|
+
this.domain = region
|
|
21
|
+
? DOMAINS[region]
|
|
22
|
+
: process.env.REDOCLY_DOMAIN || DOMAINS[DEFAULT_REGION];
|
|
23
|
+
this.registryApi = new RegistryApi(this.accessTokens, this.region);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
loadRegion(region?: Region) {
|
|
27
|
+
if (region && !DOMAINS[region]) {
|
|
28
|
+
process.stdout.write(
|
|
29
|
+
red(`Invalid argument: region in config file.\nGiven: ${green(region)}, choices: "us", "eu".\n`),
|
|
30
|
+
);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
11
33
|
|
|
12
|
-
|
|
13
|
-
|
|
34
|
+
if (process.env.REDOCLY_DOMAIN) {
|
|
35
|
+
return (Object.keys(DOMAINS).find(
|
|
36
|
+
(region) => DOMAINS[region as Region] === process.env.REDOCLY_DOMAIN,
|
|
37
|
+
) || DEFAULT_REGION) as Region;
|
|
38
|
+
}
|
|
39
|
+
return region || DEFAULT_REGION;
|
|
14
40
|
}
|
|
15
41
|
|
|
16
|
-
|
|
17
|
-
return
|
|
42
|
+
getRegion(): Region {
|
|
43
|
+
return this.region;
|
|
18
44
|
}
|
|
19
45
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
46
|
+
hasTokens(): boolean {
|
|
47
|
+
return isNotEmptyObject(this.accessTokens);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setAccessTokens(accessTokens: AccessTokens) {
|
|
51
|
+
this.accessTokens = accessTokens;
|
|
52
|
+
}
|
|
25
53
|
|
|
54
|
+
loadTokens(): void {
|
|
26
55
|
const credentialsPath = resolve(homedir(), TOKEN_FILENAME);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.
|
|
56
|
+
const credentials = this.readCredentialsFile(credentialsPath);
|
|
57
|
+
if (isNotEmptyObject(credentials)) {
|
|
58
|
+
this.setAccessTokens({
|
|
59
|
+
...credentials,
|
|
60
|
+
...(credentials.token && !credentials[this.region] && {
|
|
61
|
+
[this.region]: credentials.token
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
if (process.env.REDOCLY_AUTHORIZATION) {
|
|
66
|
+
this.setAccessTokens({
|
|
67
|
+
...this.accessTokens,
|
|
68
|
+
[this.region]: process.env.REDOCLY_AUTHORIZATION
|
|
69
|
+
})
|
|
30
70
|
}
|
|
31
71
|
}
|
|
32
72
|
|
|
73
|
+
async getValidTokens(): Promise<{
|
|
74
|
+
region: string;
|
|
75
|
+
token: string;
|
|
76
|
+
valid: boolean;
|
|
77
|
+
}[]> {
|
|
78
|
+
return (await Promise.all(
|
|
79
|
+
Object.entries(this.accessTokens).map(async ([key, value]) => {
|
|
80
|
+
return { region: key, token: value, valid: await this.verifyToken(value, key as Region) }
|
|
81
|
+
})
|
|
82
|
+
)).filter(item => Boolean(item.valid));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async getTokens() {
|
|
86
|
+
return this.hasTokens() ? await this.getValidTokens() : [];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async isAuthorizedWithRedoclyByRegion(): Promise<boolean> {
|
|
90
|
+
if (!this.hasTokens()) return false;
|
|
91
|
+
const accessToken = this.accessTokens[this.region];
|
|
92
|
+
return !!accessToken && await this.verifyToken(accessToken, this.region);
|
|
93
|
+
}
|
|
94
|
+
|
|
33
95
|
async isAuthorizedWithRedocly(): Promise<boolean> {
|
|
34
|
-
return this.
|
|
96
|
+
return this.hasTokens() && isNotEmptyObject(await this.getValidTokens());
|
|
35
97
|
}
|
|
36
98
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const authDetails = await RedoclyClient.authorize(accessToken, { verbose });
|
|
40
|
-
if (!authDetails) return false;
|
|
41
|
-
return true;
|
|
99
|
+
readCredentialsFile(credentialsPath: string) {
|
|
100
|
+
return existsSync(credentialsPath) ? JSON.parse(readFileSync(credentialsPath, 'utf-8')) : {};
|
|
42
101
|
}
|
|
43
102
|
|
|
44
|
-
async
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
process.stderr.write(
|
|
48
|
-
`${yellow(
|
|
49
|
-
'Warning:',
|
|
50
|
-
)} invalid Redocly API key. Use "npx @redocly/openapi-cli login" to provide your API key\n`,
|
|
51
|
-
);
|
|
52
|
-
return undefined;
|
|
53
|
-
}
|
|
54
|
-
return this.accessToken;
|
|
103
|
+
async verifyToken(accessToken: string, region: Region, verbose: boolean = false): Promise<boolean> {
|
|
104
|
+
if (!accessToken) return false;
|
|
105
|
+
return this.registryApi.authStatus(accessToken, region, verbose);
|
|
55
106
|
}
|
|
56
107
|
|
|
57
108
|
async login(accessToken: string, verbose: boolean = false) {
|
|
58
109
|
const credentialsPath = resolve(homedir(), TOKEN_FILENAME);
|
|
59
110
|
process.stdout.write(gray('\n Logging in...\n'));
|
|
60
111
|
|
|
61
|
-
const authorized = await this.verifyToken(accessToken, verbose);
|
|
62
|
-
|
|
112
|
+
const authorized = await this.verifyToken(accessToken, this.region, verbose);
|
|
63
113
|
if (!authorized) {
|
|
64
114
|
process.stdout.write(
|
|
65
115
|
red('Authorization failed. Please check if you entered a valid API key.\n'),
|
|
@@ -67,11 +117,13 @@ export class RedoclyClient {
|
|
|
67
117
|
process.exit(1);
|
|
68
118
|
}
|
|
69
119
|
|
|
70
|
-
this.accessToken = accessToken;
|
|
71
120
|
const credentials = {
|
|
72
|
-
|
|
121
|
+
...this.readCredentialsFile(credentialsPath),
|
|
122
|
+
[this.region!]: accessToken,
|
|
123
|
+
token: accessToken, // FIXME: backward compatibility, remove on 1.0.0
|
|
73
124
|
};
|
|
74
|
-
|
|
125
|
+
this.accessTokens = credentials;
|
|
126
|
+
this.registryApi.setAccessTokens(credentials);
|
|
75
127
|
writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2));
|
|
76
128
|
process.stdout.write(green(' Authorization confirmed. ✅\n\n'));
|
|
77
129
|
}
|
|
@@ -83,198 +135,20 @@ export class RedoclyClient {
|
|
|
83
135
|
}
|
|
84
136
|
process.stdout.write('Logged out from the Redocly account. ✋\n');
|
|
85
137
|
}
|
|
138
|
+
}
|
|
86
139
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
static async authorize(accessToken: string, options: { queryName?: string; verbose?: boolean }) {
|
|
95
|
-
const { queryName = '', verbose = false } = options;
|
|
96
|
-
try {
|
|
97
|
-
const queryStr = `query ${queryName}{ viewer { id } }`;
|
|
98
|
-
|
|
99
|
-
return await query(queryStr, {}, { Authorization: accessToken });
|
|
100
|
-
} catch (e) {
|
|
101
|
-
if (verbose) console.log(e);
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async updateDependencies(dependencies: string[] | undefined): Promise<void> {
|
|
107
|
-
const definitionId = process.env.DEFINITION;
|
|
108
|
-
const versionId = process.env.DEFINITION;
|
|
109
|
-
const branchId = process.env.BRANCH;
|
|
110
|
-
|
|
111
|
-
if (!definitionId || !versionId || !branchId) return;
|
|
112
|
-
|
|
113
|
-
await this.query(
|
|
114
|
-
`
|
|
115
|
-
mutation UpdateBranchDependenciesFromURLs(
|
|
116
|
-
$urls: [String!]!
|
|
117
|
-
$definitionId: Int!
|
|
118
|
-
$versionId: Int!
|
|
119
|
-
$branchId: Int!
|
|
120
|
-
) {
|
|
121
|
-
updateBranchDependenciesFromURLs(
|
|
122
|
-
definitionId: $definitionId
|
|
123
|
-
versionId: $versionId
|
|
124
|
-
branchId: $branchId
|
|
125
|
-
urls: $urls
|
|
126
|
-
) {
|
|
127
|
-
branchName
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
`,
|
|
131
|
-
{
|
|
132
|
-
urls: dependencies || [],
|
|
133
|
-
definitionId: parseInt(definitionId, 10),
|
|
134
|
-
versionId: parseInt(versionId, 10),
|
|
135
|
-
branchId: parseInt(branchId, 10),
|
|
136
|
-
},
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
updateDefinitionVersion(definitionId: number, versionId: number, updatePatch: object): Promise<void> {
|
|
141
|
-
return this.query(`
|
|
142
|
-
mutation UpdateDefinitionVersion($definitionId: Int!, $versionId: Int!, $updatePatch: DefinitionVersionPatch!) {
|
|
143
|
-
updateDefinitionVersionByDefinitionIdAndId(input: {definitionId: $definitionId, id: $versionId, patch: $updatePatch}) {
|
|
144
|
-
definitionVersion {
|
|
145
|
-
...VersionDetails
|
|
146
|
-
__typename
|
|
147
|
-
}
|
|
148
|
-
__typename
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
fragment VersionDetails on DefinitionVersion {
|
|
153
|
-
id
|
|
154
|
-
nodeId
|
|
155
|
-
uuid
|
|
156
|
-
definitionId
|
|
157
|
-
name
|
|
158
|
-
description
|
|
159
|
-
sourceType
|
|
160
|
-
source
|
|
161
|
-
registryAccess
|
|
162
|
-
__typename
|
|
163
|
-
}
|
|
164
|
-
`,
|
|
165
|
-
{
|
|
166
|
-
definitionId,
|
|
167
|
-
versionId,
|
|
168
|
-
updatePatch,
|
|
169
|
-
},
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
getOrganizationId(organizationId: string) {
|
|
174
|
-
return this.query(`
|
|
175
|
-
query ($organizationId: String!) {
|
|
176
|
-
organizationById(id: $organizationId) {
|
|
177
|
-
id
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
`, {
|
|
181
|
-
organizationId
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
getDefinitionByName(name: string, organizationId: string) {
|
|
186
|
-
return this.query(`
|
|
187
|
-
query ($name: String!, $organizationId: String!) {
|
|
188
|
-
definition: definitionByOrganizationIdAndName(name: $name, organizationId: $organizationId) {
|
|
189
|
-
id
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
`, {
|
|
193
|
-
name,
|
|
194
|
-
organizationId
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
createDefinition(organizationId: string, name: string) {
|
|
199
|
-
return this.query(`
|
|
200
|
-
mutation CreateDefinition($organizationId: String!, $name: String!) {
|
|
201
|
-
def: createDefinition(input: {organizationId: $organizationId, name: $name }) {
|
|
202
|
-
definition {
|
|
203
|
-
id
|
|
204
|
-
nodeId
|
|
205
|
-
name
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
`, {
|
|
210
|
-
organizationId,
|
|
211
|
-
name
|
|
212
|
-
})
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
createDefinitionVersion(definitionId: string, name: string, sourceType: string, source: any) {
|
|
216
|
-
return this.query(`
|
|
217
|
-
mutation CreateVersion($definitionId: Int!, $name: String!, $sourceType: DvSourceType!, $source: JSON) {
|
|
218
|
-
createDefinitionVersion(input: {definitionId: $definitionId, name: $name, sourceType: $sourceType, source: $source }) {
|
|
219
|
-
definitionVersion {
|
|
220
|
-
id
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
`, {
|
|
225
|
-
definitionId,
|
|
226
|
-
name,
|
|
227
|
-
sourceType,
|
|
228
|
-
source
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
getSignedUrl(organizationId: string, filesHash: string, fileName: string) {
|
|
233
|
-
return this.query(`
|
|
234
|
-
query ($organizationId: String!, $filesHash: String!, $fileName: String!) {
|
|
235
|
-
signFileUploadCLI(organizationId: $organizationId, filesHash: $filesHash, fileName: $fileName) {
|
|
236
|
-
signedFileUrl
|
|
237
|
-
uploadedFilePath
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
`, {
|
|
241
|
-
organizationId,
|
|
242
|
-
filesHash,
|
|
243
|
-
fileName
|
|
244
|
-
})
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
getDefinitionVersion(organizationId: string, definitionName: string, versionName: string) {
|
|
248
|
-
return this.query(`
|
|
249
|
-
query ($organizationId: String!, $definitionName: String!, $versionName: String!) {
|
|
250
|
-
version: definitionVersionByOrganizationDefinitionAndName(organizationId: $organizationId, definitionName: $definitionName, versionName: $versionName) {
|
|
251
|
-
id
|
|
252
|
-
definitionId
|
|
253
|
-
defaultBranch {
|
|
254
|
-
name
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
`, {
|
|
259
|
-
organizationId,
|
|
260
|
-
definitionName,
|
|
261
|
-
versionName
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
static isRegistryURL(link: string): boolean {
|
|
266
|
-
const domain = process.env.REDOCLY_DOMAIN || 'redoc.ly';
|
|
267
|
-
if (!link.startsWith(`https://api.${domain}/registry/`)) return false;
|
|
268
|
-
const registryPath = link.replace(`https://api.${domain}/registry/`, '');
|
|
140
|
+
export function isRedoclyRegistryURL(link: string): boolean {
|
|
141
|
+
const domain = process.env.REDOCLY_DOMAIN || DOMAINS[DEFAULT_REGION];
|
|
142
|
+
if (!link.startsWith(`https://api.${domain}/registry/`)) return false;
|
|
143
|
+
const registryPath = link.replace(`https://api.${domain}/registry/`, '');
|
|
269
144
|
|
|
270
|
-
|
|
145
|
+
const pathParts = registryPath.split('/');
|
|
271
146
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
147
|
+
// we can be sure, that there is job UUID present
|
|
148
|
+
// (org, definition, version, bundle, branch, job, "openapi.yaml" 🤦♂️)
|
|
149
|
+
// so skip this link.
|
|
150
|
+
// FIXME
|
|
151
|
+
if (pathParts.length === 7) return false;
|
|
277
152
|
|
|
278
|
-
|
|
279
|
-
}
|
|
153
|
+
return true;
|
|
280
154
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export namespace RegistryApiTypes {
|
|
2
|
+
interface VersionParams {
|
|
3
|
+
organizationId: string;
|
|
4
|
+
name: string;
|
|
5
|
+
version: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface PrepareFileuploadParams extends VersionParams {
|
|
9
|
+
filesHash: string;
|
|
10
|
+
filename: string;
|
|
11
|
+
isUpsert?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface PushApiParams extends VersionParams {
|
|
15
|
+
rootFilePath: string;
|
|
16
|
+
filePaths: string[];
|
|
17
|
+
branch?: string;
|
|
18
|
+
isUpsert?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface PrepareFileuploadOKResponse {
|
|
22
|
+
filePath: string;
|
|
23
|
+
signedUploadUrl: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface NotFoundProblemResponse {
|
|
27
|
+
status: 404;
|
|
28
|
+
title: 'Not Found';
|
|
29
|
+
code: 'ORGANIZATION_NOT_FOUND' | 'API_VERSION_NOT_FOUND';
|
|
30
|
+
}
|
|
31
|
+
}
|