neonctl 1.18.2 → 1.19.1
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/analytics.js +50 -20
- package/commands/auth.js +15 -7
- package/commands/auth.test.js +11 -6
- package/commands/branches.test.js +33 -0
- package/commands/connection_string.test.js +136 -0
- package/commands/projects.js +5 -2
- package/config.js +1 -0
- package/errors.js +13 -0
- package/index.js +7 -2
- package/package.json +1 -1
- package/test_utils/mock_server.js +1 -1
- package/test_utils/oauth_server.js +1 -1
- package/utils/string.js +4 -0
- package/writer.js +3 -2
package/analytics.js
CHANGED
|
@@ -1,33 +1,63 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
1
3
|
import { Analytics } from '@segment/analytics-node';
|
|
4
|
+
import { isAxiosError } from 'axios';
|
|
5
|
+
import { CREDENTIALS_FILE } from './config.js';
|
|
2
6
|
import { isCi } from './env.js';
|
|
7
|
+
import { log } from './log.js';
|
|
3
8
|
import pkg from './pkg.js';
|
|
4
9
|
const WRITE_KEY = '3SQXn5ejjXWLEJ8xU2PRYhAotLtTaeeV';
|
|
10
|
+
let client;
|
|
11
|
+
let userId = '';
|
|
5
12
|
export const analyticsMiddleware = (args) => {
|
|
6
13
|
if (!args.analytics) {
|
|
7
14
|
return;
|
|
8
15
|
}
|
|
9
|
-
|
|
16
|
+
try {
|
|
17
|
+
const credentialsPath = join(args.configDir, CREDENTIALS_FILE);
|
|
18
|
+
const credentials = readFileSync(credentialsPath, { encoding: 'utf-8' });
|
|
19
|
+
userId = JSON.parse(credentials).user_id;
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
log.debug('Failed to read credentials file', err);
|
|
23
|
+
}
|
|
24
|
+
client = new Analytics({
|
|
10
25
|
writeKey: WRITE_KEY,
|
|
11
26
|
host: 'https://track.neon.tech',
|
|
12
27
|
});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
output: args.output,
|
|
25
|
-
},
|
|
26
|
-
ci: isCi(),
|
|
28
|
+
client.identify({
|
|
29
|
+
userId: userId?.toString() ?? 'anonymous',
|
|
30
|
+
});
|
|
31
|
+
client.track({
|
|
32
|
+
userId: userId ?? 'anonymous',
|
|
33
|
+
event: 'CLI Started',
|
|
34
|
+
properties: {
|
|
35
|
+
version: pkg.version,
|
|
36
|
+
command: args._.join(' '),
|
|
37
|
+
flags: {
|
|
38
|
+
output: args.output,
|
|
27
39
|
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
ci: isCi(),
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
client.closeAndFlush();
|
|
44
|
+
log.debug('Sent CLI started event with userId: %s', userId);
|
|
45
|
+
};
|
|
46
|
+
export const sendError = (err, errCode) => {
|
|
47
|
+
if (!client) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const axiosError = isAxiosError(err) ? err : undefined;
|
|
51
|
+
client.track({
|
|
52
|
+
event: 'CLI Error',
|
|
53
|
+
userId: userId ?? 'anonymous',
|
|
54
|
+
properties: {
|
|
55
|
+
message: err.message,
|
|
56
|
+
stack: err.stack,
|
|
57
|
+
errCode,
|
|
58
|
+
statusCode: axiosError?.response?.status,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
client.closeAndFlush();
|
|
62
|
+
log.debug('Sent CLI error event: %s', errCode);
|
|
33
63
|
};
|
package/commands/auth.js
CHANGED
|
@@ -5,7 +5,7 @@ import { auth, refreshToken } from '../auth.js';
|
|
|
5
5
|
import { log } from '../log.js';
|
|
6
6
|
import { getApiClient } from '../api.js';
|
|
7
7
|
import { isCi } from '../env.js';
|
|
8
|
-
|
|
8
|
+
import { CREDENTIALS_FILE } from '../config.js';
|
|
9
9
|
export const command = 'auth';
|
|
10
10
|
export const aliases = ['login'];
|
|
11
11
|
export const describe = 'Authenticate';
|
|
@@ -13,7 +13,7 @@ export const builder = (yargs) => yargs;
|
|
|
13
13
|
export const handler = async (args) => {
|
|
14
14
|
await authFlow(args);
|
|
15
15
|
};
|
|
16
|
-
export const authFlow = async ({ configDir, oauthHost, clientId, forceAuth, }) => {
|
|
16
|
+
export const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceAuth, }) => {
|
|
17
17
|
if (!forceAuth && isCi()) {
|
|
18
18
|
throw new Error('Cannot run interactive auth in CI');
|
|
19
19
|
}
|
|
@@ -22,17 +22,25 @@ export const authFlow = async ({ configDir, oauthHost, clientId, forceAuth, }) =
|
|
|
22
22
|
clientId: clientId,
|
|
23
23
|
});
|
|
24
24
|
const credentialsPath = join(configDir, CREDENTIALS_FILE);
|
|
25
|
-
|
|
25
|
+
await preserveCredentials(credentialsPath, tokenSet, getApiClient({
|
|
26
|
+
apiKey: tokenSet.access_token || '',
|
|
27
|
+
apiHost,
|
|
28
|
+
}));
|
|
26
29
|
log.info(`Saved credentials to ${credentialsPath}`);
|
|
27
30
|
log.info('Auth complete');
|
|
28
31
|
return tokenSet.access_token || '';
|
|
29
32
|
};
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
const preserveCredentials = async (path, credentials, apiClient) => {
|
|
34
|
+
const { data: { id }, } = await apiClient.getCurrentUserInfo();
|
|
35
|
+
const contents = JSON.stringify({
|
|
36
|
+
...credentials,
|
|
37
|
+
user_id: id,
|
|
38
|
+
});
|
|
39
|
+
// correctly sets needed permissions for the credentials file
|
|
32
40
|
writeFileSync(path, contents, {
|
|
33
41
|
mode: 0o700,
|
|
34
42
|
});
|
|
35
|
-
}
|
|
43
|
+
};
|
|
36
44
|
export const ensureAuth = async (props) => {
|
|
37
45
|
if (props._.length === 0) {
|
|
38
46
|
return;
|
|
@@ -63,7 +71,7 @@ export const ensureAuth = async (props) => {
|
|
|
63
71
|
apiKey: props.apiKey,
|
|
64
72
|
apiHost: props.apiHost,
|
|
65
73
|
});
|
|
66
|
-
|
|
74
|
+
await preserveCredentials(credentialsPath, refreshedTokenSet, props.apiClient);
|
|
67
75
|
return;
|
|
68
76
|
}
|
|
69
77
|
const token = tokenSet.access_token || 'UNKNOWN';
|
package/commands/auth.test.js
CHANGED
|
@@ -2,6 +2,7 @@ import axios from 'axios';
|
|
|
2
2
|
import { beforeAll, describe, test, jest, afterAll, expect, } from '@jest/globals';
|
|
3
3
|
import { mkdtempSync, rmSync, readFileSync } from 'node:fs';
|
|
4
4
|
import { startOauthServer } from '../test_utils/oauth_server';
|
|
5
|
+
import { runMockServer } from '../test_utils/mock_server';
|
|
5
6
|
jest.unstable_mockModule('open', () => ({
|
|
6
7
|
__esModule: true,
|
|
7
8
|
default: jest.fn((url) => {
|
|
@@ -12,26 +13,30 @@ jest.unstable_mockModule('open', () => ({
|
|
|
12
13
|
const authModule = await import('./auth');
|
|
13
14
|
describe('auth', () => {
|
|
14
15
|
let configDir = '';
|
|
15
|
-
let
|
|
16
|
+
let oauthServer;
|
|
17
|
+
let mockServer;
|
|
16
18
|
beforeAll(async () => {
|
|
17
19
|
configDir = mkdtempSync('test-config');
|
|
18
|
-
|
|
20
|
+
oauthServer = await startOauthServer();
|
|
21
|
+
mockServer = await runMockServer('main');
|
|
19
22
|
});
|
|
20
|
-
afterAll(() => {
|
|
23
|
+
afterAll(async () => {
|
|
21
24
|
rmSync(configDir, { recursive: true });
|
|
22
|
-
|
|
25
|
+
await oauthServer.stop();
|
|
26
|
+
await new Promise((resolve) => mockServer.close(resolve));
|
|
23
27
|
});
|
|
24
28
|
test('should auth', async () => {
|
|
25
29
|
await authModule.authFlow({
|
|
26
30
|
_: ['auth'],
|
|
27
|
-
apiHost:
|
|
31
|
+
apiHost: `http://localhost:${mockServer.address().port}`,
|
|
28
32
|
clientId: 'test-client-id',
|
|
29
33
|
configDir,
|
|
30
34
|
forceAuth: true,
|
|
31
|
-
oauthHost:
|
|
35
|
+
oauthHost: `http://localhost:${oauthServer.address().port}`,
|
|
32
36
|
});
|
|
33
37
|
const credentials = JSON.parse(readFileSync(`${configDir}/credentials.json`, 'utf-8'));
|
|
34
38
|
expect(credentials.access_token).toEqual(expect.any(String));
|
|
35
39
|
expect(credentials.refresh_token).toEqual(expect.any(String));
|
|
40
|
+
expect(credentials.user_id).toEqual(expect.any(String));
|
|
36
41
|
});
|
|
37
42
|
});
|
|
@@ -114,6 +114,19 @@ describe('branches', () => {
|
|
|
114
114
|
snapshot: true,
|
|
115
115
|
},
|
|
116
116
|
});
|
|
117
|
+
testCliCommand({
|
|
118
|
+
name: 'delete by id',
|
|
119
|
+
args: [
|
|
120
|
+
'branches',
|
|
121
|
+
'delete',
|
|
122
|
+
'br-cloudy-branch-12345678',
|
|
123
|
+
'--project-id',
|
|
124
|
+
'test',
|
|
125
|
+
],
|
|
126
|
+
expected: {
|
|
127
|
+
snapshot: true,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
117
130
|
testCliCommand({
|
|
118
131
|
name: 'rename',
|
|
119
132
|
args: [
|
|
@@ -141,6 +154,19 @@ describe('branches', () => {
|
|
|
141
154
|
snapshot: true,
|
|
142
155
|
},
|
|
143
156
|
});
|
|
157
|
+
testCliCommand({
|
|
158
|
+
name: 'set primary by id',
|
|
159
|
+
args: [
|
|
160
|
+
'branches',
|
|
161
|
+
'set-primary',
|
|
162
|
+
'br-cloudy-branch-12345678',
|
|
163
|
+
'--project-id',
|
|
164
|
+
'test',
|
|
165
|
+
],
|
|
166
|
+
expected: {
|
|
167
|
+
snapshot: true,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
144
170
|
testCliCommand({
|
|
145
171
|
name: 'get by id',
|
|
146
172
|
args: ['branches', 'get', 'br-sunny-branch-123456', '--project-id', 'test'],
|
|
@@ -148,6 +174,13 @@ describe('branches', () => {
|
|
|
148
174
|
snapshot: true,
|
|
149
175
|
},
|
|
150
176
|
});
|
|
177
|
+
testCliCommand({
|
|
178
|
+
name: 'get by id',
|
|
179
|
+
args: ['branches', 'get', 'br-cloudy-branch-12345678', '--project-id', 'test'],
|
|
180
|
+
expected: {
|
|
181
|
+
snapshot: true,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
151
184
|
testCliCommand({
|
|
152
185
|
name: 'get by name',
|
|
153
186
|
args: ['branches', 'get', 'test_branch', '--project-id', 'test'],
|
|
@@ -17,6 +17,38 @@ describe('connection_string', () => {
|
|
|
17
17
|
snapshot: true,
|
|
18
18
|
},
|
|
19
19
|
});
|
|
20
|
+
testCliCommand({
|
|
21
|
+
name: 'connection_string branch id',
|
|
22
|
+
args: [
|
|
23
|
+
'connection-string',
|
|
24
|
+
'br-sunny-branch-123456',
|
|
25
|
+
'--project-id',
|
|
26
|
+
'test',
|
|
27
|
+
'--database-name',
|
|
28
|
+
'test_db',
|
|
29
|
+
'--role-name',
|
|
30
|
+
'test_role',
|
|
31
|
+
],
|
|
32
|
+
expected: {
|
|
33
|
+
snapshot: true,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
testCliCommand({
|
|
37
|
+
name: 'connection_string branch id',
|
|
38
|
+
args: [
|
|
39
|
+
'connection-string',
|
|
40
|
+
'br-cloudy-branch-12345678',
|
|
41
|
+
'--project-id',
|
|
42
|
+
'test',
|
|
43
|
+
'--database-name',
|
|
44
|
+
'test_db',
|
|
45
|
+
'--role-name',
|
|
46
|
+
'test_role',
|
|
47
|
+
],
|
|
48
|
+
expected: {
|
|
49
|
+
snapshot: true,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
20
52
|
testCliCommand({
|
|
21
53
|
name: 'connection_string pooled',
|
|
22
54
|
args: [
|
|
@@ -34,6 +66,40 @@ describe('connection_string', () => {
|
|
|
34
66
|
snapshot: true,
|
|
35
67
|
},
|
|
36
68
|
});
|
|
69
|
+
testCliCommand({
|
|
70
|
+
name: 'connection_string pooled branch id',
|
|
71
|
+
args: [
|
|
72
|
+
'connection-string',
|
|
73
|
+
'br-sunny-branch-123456',
|
|
74
|
+
'--project-id',
|
|
75
|
+
'test',
|
|
76
|
+
'--database-name',
|
|
77
|
+
'test_db',
|
|
78
|
+
'--role-name',
|
|
79
|
+
'test_role',
|
|
80
|
+
'--pooled',
|
|
81
|
+
],
|
|
82
|
+
expected: {
|
|
83
|
+
snapshot: true,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
testCliCommand({
|
|
87
|
+
name: 'connection_string pooled branch id',
|
|
88
|
+
args: [
|
|
89
|
+
'connection-string',
|
|
90
|
+
'br-cloudy-branch-12345678',
|
|
91
|
+
'--project-id',
|
|
92
|
+
'test',
|
|
93
|
+
'--database-name',
|
|
94
|
+
'test_db',
|
|
95
|
+
'--role-name',
|
|
96
|
+
'test_role',
|
|
97
|
+
'--pooled',
|
|
98
|
+
],
|
|
99
|
+
expected: {
|
|
100
|
+
snapshot: true,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
37
103
|
testCliCommand({
|
|
38
104
|
name: 'connection_string prisma',
|
|
39
105
|
args: [
|
|
@@ -51,6 +117,40 @@ describe('connection_string', () => {
|
|
|
51
117
|
snapshot: true,
|
|
52
118
|
},
|
|
53
119
|
});
|
|
120
|
+
testCliCommand({
|
|
121
|
+
name: 'connection_string pooled branch id',
|
|
122
|
+
args: [
|
|
123
|
+
'connection-string',
|
|
124
|
+
'br-sunny-branch-123456',
|
|
125
|
+
'--project-id',
|
|
126
|
+
'test',
|
|
127
|
+
'--database-name',
|
|
128
|
+
'test_db',
|
|
129
|
+
'--role-name',
|
|
130
|
+
'test_role',
|
|
131
|
+
'--pooled',
|
|
132
|
+
],
|
|
133
|
+
expected: {
|
|
134
|
+
snapshot: true,
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
testCliCommand({
|
|
138
|
+
name: 'connection_string pooled branch id',
|
|
139
|
+
args: [
|
|
140
|
+
'connection-string',
|
|
141
|
+
'br-cloudy-branch-12345678',
|
|
142
|
+
'--project-id',
|
|
143
|
+
'test',
|
|
144
|
+
'--database-name',
|
|
145
|
+
'test_db',
|
|
146
|
+
'--role-name',
|
|
147
|
+
'test_role',
|
|
148
|
+
'--pooled',
|
|
149
|
+
],
|
|
150
|
+
expected: {
|
|
151
|
+
snapshot: true,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
54
154
|
testCliCommand({
|
|
55
155
|
name: 'connection_string prisma pooled',
|
|
56
156
|
args: [
|
|
@@ -69,6 +169,42 @@ describe('connection_string', () => {
|
|
|
69
169
|
snapshot: true,
|
|
70
170
|
},
|
|
71
171
|
});
|
|
172
|
+
testCliCommand({
|
|
173
|
+
name: 'connection_string prisma pooled branch id',
|
|
174
|
+
args: [
|
|
175
|
+
'connection-string',
|
|
176
|
+
'br-sunny-branch-123456',
|
|
177
|
+
'--project-id',
|
|
178
|
+
'test',
|
|
179
|
+
'--database-name',
|
|
180
|
+
'test_db',
|
|
181
|
+
'--role-name',
|
|
182
|
+
'test_role',
|
|
183
|
+
'--prisma',
|
|
184
|
+
'--pooled',
|
|
185
|
+
],
|
|
186
|
+
expected: {
|
|
187
|
+
snapshot: true,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
testCliCommand({
|
|
191
|
+
name: 'connection_string prisma pooled branch id',
|
|
192
|
+
args: [
|
|
193
|
+
'connection-string',
|
|
194
|
+
'br-cloudy-branch-12345678',
|
|
195
|
+
'--project-id',
|
|
196
|
+
'test',
|
|
197
|
+
'--database-name',
|
|
198
|
+
'test_db',
|
|
199
|
+
'--role-name',
|
|
200
|
+
'test_role',
|
|
201
|
+
'--prisma',
|
|
202
|
+
'--pooled',
|
|
203
|
+
],
|
|
204
|
+
expected: {
|
|
205
|
+
snapshot: true,
|
|
206
|
+
},
|
|
207
|
+
});
|
|
72
208
|
testCliCommand({
|
|
73
209
|
name: 'connection_string without any args should pass',
|
|
74
210
|
args: ['connection-string'],
|
package/commands/projects.js
CHANGED
|
@@ -82,8 +82,11 @@ const create = async (props) => {
|
|
|
82
82
|
project,
|
|
83
83
|
});
|
|
84
84
|
const out = writer(props);
|
|
85
|
-
out.write(data.project, { fields: PROJECT_FIELDS });
|
|
86
|
-
out.write(data.connection_uris, {
|
|
85
|
+
out.write(data.project, { fields: PROJECT_FIELDS, title: 'Project' });
|
|
86
|
+
out.write(data.connection_uris, {
|
|
87
|
+
fields: ['connection_uri'],
|
|
88
|
+
title: 'Connection URIs',
|
|
89
|
+
});
|
|
87
90
|
out.end();
|
|
88
91
|
};
|
|
89
92
|
const deleteProject = async (props) => {
|
package/config.js
CHANGED
|
@@ -2,6 +2,7 @@ import { join } from 'node:path';
|
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
4
4
|
import { isCi } from './env.js';
|
|
5
|
+
export const CREDENTIALS_FILE = 'credentials.json';
|
|
5
6
|
export const defaultDir = join(process.env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'neonctl');
|
|
6
7
|
export const ensureConfigDir = async ({ 'config-dir': configDir, 'force-auth': forceAuth, }) => {
|
|
7
8
|
if (!existsSync(configDir) && (!isCi() || forceAuth)) {
|
package/errors.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const ERROR_MATCHERS = [
|
|
2
|
+
[/^Unknown command: (.*)$/, 'UNKNOWN_COMMAND'],
|
|
3
|
+
[/^Missing required argument: (.*)$/, 'MISSING_ARGUMENT'],
|
|
4
|
+
];
|
|
5
|
+
export const matchErrorCode = (message) => {
|
|
6
|
+
for (const [matcher, code] of ERROR_MATCHERS) {
|
|
7
|
+
const match = message.match(matcher);
|
|
8
|
+
if (match) {
|
|
9
|
+
return code;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return 'UNKNOWN_ERROR';
|
|
13
|
+
};
|
package/index.js
CHANGED
|
@@ -20,9 +20,10 @@ import { defaultClientID } from './auth.js';
|
|
|
20
20
|
import { fillInArgs } from './utils/middlewares.js';
|
|
21
21
|
import pkg from './pkg.js';
|
|
22
22
|
import commands from './commands/index.js';
|
|
23
|
-
import { analyticsMiddleware } from './analytics.js';
|
|
23
|
+
import { analyticsMiddleware, sendError } from './analytics.js';
|
|
24
24
|
import { isCi } from './env.js';
|
|
25
25
|
import { isAxiosError } from 'axios';
|
|
26
|
+
import { matchErrorCode } from './errors.js';
|
|
26
27
|
let builder = yargs(hideBin(process.argv));
|
|
27
28
|
builder = builder
|
|
28
29
|
.scriptName(pkg.name)
|
|
@@ -88,7 +89,7 @@ builder = builder
|
|
|
88
89
|
type: 'boolean',
|
|
89
90
|
default: !isCi(),
|
|
90
91
|
})
|
|
91
|
-
.middleware(analyticsMiddleware)
|
|
92
|
+
.middleware(analyticsMiddleware, true)
|
|
92
93
|
.group('version', 'Global options:')
|
|
93
94
|
.alias('version', 'v')
|
|
94
95
|
.group('help', 'Global options:')
|
|
@@ -99,16 +100,20 @@ builder = builder
|
|
|
99
100
|
if (isAxiosError(err)) {
|
|
100
101
|
if (err.code === 'ECONNABORTED') {
|
|
101
102
|
log.error('Request timed out');
|
|
103
|
+
sendError(err, 'REQUEST_TIMEOUT');
|
|
102
104
|
}
|
|
103
105
|
else if (err.response?.status === 401) {
|
|
106
|
+
sendError(err, 'AUTH_FAILED');
|
|
104
107
|
log.error('Authentication failed, please run `neonctl auth`');
|
|
105
108
|
}
|
|
106
109
|
else {
|
|
107
110
|
log.debug('Fail: %d | %s', err.response?.status, err.response?.statusText);
|
|
108
111
|
log.error(err.response?.data?.message);
|
|
112
|
+
sendError(err, 'API_ERROR');
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
else {
|
|
116
|
+
sendError(err || new Error(msg), matchErrorCode(msg || err?.message));
|
|
112
117
|
log.error(msg || err?.message);
|
|
113
118
|
}
|
|
114
119
|
err?.stack && log.debug('Stack: %s', err.stack);
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@ export const runMockServer = async (mockDir) => new Promise((resolve) => {
|
|
|
8
8
|
app.use('/', emocks(join(process.cwd(), 'mocks', mockDir)));
|
|
9
9
|
const server = app.listen(0);
|
|
10
10
|
server.on('listening', () => {
|
|
11
|
+
resolve(server);
|
|
11
12
|
log.info('Mock server listening at %d', server.address().port);
|
|
12
13
|
});
|
|
13
|
-
resolve(server);
|
|
14
14
|
});
|
|
@@ -3,7 +3,7 @@ import { OAuth2Server } from 'oauth2-mock-server';
|
|
|
3
3
|
export const startOauthServer = async () => {
|
|
4
4
|
const server = new OAuth2Server();
|
|
5
5
|
await server.issuer.keys.generate('RS256');
|
|
6
|
-
await server.start(
|
|
6
|
+
await server.start(0, 'localhost');
|
|
7
7
|
log.info('Started OAuth server');
|
|
8
8
|
return server;
|
|
9
9
|
};
|
package/utils/string.js
ADDED
package/writer.js
CHANGED
|
@@ -2,6 +2,7 @@ import YAML from 'yaml';
|
|
|
2
2
|
import Table from 'cli-table';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { isCi } from './env.js';
|
|
5
|
+
import { toSnakeCase } from './utils/string.js';
|
|
5
6
|
/**
|
|
6
7
|
*
|
|
7
8
|
* Parses the output format, takes data and writes the output to stdout.
|
|
@@ -32,7 +33,7 @@ export const writer = (props) => {
|
|
|
32
33
|
out.write(YAML.stringify(chunks.length === 1
|
|
33
34
|
? chunks[0].data
|
|
34
35
|
: Object.fromEntries(chunks.map(({ config, data }, idx) => [
|
|
35
|
-
config.title
|
|
36
|
+
config.title ? toSnakeCase(config.title) : idx,
|
|
36
37
|
data,
|
|
37
38
|
])), null, 2));
|
|
38
39
|
return;
|
|
@@ -41,7 +42,7 @@ export const writer = (props) => {
|
|
|
41
42
|
out.write(JSON.stringify(chunks.length === 1
|
|
42
43
|
? chunks[0].data
|
|
43
44
|
: Object.fromEntries(chunks.map(({ config, data }, idx) => [
|
|
44
|
-
config.title
|
|
45
|
+
config.title ? toSnakeCase(config.title) : idx,
|
|
45
46
|
data,
|
|
46
47
|
])), null, 2));
|
|
47
48
|
return;
|