dbdiagram 0.1.2 → 0.3.0
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 +13 -0
- package/dist/actions/auth/auth-login.action.js +8 -19
- package/dist/actions/auth/auth-logout.action.js +11 -3
- package/dist/actions/auth/auth-status.action.js +7 -13
- package/dist/actions/build/build-document.action.js +29 -45
- package/dist/actions/delete.action.js +8 -15
- package/dist/actions/list/list-document.action.js +13 -19
- package/dist/actions/list/list.action.js +12 -19
- package/dist/actions/pull.action.js +8 -17
- package/dist/actions/push.action.js +22 -24
- package/dist/actions/tokens/token-delete.action.js +8 -14
- package/dist/actions/tokens/token-generate.action.js +7 -12
- package/dist/actions/tokens/token-list.action.js +10 -17
- package/dist/commands/auth/auth-logout.command.js +1 -0
- package/dist/commands/auth/auth.command.js +1 -1
- package/dist/commands/build/build.command.js +1 -1
- package/dist/commands/tokens/tokens.command.js +1 -1
- package/dist/config/credential-manager.js +10 -23
- package/dist/config/settings-manager.js +14 -31
- package/dist/config.js +4 -0
- package/dist/constants/cli-error.constant.js +81 -0
- package/dist/constants/dot-dbdiagram-dir.constant.js +1 -0
- package/dist/errors/cli.error.js +9 -0
- package/dist/errors/portal-api.error.js +1 -0
- package/dist/hooks/global.hook.js +8 -1
- package/dist/index.js +22 -2
- package/dist/integrations/telemetry/telemetry.integration.js +77 -0
- package/dist/program.js +2 -1
- package/dist/services/telemetry.service.js +59 -0
- package/dist/types/telemetry.type.js +1 -0
- package/dist/utils/agent-detector.util.js +27 -0
- package/dist/utils/cli-error.util.js +46 -0
- package/dist/utils/command-name.util.js +12 -0
- package/dist/utils/date.util.js +4 -0
- package/dist/utils/environment-detector.util.js +6 -0
- package/dist/utils/file.util.js +31 -0
- package/dist/utils/output.util.js +35 -4
- package/package.json +4 -2
- package/dist/constants/auth-message.constant.js +0 -1
- package/dist/errors/portal-error-codes.js +0 -23
- package/dist/utils/portal-error.util.js +0 -18
- package/dist/utils/table.util.js +0 -10
package/README.md
CHANGED
|
@@ -181,6 +181,19 @@ yarn build # compile to dist/
|
|
|
181
181
|
yarn start # run compiled output
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
+
### Telemetry in development
|
|
185
|
+
|
|
186
|
+
Copy `.env.example` to `.env`. Its defaults point telemetry at a local collector
|
|
187
|
+
(`TRACKING_COLLECTOR_URL=http://localhost:9090`) and tag events with a non-production app id (`TRACKING_APP_ID=dbdiagram-cli-test`), so development never writes to production analytics.
|
|
188
|
+
|
|
189
|
+
To disable tracking entirely, point the collector at a local address with nothing listening:
|
|
190
|
+
|
|
191
|
+
```sh
|
|
192
|
+
TRACKING_COLLECTOR_URL=http://localhost:9090 yarn dev push ...
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Sends fail silently (the CLI never retries and never surfaces telemetry errors), so no events leave your machine. To instead inspect what would be sent, run [Snowplow Micro](https://docs.snowplow.io/docs/data-product-studio/data-quality/snowplow-micro/) on that port and read `curl localhost:9090/micro/good` (passed validation) and `/micro/bad` (failed).
|
|
196
|
+
|
|
184
197
|
## Test
|
|
185
198
|
|
|
186
199
|
```sh
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import { spinner
|
|
2
|
+
import { spinner } from '@clack/prompts';
|
|
3
3
|
import open from 'open';
|
|
4
4
|
import { storeCredential } from '../../config/credential-manager.js';
|
|
5
5
|
import { PORTAL_LOGIN_CONFIGS, PORTAL_URLS } from '../../config.js';
|
|
6
|
-
import { initAuthClient
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
[PORTAL_AUTH_ERROR_CODES.loginCancelled]: 'Login cancelled.',
|
|
10
|
-
[PORTAL_AUTH_ERROR_CODES.stateMismatch]: 'Authentication failed: security validation error.',
|
|
11
|
-
[PORTAL_AUTH_ERROR_CODES.exchangeFailed]: 'Authentication failed: could not exchange token.',
|
|
12
|
-
[PORTAL_AUTH_ERROR_CODES.invalidCallback]: 'Authentication failed: invalid callback received.',
|
|
13
|
-
[PORTAL_AUTH_ERROR_CODES.loopbackServerError]: 'Authentication failed: could not start local server.',
|
|
14
|
-
[PORTAL_AUTH_ERROR_CODES.unknownError]: 'Authentication failed: an unexpected error occurred.',
|
|
15
|
-
};
|
|
6
|
+
import { initAuthClient } from '../../libs/portal/index.js';
|
|
7
|
+
import { toCliError } from '../../utils/cli-error.util.js';
|
|
8
|
+
import { displayError } from '../../utils/output.util.js';
|
|
16
9
|
export async function loginAction() {
|
|
10
|
+
const s = spinner();
|
|
17
11
|
try {
|
|
18
12
|
const authClient = await initAuthClient({
|
|
19
13
|
loginUrl: PORTAL_URLS.loginUrl,
|
|
@@ -25,7 +19,6 @@ export async function loginAction() {
|
|
|
25
19
|
error: path.join(import.meta.dirname, '../../assets/callback-error.html'),
|
|
26
20
|
},
|
|
27
21
|
});
|
|
28
|
-
const s = spinner();
|
|
29
22
|
await open(authClient.getLoginUrl());
|
|
30
23
|
s.start('Waiting for browser login...');
|
|
31
24
|
const { accessToken } = await authClient.obtainTokens();
|
|
@@ -33,12 +26,8 @@ export async function loginAction() {
|
|
|
33
26
|
s.stop('Login successful. Credentials saved.');
|
|
34
27
|
}
|
|
35
28
|
catch (error) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
process.exitCode = 1;
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
throw error;
|
|
29
|
+
s.stop();
|
|
30
|
+
displayError(toCliError(error), 'plain');
|
|
31
|
+
process.exitCode = 1;
|
|
43
32
|
}
|
|
44
33
|
}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { log } from '@clack/prompts';
|
|
2
2
|
import { clearCredential } from '../../config/credential-manager.js';
|
|
3
|
-
|
|
3
|
+
import { CliError } from '../../errors/cli.error.js';
|
|
4
|
+
import { AUTH_LOGOUT_FAILED } from '../../constants/cli-error.constant.js';
|
|
5
|
+
import { displayError, getOutputFormat, printJson } from '../../utils/output.util.js';
|
|
6
|
+
export async function logoutAction(options = {}) {
|
|
4
7
|
try {
|
|
5
8
|
clearCredential();
|
|
6
|
-
|
|
9
|
+
if (options.json) {
|
|
10
|
+
printJson({ success: true });
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
log.success('Logout successful.');
|
|
14
|
+
}
|
|
7
15
|
}
|
|
8
16
|
catch (error) {
|
|
9
17
|
const message = error instanceof Error ? error.message : String(error);
|
|
10
|
-
|
|
18
|
+
displayError(new CliError(AUTH_LOGOUT_FAILED, message), getOutputFormat(options.json));
|
|
11
19
|
process.exitCode = 1;
|
|
12
20
|
}
|
|
13
21
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spinner } from '@clack/prompts';
|
|
2
2
|
import { getCredential } from '../../config/credential-manager.js';
|
|
3
|
-
import {
|
|
3
|
+
import { AUTH_NOT_LOGGED_IN } from '../../constants/cli-error.constant.js';
|
|
4
|
+
import { CliError } from '../../errors/cli.error.js';
|
|
4
5
|
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
5
|
-
import {
|
|
6
|
-
import { printJson } from '../../utils/output.util.js';
|
|
6
|
+
import { getCliErrorMessageFromCode, toCliError } from '../../utils/cli-error.util.js';
|
|
7
|
+
import { displayError, getOutputFormat, printJson } from '../../utils/output.util.js';
|
|
7
8
|
export async function statusAction(options = {}) {
|
|
8
9
|
const credential = getCredential();
|
|
9
10
|
if (!credential) {
|
|
10
|
-
log.warn(NOT_LOGGED_IN_MESSAGE);
|
|
11
11
|
process.exitCode = 1;
|
|
12
|
-
return;
|
|
12
|
+
return displayError(new CliError(AUTH_NOT_LOGGED_IN, getCliErrorMessageFromCode(AUTH_NOT_LOGGED_IN)), getOutputFormat(options.json));
|
|
13
13
|
}
|
|
14
14
|
const s = spinner();
|
|
15
15
|
s.start('Checking login status...');
|
|
@@ -26,13 +26,7 @@ export async function statusAction(options = {}) {
|
|
|
26
26
|
}
|
|
27
27
|
catch (error) {
|
|
28
28
|
s.stop('Failed to check login status.');
|
|
29
|
-
|
|
30
|
-
log.error(getPortalApiErrorMessage(error));
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
34
|
-
log.error(message);
|
|
35
|
-
}
|
|
29
|
+
displayError(toCliError(error), getOutputFormat(options.json));
|
|
36
30
|
process.exitCode = 1;
|
|
37
31
|
}
|
|
38
32
|
}
|
|
@@ -1,64 +1,56 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { log, spinner } from '@clack/prompts';
|
|
3
|
+
import { DBDOCS_CONFIGS } from '../../config.js';
|
|
3
4
|
import { getCredential } from '../../config/credential-manager.js';
|
|
4
|
-
import {
|
|
5
|
+
import { DOCUMENT_SOURCE_TYPE } from '../../constants/document.constant.js';
|
|
6
|
+
import { AUTH_NOT_LOGGED_IN, DBML_PARSE_ERROR, FILE_READ_ERROR, PROJECT_NAME_INVALID, PROJECT_NAME_MISSING, SOURCE_MISSING, VERSION_NAME_INVALID, WORKSPACE_NAME_INVALID, WORKSPACE_NAME_MISSING, } from '../../constants/cli-error.constant.js';
|
|
7
|
+
import { CliError } from '../../errors/cli.error.js';
|
|
8
|
+
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
9
|
+
import { trackEvent } from '../../services/telemetry.service.js';
|
|
5
10
|
import { prepareBuildDocumentData } from '../../services/dbml/dbml.service.js';
|
|
11
|
+
import { getCliErrorMessageFromCode, toCliError } from '../../utils/cli-error.util.js';
|
|
6
12
|
import { formatDbmlError, getDbmlVersion } from '../../utils/dbml.util.js';
|
|
7
|
-
import {
|
|
8
|
-
import { DBDOCS_CONFIGS } from '../../config.js';
|
|
9
|
-
import { isPortalApiError, getPortalApiErrorMessage } from '../../utils/portal-error.util.js';
|
|
10
|
-
import { printJson } from '../../utils/output.util.js';
|
|
13
|
+
import { displayError, getOutputFormat, printJson } from '../../utils/output.util.js';
|
|
11
14
|
import { validateProjectUrlName, validateVersionName, validateWorkspaceUrlName, } from '../../utils/validation.util.js';
|
|
12
|
-
import { DOCUMENT_SOURCE_TYPE } from '../../constants/document.constant.js';
|
|
13
15
|
const DOCUMENT_SOURCE_CLIENT_TYPE = 'dbdiagram-cli';
|
|
14
16
|
export async function documentAction(options) {
|
|
15
17
|
const credential = getCredential();
|
|
16
18
|
if (!credential) {
|
|
17
|
-
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
18
19
|
process.exitCode = 1;
|
|
19
|
-
return;
|
|
20
|
+
return displayError(new CliError(AUTH_NOT_LOGGED_IN, getCliErrorMessageFromCode(AUTH_NOT_LOGGED_IN)), getOutputFormat(options.json));
|
|
20
21
|
}
|
|
21
22
|
if (options.versionName !== undefined) {
|
|
22
23
|
const versionError = validateVersionName(options.versionName);
|
|
23
24
|
if (versionError) {
|
|
24
|
-
log.error(versionError);
|
|
25
25
|
process.exitCode = 1;
|
|
26
|
-
return;
|
|
26
|
+
return displayError(new CliError(VERSION_NAME_INVALID, versionError), getOutputFormat(options.json));
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
if (!options.workspaceUrlName) {
|
|
30
|
-
log.error('Workspace url name is required. Use --project <workspace>/<project> '
|
|
31
|
-
+ 'or configure workspaceUrlName in settings.json');
|
|
32
30
|
process.exitCode = 1;
|
|
33
|
-
return;
|
|
31
|
+
return displayError(new CliError(WORKSPACE_NAME_MISSING, getCliErrorMessageFromCode(WORKSPACE_NAME_MISSING)), getOutputFormat(options.json));
|
|
34
32
|
}
|
|
35
33
|
const workspaceError = validateWorkspaceUrlName(options.workspaceUrlName);
|
|
36
34
|
if (workspaceError) {
|
|
37
|
-
log.error(workspaceError);
|
|
38
35
|
process.exitCode = 1;
|
|
39
|
-
return;
|
|
36
|
+
return displayError(new CliError(WORKSPACE_NAME_INVALID, workspaceError), getOutputFormat(options.json));
|
|
40
37
|
}
|
|
41
38
|
if (!options.projectUrlOrName) {
|
|
42
|
-
log.error('Project name/url name is required. Use --project <workspace>/<project> '
|
|
43
|
-
+ 'or configure projectUrlName in settings.json');
|
|
44
39
|
process.exitCode = 1;
|
|
45
|
-
return;
|
|
40
|
+
return displayError(new CliError(PROJECT_NAME_MISSING, getCliErrorMessageFromCode(PROJECT_NAME_MISSING)), getOutputFormat(options.json));
|
|
46
41
|
}
|
|
47
42
|
if (options.projectUrlOrName.includes('/')) {
|
|
48
|
-
log.error('Invalid --project value. Expected <workspace>/<project> or <project>.');
|
|
49
43
|
process.exitCode = 1;
|
|
50
|
-
return;
|
|
44
|
+
return displayError(new CliError(PROJECT_NAME_INVALID, 'Invalid --project value. Expected <workspace>/<project> or <project>.'), getOutputFormat(options.json));
|
|
51
45
|
}
|
|
52
46
|
const projectError = validateProjectUrlName(options.projectUrlOrName);
|
|
53
47
|
if (projectError) {
|
|
54
|
-
log.error(projectError);
|
|
55
48
|
process.exitCode = 1;
|
|
56
|
-
return;
|
|
49
|
+
return displayError(new CliError(PROJECT_NAME_INVALID, projectError), getOutputFormat(options.json));
|
|
57
50
|
}
|
|
58
51
|
if (options.source === undefined) {
|
|
59
|
-
log.error('One source must be specified: --from-file or --from-diagram');
|
|
60
52
|
process.exitCode = 1;
|
|
61
|
-
return;
|
|
53
|
+
return displayError(new CliError(SOURCE_MISSING, getCliErrorMessageFromCode(SOURCE_MISSING)), getOutputFormat(options.json));
|
|
62
54
|
}
|
|
63
55
|
let content;
|
|
64
56
|
if (options.source.type === DOCUMENT_SOURCE_TYPE.FILE) {
|
|
@@ -67,9 +59,8 @@ export async function documentAction(options) {
|
|
|
67
59
|
}
|
|
68
60
|
catch (error) {
|
|
69
61
|
const message = error instanceof Error ? error.message : String(error);
|
|
70
|
-
log.error(`Failed to read file: ${message}`);
|
|
71
62
|
process.exitCode = 1;
|
|
72
|
-
return;
|
|
63
|
+
return displayError(new CliError(FILE_READ_ERROR, `Failed to read file: ${message}`), getOutputFormat(options.json));
|
|
73
64
|
}
|
|
74
65
|
}
|
|
75
66
|
else {
|
|
@@ -78,15 +69,8 @@ export async function documentAction(options) {
|
|
|
78
69
|
content = diagram.content;
|
|
79
70
|
}
|
|
80
71
|
catch (error) {
|
|
81
|
-
if (isPortalApiError(error)) {
|
|
82
|
-
log.error(getPortalApiErrorMessage(error));
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
86
|
-
log.error(`Failed to fetch diagram: ${message}`);
|
|
87
|
-
}
|
|
88
72
|
process.exitCode = 1;
|
|
89
|
-
return;
|
|
73
|
+
return displayError(toCliError(error), getOutputFormat(options.json));
|
|
90
74
|
}
|
|
91
75
|
}
|
|
92
76
|
if (!content || !content.trim()) {
|
|
@@ -99,13 +83,13 @@ export async function documentAction(options) {
|
|
|
99
83
|
preparedDocumentData = await prepareBuildDocumentData(content);
|
|
100
84
|
}
|
|
101
85
|
catch (error) {
|
|
102
|
-
log.error(`Failed to parse DBML: ${formatDbmlError(error)}`);
|
|
103
86
|
process.exitCode = 1;
|
|
104
|
-
return;
|
|
87
|
+
return displayError(new CliError(DBML_PARSE_ERROR, `Failed to parse DBML: ${formatDbmlError(error)}`), getOutputFormat(options.json));
|
|
105
88
|
}
|
|
106
89
|
const s = spinner();
|
|
107
90
|
s.start('Building document on dbdocs...');
|
|
108
91
|
try {
|
|
92
|
+
const currentUser = await portalIntegration.getCurrentUser();
|
|
109
93
|
const { project } = await portalIntegration.createDocument({
|
|
110
94
|
projectName: options.projectUrlOrName,
|
|
111
95
|
projectDescription: preparedDocumentData.description,
|
|
@@ -119,10 +103,16 @@ export async function documentAction(options) {
|
|
|
119
103
|
normalizedDatabase: preparedDocumentData.normalizedDatabase,
|
|
120
104
|
dbmlVersion: getDbmlVersion(),
|
|
121
105
|
});
|
|
122
|
-
const
|
|
106
|
+
const { workspaceUrl: workspaceUrlName, urlName: projectUrlName } = project;
|
|
107
|
+
trackEvent({
|
|
108
|
+
name: 'cli_document_build',
|
|
109
|
+
properties: { workspaceUrlName, projectUrlName },
|
|
110
|
+
userId: currentUser.id.toString(),
|
|
111
|
+
});
|
|
112
|
+
const url = `${DBDOCS_CONFIGS.baseUrl}/${workspaceUrlName}/${projectUrlName}`;
|
|
123
113
|
if (options.json) {
|
|
124
114
|
s.stop();
|
|
125
|
-
printJson({ projectUrlName: `${
|
|
115
|
+
printJson({ projectUrlName: `${workspaceUrlName}/${projectUrlName}`, url });
|
|
126
116
|
}
|
|
127
117
|
else {
|
|
128
118
|
s.stop(`Document built successfully. Visit: ${url}`);
|
|
@@ -130,13 +120,7 @@ export async function documentAction(options) {
|
|
|
130
120
|
}
|
|
131
121
|
catch (error) {
|
|
132
122
|
s.stop('Failed to build document.');
|
|
133
|
-
|
|
134
|
-
log.error(getPortalApiErrorMessage(error));
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
138
|
-
log.error(message);
|
|
139
|
-
}
|
|
123
|
+
displayError(toCliError(error), getOutputFormat(options.json));
|
|
140
124
|
process.exitCode = 1;
|
|
141
125
|
}
|
|
142
126
|
}
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import { confirm, isCancel, log, spinner } from '@clack/prompts';
|
|
2
2
|
import { getCredential } from '../config/credential-manager.js';
|
|
3
|
-
import { NOT_LOGGED_IN_MESSAGE } from '../constants/auth-message.constant.js';
|
|
4
3
|
import { getSettings, writeSettings } from '../config/settings-manager.js';
|
|
4
|
+
import { AUTH_NOT_LOGGED_IN, DIAGRAM_ID_MISSING } from '../constants/cli-error.constant.js';
|
|
5
|
+
import { CliError } from '../errors/cli.error.js';
|
|
5
6
|
import { portalIntegration } from '../integrations/portal/portal.integration.js';
|
|
6
|
-
import {
|
|
7
|
-
import { printJson } from '../utils/output.util.js';
|
|
7
|
+
import { getCliErrorMessageFromCode, toCliError } from '../utils/cli-error.util.js';
|
|
8
|
+
import { displayError, getOutputFormat, printJson } from '../utils/output.util.js';
|
|
8
9
|
export async function deleteAction(options) {
|
|
9
10
|
const credential = getCredential();
|
|
10
11
|
if (!credential) {
|
|
11
|
-
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
12
12
|
process.exitCode = 1;
|
|
13
|
-
return;
|
|
13
|
+
return displayError(new CliError(AUTH_NOT_LOGGED_IN, getCliErrorMessageFromCode(AUTH_NOT_LOGGED_IN)), getOutputFormat(options.json));
|
|
14
14
|
}
|
|
15
15
|
if (!options.diagramId) {
|
|
16
|
-
log.error('Missing required flag: --diagram-id');
|
|
17
16
|
process.exitCode = 1;
|
|
18
|
-
return;
|
|
17
|
+
return displayError(new CliError(DIAGRAM_ID_MISSING, getCliErrorMessageFromCode(DIAGRAM_ID_MISSING)), getOutputFormat(options.json));
|
|
19
18
|
}
|
|
20
19
|
if (!options.force) {
|
|
21
20
|
const shouldDelete = await confirm({
|
|
@@ -42,7 +41,7 @@ export async function deleteAction(options) {
|
|
|
42
41
|
printJson({
|
|
43
42
|
diagramId: options.diagramId,
|
|
44
43
|
deleted: true,
|
|
45
|
-
...(isRemoveInSettings ? {
|
|
44
|
+
...(isRemoveInSettings ? { diagramIdSettingCleared: true } : {}),
|
|
46
45
|
});
|
|
47
46
|
}
|
|
48
47
|
else {
|
|
@@ -55,13 +54,7 @@ export async function deleteAction(options) {
|
|
|
55
54
|
catch (error) {
|
|
56
55
|
s.error('Failed to delete diagram.');
|
|
57
56
|
s.stop();
|
|
58
|
-
|
|
59
|
-
log.error(getPortalApiErrorMessage(error));
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
63
|
-
log.error(message);
|
|
64
|
-
}
|
|
57
|
+
displayError(toCliError(error), getOutputFormat(options.json));
|
|
65
58
|
process.exitCode = 1;
|
|
66
59
|
}
|
|
67
60
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { log, spinner } from '@clack/prompts';
|
|
2
2
|
import { DBDOCS_CONFIGS } from '../../config.js';
|
|
3
3
|
import { getCredential } from '../../config/credential-manager.js';
|
|
4
|
-
import {
|
|
4
|
+
import { CliError } from '../../errors/cli.error.js';
|
|
5
|
+
import { getCliErrorMessageFromCode, toCliError } from '../../utils/cli-error.util.js';
|
|
6
|
+
import { AUTH_NOT_LOGGED_IN } from '../../constants/cli-error.constant.js';
|
|
5
7
|
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
8
|
+
import { formatShortDateTime } from '../../utils/date.util.js';
|
|
6
9
|
import { logConsole } from '../../utils/logger.util.js';
|
|
7
|
-
import { printJson, printTable } from '../../utils/output.util.js';
|
|
8
|
-
import { isPortalApiError, getPortalApiErrorMessage } from '../../utils/portal-error.util.js';
|
|
9
|
-
import { computeColWidths } from '../../utils/table.util.js';
|
|
10
|
+
import { displayError, getOutputFormat, printJson, printTable } from '../../utils/output.util.js';
|
|
10
11
|
function mapRole(role) {
|
|
11
12
|
switch (role) {
|
|
12
13
|
case 'admin':
|
|
@@ -34,9 +35,8 @@ function mapAccessControl(ac) {
|
|
|
34
35
|
export async function listDocumentAction(opts) {
|
|
35
36
|
const credential = getCredential();
|
|
36
37
|
if (!credential) {
|
|
37
|
-
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
38
38
|
process.exitCode = 1;
|
|
39
|
-
return;
|
|
39
|
+
return displayError(new CliError(AUTH_NOT_LOGGED_IN, getCliErrorMessageFromCode(AUTH_NOT_LOGGED_IN)), getOutputFormat(opts.json));
|
|
40
40
|
}
|
|
41
41
|
const s = spinner();
|
|
42
42
|
s.start('Fetching documents...');
|
|
@@ -87,30 +87,29 @@ export async function listDocumentAction(opts) {
|
|
|
87
87
|
return {
|
|
88
88
|
workspace: w.urlName,
|
|
89
89
|
name: p.name,
|
|
90
|
+
url: `${DBDOCS_CONFIGS.baseUrl}/${w.urlName}/${p.urlName}`,
|
|
90
91
|
owner: ownerLabel,
|
|
91
92
|
permission: mapRole(p.role),
|
|
92
93
|
accessControl: mapAccessControl(p.accessControl),
|
|
93
|
-
|
|
94
|
-
updatedAt: p.updatedAt,
|
|
94
|
+
updatedAt: formatShortDateTime(p.updatedAt),
|
|
95
95
|
};
|
|
96
96
|
}));
|
|
97
97
|
printJson(allMappedData);
|
|
98
98
|
}
|
|
99
99
|
else {
|
|
100
100
|
for (const w of workspacesWithProjects) {
|
|
101
|
-
logConsole(`\
|
|
101
|
+
logConsole(`\nWorkspace: ${w.urlName}`);
|
|
102
102
|
printTable(w.projects, {
|
|
103
|
-
head: ['Name', 'Owner', 'Permission', 'Access Control', '
|
|
104
|
-
colWidths: computeColWidths([0.14, 0.22, 0.12, 0.14, 0.24, 0.14]),
|
|
103
|
+
head: ['Name', 'Url', 'Owner', 'Permission', 'Access Control', 'Last updated'],
|
|
105
104
|
rowMapper: (p) => {
|
|
106
105
|
const ownerLabel = p.owner.email === currentUser.email ? `${p.owner.email} (you)` : p.owner.email;
|
|
107
106
|
return [
|
|
108
107
|
p.name,
|
|
108
|
+
`${DBDOCS_CONFIGS.baseUrl}/${w.urlName}/${p.urlName}`,
|
|
109
109
|
ownerLabel,
|
|
110
110
|
mapRole(p.role),
|
|
111
111
|
mapAccessControl(p.accessControl),
|
|
112
|
-
|
|
113
|
-
new Date(p.updatedAt).toLocaleString(),
|
|
112
|
+
formatShortDateTime(p.updatedAt),
|
|
114
113
|
];
|
|
115
114
|
},
|
|
116
115
|
});
|
|
@@ -119,12 +118,7 @@ export async function listDocumentAction(opts) {
|
|
|
119
118
|
}
|
|
120
119
|
catch (error) {
|
|
121
120
|
s.stop('Failed to fetch documents.');
|
|
122
|
-
|
|
123
|
-
log.error(getPortalApiErrorMessage(error));
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
log.error(error instanceof Error ? error.message : String(error));
|
|
127
|
-
}
|
|
121
|
+
displayError(toCliError(error), getOutputFormat(opts.json));
|
|
128
122
|
process.exitCode = 1;
|
|
129
123
|
}
|
|
130
124
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spinner } from '@clack/prompts';
|
|
2
2
|
import { DBDIAGRAM_CONFIGS } from '../../config.js';
|
|
3
3
|
import { getCredential } from '../../config/credential-manager.js';
|
|
4
|
-
import {
|
|
4
|
+
import { CliError } from '../../errors/cli.error.js';
|
|
5
|
+
import { getCliErrorMessageFromCode, toCliError } from '../../utils/cli-error.util.js';
|
|
6
|
+
import { AUTH_NOT_LOGGED_IN } from '../../constants/cli-error.constant.js';
|
|
5
7
|
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { printJson, printTable } from '../../utils/output.util.js';
|
|
8
|
+
import { formatShortDateTime } from '../../utils/date.util.js';
|
|
9
|
+
import { displayError, getOutputFormat, printJson, printTable } from '../../utils/output.util.js';
|
|
9
10
|
function mapStatus(status) {
|
|
10
11
|
switch (status) {
|
|
11
12
|
case 'public':
|
|
@@ -24,9 +25,8 @@ function ownerLabel(owner, currentUserEmail) {
|
|
|
24
25
|
export async function listAction(opts) {
|
|
25
26
|
const credential = getCredential();
|
|
26
27
|
if (!credential) {
|
|
27
|
-
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
28
28
|
process.exitCode = 1;
|
|
29
|
-
return;
|
|
29
|
+
return displayError(new CliError(AUTH_NOT_LOGGED_IN, getCliErrorMessageFromCode(AUTH_NOT_LOGGED_IN)), getOutputFormat(opts.json));
|
|
30
30
|
}
|
|
31
31
|
const s = spinner();
|
|
32
32
|
s.start('Fetching diagrams...');
|
|
@@ -52,7 +52,7 @@ export async function listAction(opts) {
|
|
|
52
52
|
owner: ownerLabel(d.owner, currentUser.email),
|
|
53
53
|
status: mapStatus(d.status),
|
|
54
54
|
url: `${DBDIAGRAM_CONFIGS.baseUrl}/d/${d.id}`,
|
|
55
|
-
updatedAt:
|
|
55
|
+
updatedAt: formatShortDateTime(d.updatedAt),
|
|
56
56
|
}));
|
|
57
57
|
if (opts.json) {
|
|
58
58
|
printJson(mappedData);
|
|
@@ -60,7 +60,6 @@ export async function listAction(opts) {
|
|
|
60
60
|
else {
|
|
61
61
|
printTable(mappedData, {
|
|
62
62
|
head: ['Name', 'ID', 'Owner', 'Status', 'Url', 'Last updated'],
|
|
63
|
-
colWidths: computeColWidths([0.18, 0.22, 0.14, 0.09, 0.26, 0.11]),
|
|
64
63
|
rowMapper: (d) => [d.name, d.id, d.owner, d.status, d.url, d.updatedAt],
|
|
65
64
|
});
|
|
66
65
|
}
|
|
@@ -76,28 +75,22 @@ export async function listAction(opts) {
|
|
|
76
75
|
name: d.name,
|
|
77
76
|
id: d.id,
|
|
78
77
|
url: `${DBDIAGRAM_CONFIGS.baseUrl}/d/${d.id}`,
|
|
79
|
-
updatedAt:
|
|
78
|
+
updatedAt: formatShortDateTime(d.updatedAt),
|
|
80
79
|
}));
|
|
81
80
|
if (opts.json) {
|
|
82
81
|
printJson(mappedData);
|
|
83
82
|
}
|
|
84
83
|
else {
|
|
85
84
|
printTable(mappedData, {
|
|
86
|
-
head: ['
|
|
87
|
-
|
|
88
|
-
rowMapper: (d) => [d.name, d.id, d.url, d.updatedAt],
|
|
85
|
+
head: ['ID', 'Url', 'Name', 'Last updated'],
|
|
86
|
+
rowMapper: (d) => [d.id, d.url, d.name, d.updatedAt],
|
|
89
87
|
});
|
|
90
88
|
}
|
|
91
89
|
}
|
|
92
90
|
}
|
|
93
91
|
catch (error) {
|
|
94
92
|
s.stop('Failed to fetch diagrams.');
|
|
95
|
-
|
|
96
|
-
log.error(getPortalApiErrorMessage(error));
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
log.error(error instanceof Error ? error.message : String(error));
|
|
100
|
-
}
|
|
93
|
+
displayError(toCliError(error), getOutputFormat(opts.json));
|
|
101
94
|
process.exitCode = 1;
|
|
102
95
|
}
|
|
103
96
|
}
|
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { NOT_LOGGED_IN_MESSAGE } from '../constants/auth-message.constant.js';
|
|
1
|
+
import { spinner } from '@clack/prompts';
|
|
3
2
|
import { getCredential } from '../config/credential-manager.js';
|
|
4
3
|
import { writeFileWithDir } from '../services/file.service.js';
|
|
5
4
|
import { portalIntegration } from '../integrations/portal/portal.integration.js';
|
|
6
5
|
import { deriveDiagramVizPathFromDBML, readDiagramVizFile, writeDiagramVizFile, } from '../services/viz/diagram-viz-file.service.js';
|
|
7
|
-
import { getVizReadErrorMessage } from '../utils/diagram-viz-error.util.js';
|
|
8
6
|
import { fromServerFormat } from '../services/viz/diagram-viz-converter.service.js';
|
|
9
7
|
import { logConsole } from '../utils/logger.util.js';
|
|
10
|
-
import {
|
|
8
|
+
import { CliError } from '../errors/cli.error.js';
|
|
9
|
+
import { AUTH_NOT_LOGGED_IN, DIAGRAM_ID_MISSING } from '../constants/cli-error.constant.js';
|
|
10
|
+
import { getCliErrorMessageFromCode, toCliError } from '../utils/cli-error.util.js';
|
|
11
|
+
import { displayError } from '../utils/output.util.js';
|
|
11
12
|
export async function pullAction(options) {
|
|
12
13
|
const credential = getCredential();
|
|
13
14
|
if (!credential) {
|
|
14
|
-
|
|
15
|
+
displayError(new CliError(AUTH_NOT_LOGGED_IN, getCliErrorMessageFromCode(AUTH_NOT_LOGGED_IN)), 'plain');
|
|
15
16
|
process.exitCode = 1;
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
18
19
|
if (!options.diagramId) {
|
|
19
|
-
|
|
20
|
+
displayError(new CliError(DIAGRAM_ID_MISSING, getCliErrorMessageFromCode(DIAGRAM_ID_MISSING)), 'plain');
|
|
20
21
|
process.exitCode = 1;
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
@@ -29,10 +30,6 @@ export async function pullAction(options) {
|
|
|
29
30
|
if (diagram.vizData) {
|
|
30
31
|
const vizPath = deriveDiagramVizPathFromDBML(options.outFile);
|
|
31
32
|
const existingResult = await readDiagramVizFile(vizPath);
|
|
32
|
-
if (!existingResult.ok) {
|
|
33
|
-
const message = getVizReadErrorMessage(existingResult.errorCode, vizPath);
|
|
34
|
-
log.warn(message);
|
|
35
|
-
}
|
|
36
33
|
const state = fromServerFormat(diagram.vizData, existingResult.ok ? existingResult.state : undefined);
|
|
37
34
|
await writeDiagramVizFile(vizPath, state);
|
|
38
35
|
}
|
|
@@ -46,13 +43,7 @@ export async function pullAction(options) {
|
|
|
46
43
|
}
|
|
47
44
|
catch (error) {
|
|
48
45
|
s.stop('Failed to fetch diagram.');
|
|
49
|
-
|
|
50
|
-
log.error(getPortalApiErrorMessage(error));
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
54
|
-
log.error(message);
|
|
55
|
-
}
|
|
46
|
+
displayError(toCliError(error), 'plain');
|
|
56
47
|
process.exitCode = 1;
|
|
57
48
|
}
|
|
58
49
|
}
|