dbdiagram 0.5.0 → 0.6.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 +17 -1
- package/dist/actions/build/build-document.action.js +19 -26
- package/dist/actions/pull.action.js +49 -12
- package/dist/actions/push.action.js +2 -2
- package/dist/commands/pull.command.js +10 -2
- package/dist/constants/cli-error.constant.js +22 -0
- package/dist/constants/document.constant.js +1 -0
- package/dist/constants/pull.constant.js +4 -0
- package/dist/hooks/build-document.hook.js +12 -11
- package/dist/hooks/pull.hook.js +22 -2
- package/dist/integrations/portal/portal.integration.js +5 -0
- package/dist/services/viz/diagram-viz-converter.service.js +18 -1
- package/dist/types/document.type.js +1 -0
- package/dist/types/pull.type.js +1 -0
- package/dist/utils/document.util.js +7 -0
- package/dist/utils/validation.util.js +41 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,18 +93,34 @@ dbdiagram push schema.dbml --new # force-create, ignoring
|
|
|
93
93
|
|
|
94
94
|
### Pull
|
|
95
95
|
|
|
96
|
-
Pull DBML content from a diagram on dbdiagram.io.
|
|
96
|
+
Pull DBML content from a diagram on dbdiagram.io or a document on dbdocs.io.
|
|
97
97
|
|
|
98
98
|
**Options:**
|
|
99
99
|
|
|
100
100
|
- `--diagram-id <id>` — The diagram to pull from
|
|
101
|
+
- `--project <name>` — The document to pull from, as `<workspace>/<project>` or just `<project>`
|
|
101
102
|
- `-o, --out-file <filepath>` — Save to a file (omit to use the configured entry file from settings, or print to stdout)
|
|
102
103
|
|
|
104
|
+
**Source resolution:**
|
|
105
|
+
|
|
106
|
+
| Input | Resolves to |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| `--diagram-id <id>` | The given diagram |
|
|
109
|
+
| `--project <workspace>/<project>` | The given document |
|
|
110
|
+
| `--project <project>`, no `/` | The document `<project>` in the workspace from settings |
|
|
111
|
+
| Neither flag | The diagram from settings (`diagram.id`) |
|
|
112
|
+
| Both flags | The one appearing last on the command line wins |
|
|
113
|
+
|
|
103
114
|
```sh
|
|
104
115
|
dbdiagram pull --diagram-id <id> # print to stdout (or save to the configured entry file)
|
|
105
116
|
dbdiagram pull --diagram-id <id> -o schema.dbml # save to a specific file
|
|
117
|
+
dbdiagram pull --project myteam/myproject # pull a document, print to stdout
|
|
118
|
+
dbdiagram pull --project myproject -o schema.dbml # pull a document, workspace from settings
|
|
106
119
|
```
|
|
107
120
|
|
|
121
|
+
A document or diagram with no DBML yet returns empty content: pull warns and skips the write rather than
|
|
122
|
+
overwriting your local file (and its viz file) with nothing, and exits `0`.
|
|
123
|
+
|
|
108
124
|
---
|
|
109
125
|
|
|
110
126
|
### List
|
|
@@ -2,18 +2,19 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import { log, spinner } from '@clack/prompts';
|
|
3
3
|
import { DBDOCS_CONFIGS } from '../../config.js';
|
|
4
4
|
import { getCredential } from '../../config/credential-manager.js';
|
|
5
|
-
import { DOCUMENT_SOURCE_TYPE } from '../../constants/document.constant.js';
|
|
6
|
-
import { AUTH_NOT_LOGGED_IN,
|
|
5
|
+
import { DOCUMENT_SOURCE_CLIENT_TYPE, DOCUMENT_SOURCE_TYPE } from '../../constants/document.constant.js';
|
|
6
|
+
import { AUTH_NOT_LOGGED_IN, SOURCE_MISSING, VERSION_NAME_INVALID, FILE_READ_ERROR, } from '../../constants/cli-error.constant.js';
|
|
7
7
|
import { CliError } from '../../errors/cli.error.js';
|
|
8
8
|
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
9
9
|
import { trackEvent } from '../../services/telemetry.service.js';
|
|
10
10
|
import { prepareBuildDocumentData } from '../../services/dbml/dbml.service.js';
|
|
11
|
+
import { deriveDiagramVizPathFromDBML, readDiagramVizFile } from '../../services/viz/diagram-viz-file.service.js';
|
|
12
|
+
import { toDiagramVizData, toDocumentVizData } from '../../services/viz/diagram-viz-converter.service.js';
|
|
11
13
|
import { getCliErrorMessageFromCode, toCliError } from '../../utils/cli-error.util.js';
|
|
12
14
|
import { getDbmlVersion } from '../../utils/dbml.util.js';
|
|
13
15
|
import { DbmlParseError } from '../../errors/dbml-parse.error.js';
|
|
14
16
|
import { displayDbmlParseError, displayError, getOutputFormat, printJson } from '../../utils/output.util.js';
|
|
15
|
-
import {
|
|
16
|
-
const DOCUMENT_SOURCE_CLIENT_TYPE = 'dbdiagram-cli';
|
|
17
|
+
import { validateAndResolveBuildDocumentProjectNames, validateVersionName } from '../../utils/validation.util.js';
|
|
17
18
|
export async function documentAction(options) {
|
|
18
19
|
const credential = getCredential();
|
|
19
20
|
if (!credential) {
|
|
@@ -27,33 +28,18 @@ export async function documentAction(options) {
|
|
|
27
28
|
return displayError(new CliError(VERSION_NAME_INVALID, versionError), getOutputFormat(options.json));
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
+
const resolvedNames = validateAndResolveBuildDocumentProjectNames(options.workspaceUrlName, options.projectUrlOrName);
|
|
32
|
+
if (resolvedNames instanceof CliError) {
|
|
31
33
|
process.exitCode = 1;
|
|
32
|
-
return displayError(
|
|
33
|
-
}
|
|
34
|
-
const workspaceError = validateWorkspaceUrlName(options.workspaceUrlName);
|
|
35
|
-
if (workspaceError) {
|
|
36
|
-
process.exitCode = 1;
|
|
37
|
-
return displayError(new CliError(WORKSPACE_NAME_INVALID, workspaceError), getOutputFormat(options.json));
|
|
38
|
-
}
|
|
39
|
-
if (!options.projectUrlOrName) {
|
|
40
|
-
process.exitCode = 1;
|
|
41
|
-
return displayError(new CliError(PROJECT_NAME_MISSING, getCliErrorMessageFromCode(PROJECT_NAME_MISSING)), getOutputFormat(options.json));
|
|
42
|
-
}
|
|
43
|
-
if (options.projectUrlOrName.includes('/')) {
|
|
44
|
-
process.exitCode = 1;
|
|
45
|
-
return displayError(new CliError(PROJECT_NAME_INVALID, 'Invalid --project value. Expected <workspace>/<project> or <project>.'), getOutputFormat(options.json));
|
|
46
|
-
}
|
|
47
|
-
const projectError = validateProjectUrlName(options.projectUrlOrName);
|
|
48
|
-
if (projectError) {
|
|
49
|
-
process.exitCode = 1;
|
|
50
|
-
return displayError(new CliError(PROJECT_NAME_INVALID, projectError), getOutputFormat(options.json));
|
|
34
|
+
return displayError(resolvedNames, getOutputFormat(options.json));
|
|
51
35
|
}
|
|
36
|
+
const { workspaceUrlName: targetWorkspaceUrlName, projectUrlOrName: targetProjectUrlOrName, } = resolvedNames;
|
|
52
37
|
if (options.source === undefined) {
|
|
53
38
|
process.exitCode = 1;
|
|
54
39
|
return displayError(new CliError(SOURCE_MISSING, getCliErrorMessageFromCode(SOURCE_MISSING)), getOutputFormat(options.json));
|
|
55
40
|
}
|
|
56
41
|
let content;
|
|
42
|
+
let vizData;
|
|
57
43
|
if (options.source.type === DOCUMENT_SOURCE_TYPE.FILE) {
|
|
58
44
|
try {
|
|
59
45
|
content = await readFile(options.source.value, 'utf8');
|
|
@@ -63,11 +49,17 @@ export async function documentAction(options) {
|
|
|
63
49
|
process.exitCode = 1;
|
|
64
50
|
return displayError(new CliError(FILE_READ_ERROR, `Failed to read file: ${message}`), getOutputFormat(options.json));
|
|
65
51
|
}
|
|
52
|
+
const vizPath = deriveDiagramVizPathFromDBML(options.source.value);
|
|
53
|
+
const vizResult = await readDiagramVizFile(vizPath);
|
|
54
|
+
if (vizResult.ok) {
|
|
55
|
+
vizData = toDocumentVizData(toDiagramVizData(vizResult.state));
|
|
56
|
+
}
|
|
66
57
|
}
|
|
67
58
|
else {
|
|
68
59
|
try {
|
|
69
60
|
const diagram = await portalIntegration.getDiagram(options.source.value);
|
|
70
61
|
content = diagram.content;
|
|
62
|
+
vizData = diagram.vizData ? toDocumentVizData(diagram.vizData) : undefined;
|
|
71
63
|
}
|
|
72
64
|
catch (error) {
|
|
73
65
|
process.exitCode = 1;
|
|
@@ -98,9 +90,9 @@ export async function documentAction(options) {
|
|
|
98
90
|
try {
|
|
99
91
|
const currentUser = await portalIntegration.getCurrentUser();
|
|
100
92
|
const { project } = await portalIntegration.createDocument({
|
|
101
|
-
projectName:
|
|
93
|
+
projectName: targetProjectUrlOrName,
|
|
102
94
|
projectDescription: preparedDocumentData.description,
|
|
103
|
-
workspaceUrlName:
|
|
95
|
+
workspaceUrlName: targetWorkspaceUrlName,
|
|
104
96
|
clientType: DOCUMENT_SOURCE_CLIENT_TYPE,
|
|
105
97
|
doc: {
|
|
106
98
|
content,
|
|
@@ -109,6 +101,7 @@ export async function documentAction(options) {
|
|
|
109
101
|
shallowSchema: preparedDocumentData.shallowSchema,
|
|
110
102
|
normalizedDatabase: preparedDocumentData.normalizedDatabase,
|
|
111
103
|
dbmlVersion: getDbmlVersion(),
|
|
104
|
+
vizData,
|
|
112
105
|
});
|
|
113
106
|
const { workspaceUrl: workspaceUrlName, urlName: projectUrlName } = project;
|
|
114
107
|
trackEvent({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spinner } from '@clack/prompts';
|
|
1
|
+
import { log, spinner } from '@clack/prompts';
|
|
2
2
|
import { getCredential } from '../config/credential-manager.js';
|
|
3
3
|
import { writeFileWithDir } from '../services/file.service.js';
|
|
4
4
|
import { portalIntegration } from '../integrations/portal/portal.integration.js';
|
|
@@ -7,7 +7,9 @@ import { fromServerFormat } from '../services/viz/diagram-viz-converter.service.
|
|
|
7
7
|
import { logConsole } from '../utils/logger.util.js';
|
|
8
8
|
import { CliError } from '../errors/cli.error.js';
|
|
9
9
|
import { AUTH_NOT_LOGGED_IN, DIAGRAM_ID_MISSING } from '../constants/cli-error.constant.js';
|
|
10
|
+
import { PULL_SOURCE_TYPE } from '../constants/pull.constant.js';
|
|
10
11
|
import { getCliErrorMessageFromCode, toCliError } from '../utils/cli-error.util.js';
|
|
12
|
+
import { validateAndResolvePullDocumentProjectNames } from '../utils/validation.util.js';
|
|
11
13
|
import { displayError } from '../utils/output.util.js';
|
|
12
14
|
export async function pullAction(options) {
|
|
13
15
|
const credential = getCredential();
|
|
@@ -16,33 +18,68 @@ export async function pullAction(options) {
|
|
|
16
18
|
process.exitCode = 1;
|
|
17
19
|
return;
|
|
18
20
|
}
|
|
19
|
-
|
|
21
|
+
const { source } = options;
|
|
22
|
+
if (!source) {
|
|
20
23
|
displayError(new CliError(DIAGRAM_ID_MISSING, getCliErrorMessageFromCode(DIAGRAM_ID_MISSING)), 'plain');
|
|
21
24
|
process.exitCode = 1;
|
|
22
25
|
return;
|
|
23
26
|
}
|
|
27
|
+
let target;
|
|
28
|
+
if (source.type === PULL_SOURCE_TYPE.DOCUMENT) {
|
|
29
|
+
const resolved = validateAndResolvePullDocumentProjectNames(source.workspaceUrlName, source.projectUrlOrName);
|
|
30
|
+
if (resolved instanceof CliError) {
|
|
31
|
+
displayError(resolved, 'plain');
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
target = { kind: 'document', ...resolved };
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
target = { kind: 'diagram', diagramId: source.value };
|
|
39
|
+
}
|
|
40
|
+
const isDocumentSource = target.kind === 'document';
|
|
24
41
|
const s = spinner();
|
|
25
|
-
s.start('Fetching diagram...');
|
|
42
|
+
s.start(isDocumentSource ? 'Fetching document...' : 'Fetching diagram...');
|
|
26
43
|
try {
|
|
27
|
-
|
|
44
|
+
let content;
|
|
45
|
+
let vizData;
|
|
46
|
+
let successLabel;
|
|
47
|
+
if (target.kind === 'document') {
|
|
48
|
+
const { workspaceUrlName, projectUrlOrName } = target;
|
|
49
|
+
const document = await portalIntegration.getDocument(workspaceUrlName, projectUrlOrName);
|
|
50
|
+
content = document.content;
|
|
51
|
+
vizData = document.vizData;
|
|
52
|
+
successLabel = `${workspaceUrlName}/${projectUrlOrName}`;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const diagram = await portalIntegration.getDiagram(target.diagramId);
|
|
56
|
+
content = diagram.content;
|
|
57
|
+
vizData = diagram.vizData;
|
|
58
|
+
successLabel = diagram.name ? `"${diagram.name}"` : 'your diagram';
|
|
59
|
+
}
|
|
28
60
|
if (options.outFile) {
|
|
29
|
-
|
|
30
|
-
|
|
61
|
+
if (!content || !content.trim()) {
|
|
62
|
+
s.stop();
|
|
63
|
+
log.warn('Pulled content is empty. Skipping write to avoid overwriting local files.');
|
|
64
|
+
process.exitCode = 0;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
await writeFileWithDir(options.outFile, content);
|
|
68
|
+
if (vizData) {
|
|
31
69
|
const vizPath = deriveDiagramVizPathFromDBML(options.outFile);
|
|
32
70
|
const existingResult = await readDiagramVizFile(vizPath);
|
|
33
|
-
const state = fromServerFormat(
|
|
71
|
+
const state = fromServerFormat(vizData, existingResult.ok ? existingResult.state : undefined);
|
|
34
72
|
await writeDiagramVizFile(vizPath, state);
|
|
35
73
|
}
|
|
36
|
-
|
|
37
|
-
s.stop(`Saved ${label} to ${options.outFile}`);
|
|
74
|
+
s.stop(`Saved ${successLabel} to ${options.outFile}`);
|
|
38
75
|
}
|
|
39
76
|
else {
|
|
40
|
-
s.stop('Diagram fetched successfully.');
|
|
41
|
-
logConsole(
|
|
77
|
+
s.stop(isDocumentSource ? 'Document fetched successfully.' : 'Diagram fetched successfully.');
|
|
78
|
+
logConsole(content);
|
|
42
79
|
}
|
|
43
80
|
}
|
|
44
81
|
catch (error) {
|
|
45
|
-
s.stop('Failed to fetch diagram.');
|
|
82
|
+
s.stop(isDocumentSource ? 'Failed to fetch document.' : 'Failed to fetch diagram.');
|
|
46
83
|
displayError(toCliError(error), 'plain');
|
|
47
84
|
process.exitCode = 1;
|
|
48
85
|
}
|
|
@@ -4,7 +4,7 @@ import { getCredential } from '../config/credential-manager.js';
|
|
|
4
4
|
import { portalIntegration } from '../integrations/portal/portal.integration.js';
|
|
5
5
|
import { trackEvent } from '../services/telemetry.service.js';
|
|
6
6
|
import { deriveDiagramVizPathFromDBML, readDiagramVizFile } from '../services/viz/diagram-viz-file.service.js';
|
|
7
|
-
import {
|
|
7
|
+
import { toDiagramVizData } from '../services/viz/diagram-viz-converter.service.js';
|
|
8
8
|
import { preparePushDiagramData } from '../services/dbml/dbml.service.js';
|
|
9
9
|
import { DBDIAGRAM_CONFIGS } from '../config.js';
|
|
10
10
|
import { CliError } from '../errors/cli.error.js';
|
|
@@ -48,7 +48,7 @@ export async function pushAction(filepath, options, thisCommand) {
|
|
|
48
48
|
let vizData;
|
|
49
49
|
if (vizResult.ok) {
|
|
50
50
|
const { diagramViews } = preparedPushData;
|
|
51
|
-
vizData =
|
|
51
|
+
vizData = toDiagramVizData(vizResult.state, diagramViews);
|
|
52
52
|
}
|
|
53
53
|
const s = spinner();
|
|
54
54
|
s.start('Pushing diagram to dbdiagram...');
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { pullAction } from '../actions/pull.action.js';
|
|
2
2
|
import { pullPreAction } from '../hooks/pull.hook.js';
|
|
3
|
+
import { PULL_SOURCE_TYPE } from '../constants/pull.constant.js';
|
|
3
4
|
export function registerPullCommand(program) {
|
|
4
|
-
program
|
|
5
|
+
const cmd = program
|
|
5
6
|
.command('pull')
|
|
6
|
-
.description('Pull the DBML content of a diagram to your local machine')
|
|
7
|
+
.description('Pull the DBML content of a diagram or dbdocs document to your local machine')
|
|
7
8
|
.option('--diagram-id <id>', 'The diagram ID to pull from')
|
|
9
|
+
.option('--project <name>', 'The dbdocs document to pull from, as <workspace>/<project> or just <project>')
|
|
8
10
|
.option('--out-file, -o <filepath>', 'Save to a file (omit to use the configured entry file, or stdout)')
|
|
9
11
|
.hook('preAction', pullPreAction)
|
|
10
12
|
.action(pullAction);
|
|
13
|
+
cmd.on('option:diagram-id', (val) => {
|
|
14
|
+
cmd.setOptionValue('sourceFlag', { type: PULL_SOURCE_TYPE.DIAGRAM, value: val });
|
|
15
|
+
});
|
|
16
|
+
cmd.on('option:project', (val) => {
|
|
17
|
+
cmd.setOptionValue('sourceFlag', { type: PULL_SOURCE_TYPE.DOCUMENT, value: val });
|
|
18
|
+
});
|
|
11
19
|
}
|
|
@@ -16,9 +16,16 @@ export const WORKSPACE_NAME_MISSING = 'workspace_name_missing';
|
|
|
16
16
|
export const WORKSPACE_NAME_INVALID = 'workspace_name_invalid';
|
|
17
17
|
export const WORKSPACE_NOT_FOUND = 'workspace_not_found';
|
|
18
18
|
export const WORKSPACE_PERMISSION_DENIED = 'workspace_permission_denied';
|
|
19
|
+
export const WORKSPACE_PERMISSION_DENIED_CREATE_PROJECT = 'workspace_permission_denied_create_project';
|
|
20
|
+
export const WORKSPACE_PERMISSION_DENIED_LIST_PROJECT = 'workspace_permission_denied_list_project';
|
|
19
21
|
export const PROJECT_NAME_MISSING = 'project_name_missing';
|
|
20
22
|
export const PROJECT_NAME_INVALID = 'project_name_invalid';
|
|
21
23
|
export const VERSION_NAME_INVALID = 'version_name_invalid';
|
|
24
|
+
export const PROJECT_NOT_FOUND = 'project_not_found';
|
|
25
|
+
export const PROJECT_PERMISSION_DENIED = 'project_permission_denied';
|
|
26
|
+
export const PROJECT_PASSWORD_REQUIRED = 'project_password_required';
|
|
27
|
+
export const PROJECT_PERMISSION_DENIED_EDIT = 'project_permission_denied_edit';
|
|
28
|
+
export const LINK_USER_FAILED = 'link_user_failed';
|
|
22
29
|
export const TOKEN_NOT_FOUND = 'token_not_found';
|
|
23
30
|
export const TOKEN_LIMIT_EXCEEDED = 'token_limit_exceeded';
|
|
24
31
|
export const FILE_PATH_MISSING = 'file_path_missing';
|
|
@@ -56,14 +63,22 @@ export const CLI_ERROR_MESSAGES = {
|
|
|
56
63
|
+ 'or set workspaceUrlName in settings.json',
|
|
57
64
|
[WORKSPACE_NOT_FOUND]: 'Workspace not found.',
|
|
58
65
|
[WORKSPACE_PERMISSION_DENIED]: 'You do not have permission to access this workspace.',
|
|
66
|
+
[WORKSPACE_PERMISSION_DENIED_CREATE_PROJECT]: 'You do not have permission to create a project in this workspace.',
|
|
67
|
+
[WORKSPACE_PERMISSION_DENIED_LIST_PROJECT]: 'You do not have permission to list projects in this workspace.',
|
|
59
68
|
[PROJECT_NAME_MISSING]: 'Project name/url name is required. Use --project <workspace>/<project> '
|
|
60
69
|
+ 'or set projectUrlName in settings.json',
|
|
70
|
+
[PROJECT_NOT_FOUND]: 'Document not found.',
|
|
71
|
+
[PROJECT_PERMISSION_DENIED]: 'You do not have permission to access this document.',
|
|
72
|
+
[PROJECT_PASSWORD_REQUIRED]: 'This document is password-protected and cannot be accessed via the CLI.',
|
|
73
|
+
[PROJECT_PERMISSION_DENIED_EDIT]: 'You do not have edit permission on this document.',
|
|
61
74
|
[TOKEN_NOT_FOUND]: 'Token not found.',
|
|
62
75
|
[TOKEN_LIMIT_EXCEEDED]: 'Token limit reached. Delete an existing token before creating a new one.',
|
|
63
76
|
[FILE_PATH_MISSING]: 'Missing required argument: filepath. Provide it as an argument or set dbml.entry in settings.json.',
|
|
64
77
|
[SOURCE_MISSING]: 'One source must be specified: --from-file or --from-diagram',
|
|
65
78
|
[CONFIRMATION_REQUIRED]: 'Confirmation required. Pass --force (-f) to delete in a non-interactive environment.',
|
|
66
79
|
[INTERNAL_SERVER_ERROR]: 'An internal server error occurred. Please try again later.',
|
|
80
|
+
[LINK_USER_FAILED]: 'Failed to link your account with dbdocs. This is a portal-side issue, not an authentication '
|
|
81
|
+
+ 'problem; please try again or contact support.',
|
|
67
82
|
[NETWORK_ERROR]: 'Network error: could not reach the server. Check your connection and try again.',
|
|
68
83
|
};
|
|
69
84
|
export const PORTAL_TO_CLI_CODE = {
|
|
@@ -74,6 +89,13 @@ export const PORTAL_TO_CLI_CODE = {
|
|
|
74
89
|
diagram_limit_exceeded: DIAGRAM_LIMIT_EXCEEDED,
|
|
75
90
|
workspace_not_found: WORKSPACE_NOT_FOUND,
|
|
76
91
|
workspace_permission_denied_view: WORKSPACE_PERMISSION_DENIED,
|
|
92
|
+
project_not_found: PROJECT_NOT_FOUND,
|
|
93
|
+
project_permission_denied_view: PROJECT_PERMISSION_DENIED,
|
|
94
|
+
project_permission_denied_view_password_required: PROJECT_PASSWORD_REQUIRED,
|
|
95
|
+
project_permission_denied_edit: PROJECT_PERMISSION_DENIED_EDIT,
|
|
96
|
+
workspace_permission_denied_create_project: WORKSPACE_PERMISSION_DENIED_CREATE_PROJECT,
|
|
97
|
+
workspace_permission_denied_list_project: WORKSPACE_PERMISSION_DENIED_LIST_PROJECT,
|
|
98
|
+
link_user_failed: LINK_USER_FAILED,
|
|
77
99
|
token_not_found: TOKEN_NOT_FOUND,
|
|
78
100
|
token_limit_exceeded: TOKEN_LIMIT_EXCEEDED,
|
|
79
101
|
internal_server_error: INTERNAL_SERVER_ERROR,
|
|
@@ -6,3 +6,4 @@ export const PROJECT_URL_NAME_PATTERN = /^[A-Za-z0-9_\-@.\s]+$/;
|
|
|
6
6
|
export const WORKSPACE_URL_NAME_PATTERN = /^[A-Za-z0-9_\-@.\s]+$/;
|
|
7
7
|
export const URL_NAME_MAX_LENGTH = 255;
|
|
8
8
|
export const VERSION_NAME_MAX_LENGTH = 255;
|
|
9
|
+
export const DOCUMENT_SOURCE_CLIENT_TYPE = 'dbdiagram-cli';
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { getSettings } from '../config/settings-manager.js';
|
|
2
2
|
import { DOCUMENT_SOURCE_TYPE } from '../constants/document.constant.js';
|
|
3
|
+
import { parseProjectFlag } from '../utils/document.util.js';
|
|
4
|
+
export function normalizeBuildDocumentFlags(cmd) {
|
|
5
|
+
const projectFlag = cmd.getOptionValue('project');
|
|
6
|
+
if (projectFlag === undefined || projectFlag === null)
|
|
7
|
+
return;
|
|
8
|
+
const { workspaceUrlName, projectUrlOrName } = parseProjectFlag(projectFlag);
|
|
9
|
+
if (workspaceUrlName !== undefined) {
|
|
10
|
+
cmd.setOptionValue('workspaceUrlName', workspaceUrlName);
|
|
11
|
+
}
|
|
12
|
+
cmd.setOptionValue('projectUrlOrName', projectUrlOrName);
|
|
13
|
+
}
|
|
3
14
|
export function resolveBuildDocumentFlags(cmd, settings) {
|
|
4
15
|
if (cmd.getOptionValue('source') == null && settings.dbml?.entry) {
|
|
5
16
|
cmd.setOptionValue('source', { type: DOCUMENT_SOURCE_TYPE.FILE, value: settings.dbml.entry });
|
|
6
17
|
}
|
|
7
|
-
const projectFlag = cmd.getOptionValue('project');
|
|
8
|
-
if (projectFlag != null) {
|
|
9
|
-
const slashIndex = projectFlag.indexOf('/');
|
|
10
|
-
if (slashIndex >= 0) {
|
|
11
|
-
cmd.setOptionValue('workspaceUrlName', projectFlag.slice(0, slashIndex));
|
|
12
|
-
cmd.setOptionValue('projectUrlOrName', projectFlag.slice(slashIndex + 1));
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
cmd.setOptionValue('projectUrlOrName', projectFlag);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
18
|
if (cmd.getOptionValue('workspaceUrlName') == null && settings.document?.workspaceUrlName) {
|
|
19
19
|
cmd.setOptionValue('workspaceUrlName', settings.document.workspaceUrlName);
|
|
20
20
|
}
|
|
@@ -23,6 +23,7 @@ export function resolveBuildDocumentFlags(cmd, settings) {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
export function buildDocumentPreAction(thisCommand) {
|
|
26
|
+
normalizeBuildDocumentFlags(thisCommand);
|
|
26
27
|
const settings = getSettings();
|
|
27
28
|
if (!settings)
|
|
28
29
|
return;
|
package/dist/hooks/pull.hook.js
CHANGED
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
import { getSettings } from '../config/settings-manager.js';
|
|
2
|
+
import { PULL_SOURCE_TYPE } from '../constants/pull.constant.js';
|
|
3
|
+
import { parseProjectFlag } from '../utils/document.util.js';
|
|
4
|
+
export function normalizePullFlags(cmd) {
|
|
5
|
+
const sourceFlag = cmd.getOptionValue('sourceFlag');
|
|
6
|
+
if (sourceFlag === undefined || sourceFlag === null)
|
|
7
|
+
return;
|
|
8
|
+
const source = sourceFlag.type === PULL_SOURCE_TYPE.DOCUMENT
|
|
9
|
+
? { type: PULL_SOURCE_TYPE.DOCUMENT, ...parseProjectFlag(sourceFlag.value) }
|
|
10
|
+
: { type: PULL_SOURCE_TYPE.DIAGRAM, value: sourceFlag.value };
|
|
11
|
+
cmd.setOptionValue('source', source);
|
|
12
|
+
}
|
|
2
13
|
export function resolvePullFlags(cmd, settings) {
|
|
3
|
-
|
|
4
|
-
|
|
14
|
+
const source = cmd.getOptionValue('source');
|
|
15
|
+
if (source == null) {
|
|
16
|
+
if (settings.diagram?.id) {
|
|
17
|
+
cmd.setOptionValue('source', { type: PULL_SOURCE_TYPE.DIAGRAM, value: settings.diagram.id });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
else if (source.type === PULL_SOURCE_TYPE.DOCUMENT) {
|
|
21
|
+
if (source.workspaceUrlName == null && settings.document?.workspaceUrlName) {
|
|
22
|
+
cmd.setOptionValue('source', { ...source, workspaceUrlName: settings.document.workspaceUrlName });
|
|
23
|
+
}
|
|
5
24
|
}
|
|
6
25
|
if (cmd.getOptionValue('outFile') == null && settings.dbml?.entry) {
|
|
7
26
|
cmd.setOptionValue('outFile', settings.dbml.entry);
|
|
8
27
|
}
|
|
9
28
|
}
|
|
10
29
|
export function pullPreAction(thisCommand) {
|
|
30
|
+
normalizePullFlags(thisCommand);
|
|
11
31
|
const settings = getSettings();
|
|
12
32
|
if (!settings)
|
|
13
33
|
return;
|
|
@@ -14,6 +14,10 @@ async function createDocument(req) {
|
|
|
14
14
|
const { data } = await portalHttpClient.post(`${CLI_BASE_PATH}/projects`, req);
|
|
15
15
|
return data;
|
|
16
16
|
}
|
|
17
|
+
async function getDocument(workspaceUrlName, projectUrlName) {
|
|
18
|
+
const { data } = await portalHttpClient.get(`${CLI_BASE_PATH}/workspaces/${workspaceUrlName}/projects/${projectUrlName}`);
|
|
19
|
+
return data;
|
|
20
|
+
}
|
|
17
21
|
async function updateDiagram(req) {
|
|
18
22
|
const { name, dbml, vizData } = req;
|
|
19
23
|
const { data } = await portalHttpClient.put(`${CLI_BASE_PATH}/diagrams/${req.diagramId}`, { name, dbml, vizData });
|
|
@@ -58,6 +62,7 @@ export const portalIntegration = {
|
|
|
58
62
|
updateDiagram,
|
|
59
63
|
createDocument,
|
|
60
64
|
getDiagram,
|
|
65
|
+
getDocument,
|
|
61
66
|
deleteDiagram,
|
|
62
67
|
listDiagrams,
|
|
63
68
|
listWorkspaces,
|
|
@@ -48,7 +48,7 @@ const toDetailLevel = (value) => (Object.values(DetailLevel).includes(value)
|
|
|
48
48
|
const toRelationshipMode = (value) => (Object.values(RelationshipMode).includes(value)
|
|
49
49
|
? value
|
|
50
50
|
: DEFAULT_RELATIONSHIP_MODE);
|
|
51
|
-
export const
|
|
51
|
+
export const toDiagramVizData = (state, diagramViews) => {
|
|
52
52
|
const { defaultView, views } = state;
|
|
53
53
|
const viewEntitiesMap = new Map(diagramViews?.map((v) => [v.name, v.visibleEntities]));
|
|
54
54
|
return {
|
|
@@ -71,6 +71,23 @@ export const toServerFormat = (state, diagramViews) => {
|
|
|
71
71
|
})),
|
|
72
72
|
};
|
|
73
73
|
};
|
|
74
|
+
export const toDocumentVizData = (vizData) => {
|
|
75
|
+
return {
|
|
76
|
+
diagram: vizData.diagram,
|
|
77
|
+
referencePaths: vizData.referencePaths,
|
|
78
|
+
stickyNoteLayouts: vizData.stickyNoteLayouts,
|
|
79
|
+
tableGroups: vizData.tableGroups,
|
|
80
|
+
detailLevel: vizData.detailLevel,
|
|
81
|
+
views: vizData.views.map((view) => ({
|
|
82
|
+
name: view.name,
|
|
83
|
+
detailLevel: view.detailLevel,
|
|
84
|
+
tablePositions: view.tablePositions,
|
|
85
|
+
tableGroupCollapsedStates: view.tableGroupCollapsedStates,
|
|
86
|
+
stickyNoteLayouts: view.stickyNoteLayouts,
|
|
87
|
+
referencePaths: view.referencePaths,
|
|
88
|
+
})),
|
|
89
|
+
};
|
|
90
|
+
};
|
|
74
91
|
const viewFromServer = (view) => {
|
|
75
92
|
return {
|
|
76
93
|
detailLevel: toDetailLevel(view.detailLevel),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { PROJECT_NAME_INVALID, PROJECT_NAME_MISSING, WORKSPACE_NAME_INVALID, WORKSPACE_NAME_MISSING, } from '../constants/cli-error.constant.js';
|
|
1
2
|
import { PROJECT_URL_NAME_PATTERN, URL_NAME_MAX_LENGTH, VERSION_NAME_MAX_LENGTH, WORKSPACE_URL_NAME_PATTERN, } from '../constants/document.constant.js';
|
|
3
|
+
import { CliError } from '../errors/cli.error.js';
|
|
4
|
+
import { getCliErrorMessageFromCode } from './cli-error.util.js';
|
|
2
5
|
const INVALID_URL_NAME_MESSAGE = 'Invalid name. Must contain only letters, numbers, spaces, and _ - @ . characters.';
|
|
3
6
|
export function validateProjectUrlName(value) {
|
|
4
7
|
if (value.trim() === '') {
|
|
@@ -30,3 +33,41 @@ export function validateVersionName(value) {
|
|
|
30
33
|
}
|
|
31
34
|
return null;
|
|
32
35
|
}
|
|
36
|
+
function toWorkspaceNameFormatError(workspaceUrlName) {
|
|
37
|
+
const workspaceError = validateWorkspaceUrlName(workspaceUrlName);
|
|
38
|
+
return workspaceError ? new CliError(WORKSPACE_NAME_INVALID, workspaceError) : null;
|
|
39
|
+
}
|
|
40
|
+
function toProjectNameError(projectUrlOrName) {
|
|
41
|
+
if (projectUrlOrName.includes('/')) {
|
|
42
|
+
return new CliError(PROJECT_NAME_INVALID, 'Invalid --project value. Expected <workspace>/<project> or <project>.');
|
|
43
|
+
}
|
|
44
|
+
const projectError = validateProjectUrlName(projectUrlOrName);
|
|
45
|
+
return projectError ? new CliError(PROJECT_NAME_INVALID, projectError) : null;
|
|
46
|
+
}
|
|
47
|
+
export function validateAndResolveBuildDocumentProjectNames(workspaceUrlName, projectUrlOrName) {
|
|
48
|
+
if (!workspaceUrlName) {
|
|
49
|
+
return new CliError(WORKSPACE_NAME_MISSING, getCliErrorMessageFromCode(WORKSPACE_NAME_MISSING));
|
|
50
|
+
}
|
|
51
|
+
const workspaceFormatError = toWorkspaceNameFormatError(workspaceUrlName);
|
|
52
|
+
if (workspaceFormatError)
|
|
53
|
+
return workspaceFormatError;
|
|
54
|
+
if (!projectUrlOrName) {
|
|
55
|
+
return new CliError(PROJECT_NAME_MISSING, getCliErrorMessageFromCode(PROJECT_NAME_MISSING));
|
|
56
|
+
}
|
|
57
|
+
const projectError = toProjectNameError(projectUrlOrName);
|
|
58
|
+
if (projectError)
|
|
59
|
+
return projectError;
|
|
60
|
+
return { workspaceUrlName, projectUrlOrName };
|
|
61
|
+
}
|
|
62
|
+
export function validateAndResolvePullDocumentProjectNames(workspaceUrlName, projectUrlOrName) {
|
|
63
|
+
if (!workspaceUrlName) {
|
|
64
|
+
return new CliError(WORKSPACE_NAME_MISSING, getCliErrorMessageFromCode(WORKSPACE_NAME_MISSING));
|
|
65
|
+
}
|
|
66
|
+
const workspaceFormatError = toWorkspaceNameFormatError(workspaceUrlName);
|
|
67
|
+
if (workspaceFormatError)
|
|
68
|
+
return workspaceFormatError;
|
|
69
|
+
const projectError = toProjectNameError(projectUrlOrName);
|
|
70
|
+
if (projectError)
|
|
71
|
+
return projectError;
|
|
72
|
+
return { workspaceUrlName, projectUrlOrName };
|
|
73
|
+
}
|