@lightdash/cli 0.2395.0 → 0.2396.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/dist/.tsbuildinfo +1 -1
- package/dist/handlers/sql.d.ts +9 -0
- package/dist/handlers/sql.d.ts.map +1 -0
- package/dist/handlers/sql.js +115 -0
- package/dist/index.js +10 -0
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/handlers/sql.ts"],"names":[],"mappings":"AAYA,KAAK,iBAAiB,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AA0FF,eAAO,MAAM,UAAU,QACd,MAAM,WACF,iBAAiB,KAC3B,OAAO,CAAC,IAAI,CAiEd,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sqlHandler = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@lightdash/common");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const config_1 = require("../config");
|
|
8
|
+
const globalState_1 = tslib_1.__importDefault(require("../globalState"));
|
|
9
|
+
const styles = tslib_1.__importStar(require("../styles"));
|
|
10
|
+
const apiClient_1 = require("./dbt/apiClient");
|
|
11
|
+
const DEFAULT_PAGE_SIZE = 500;
|
|
12
|
+
const POLL_INTERVAL_MS = 500;
|
|
13
|
+
/**
|
|
14
|
+
* Convert ResultRow array to CSV string
|
|
15
|
+
*/
|
|
16
|
+
function resultsToCsv(columns, rows) {
|
|
17
|
+
// Escape CSV value: wrap in quotes if contains comma, quote, or newline
|
|
18
|
+
const escapeValue = (value) => {
|
|
19
|
+
if (value === null || value === undefined) {
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
22
|
+
const str = String(value);
|
|
23
|
+
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
|
|
24
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
25
|
+
}
|
|
26
|
+
return str;
|
|
27
|
+
};
|
|
28
|
+
const header = columns.map(escapeValue).join(',');
|
|
29
|
+
const dataRows = rows.map((row) => columns.map((col) => escapeValue(row[col]?.value?.raw)).join(','));
|
|
30
|
+
return [header, ...dataRows].join('\n');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Fetch query results once
|
|
34
|
+
*/
|
|
35
|
+
async function fetchQueryResults(projectUuid, queryUuid, pageSize) {
|
|
36
|
+
const url = `/api/v2/projects/${projectUuid}/query/${queryUuid}?pageSize=${pageSize}`;
|
|
37
|
+
return (0, apiClient_1.lightdashApi)({
|
|
38
|
+
method: 'GET',
|
|
39
|
+
url,
|
|
40
|
+
body: undefined,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Sleep for a given duration
|
|
45
|
+
*/
|
|
46
|
+
function sleep(ms) {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
setTimeout(resolve, ms);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Poll for query results until status is READY or ERROR
|
|
53
|
+
*/
|
|
54
|
+
async function pollQueryResults(projectUuid, queryUuid, pageSize) {
|
|
55
|
+
const poll = async () => {
|
|
56
|
+
const result = await fetchQueryResults(projectUuid, queryUuid, pageSize);
|
|
57
|
+
globalState_1.default.debug(`> Query status: ${result.status}`);
|
|
58
|
+
if (result.status === common_1.QueryHistoryStatus.READY) {
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
if (result.status === common_1.QueryHistoryStatus.ERROR) {
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
if (result.status === common_1.QueryHistoryStatus.CANCELLED) {
|
|
65
|
+
throw new Error('Query was cancelled');
|
|
66
|
+
}
|
|
67
|
+
// Still pending, wait and poll again
|
|
68
|
+
await sleep(POLL_INTERVAL_MS);
|
|
69
|
+
return poll();
|
|
70
|
+
};
|
|
71
|
+
return poll();
|
|
72
|
+
}
|
|
73
|
+
const sqlHandler = async (sql, options) => {
|
|
74
|
+
globalState_1.default.setVerbose(options.verbose ?? false);
|
|
75
|
+
const config = await (0, config_1.getConfig)();
|
|
76
|
+
const projectUuid = config.context?.project;
|
|
77
|
+
if (!projectUuid) {
|
|
78
|
+
throw new Error(`No project selected. Run 'lightdash config set-project' first.`);
|
|
79
|
+
}
|
|
80
|
+
globalState_1.default.debug(`> Running SQL query against project: ${projectUuid}`);
|
|
81
|
+
globalState_1.default.debug(`> SQL: ${sql}`);
|
|
82
|
+
const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
83
|
+
// Submit the query
|
|
84
|
+
const spinner = globalState_1.default.startSpinner('Submitting SQL query...');
|
|
85
|
+
const submitResult = await (0, apiClient_1.lightdashApi)({
|
|
86
|
+
method: 'POST',
|
|
87
|
+
url: `/api/v2/projects/${projectUuid}/query/sql`,
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
sql,
|
|
90
|
+
limit: options.limit,
|
|
91
|
+
context: 'cli',
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
globalState_1.default.debug(`> Query UUID: ${submitResult.queryUuid}`);
|
|
95
|
+
spinner.text = 'Waiting for query results...';
|
|
96
|
+
// Poll for results
|
|
97
|
+
const result = await pollQueryResults(projectUuid, submitResult.queryUuid, pageSize);
|
|
98
|
+
if (result.status === common_1.QueryHistoryStatus.ERROR) {
|
|
99
|
+
spinner.fail('Query failed');
|
|
100
|
+
throw new Error(result.error ?? 'Query execution failed');
|
|
101
|
+
}
|
|
102
|
+
if (result.status !== common_1.QueryHistoryStatus.READY) {
|
|
103
|
+
spinner.fail('Unexpected query status');
|
|
104
|
+
throw new Error(`Unexpected query status: ${result.status}`);
|
|
105
|
+
}
|
|
106
|
+
// Extract column names from the columns metadata
|
|
107
|
+
const columns = Object.keys(result.columns);
|
|
108
|
+
const rowCount = result.rows.length;
|
|
109
|
+
spinner.text = `Writing ${rowCount} rows to ${options.output}...`;
|
|
110
|
+
// Convert to CSV and write to file
|
|
111
|
+
const csv = resultsToCsv(columns, result.rows);
|
|
112
|
+
await fs_1.promises.writeFile(options.output, csv, 'utf8');
|
|
113
|
+
spinner.succeed(`${styles.success('Success!')} Wrote ${rowCount} rows to ${options.output}`);
|
|
114
|
+
};
|
|
115
|
+
exports.sqlHandler = sqlHandler;
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ const login_1 = require("./handlers/login");
|
|
|
21
21
|
const preview_1 = require("./handlers/preview");
|
|
22
22
|
const renameHandler_1 = require("./handlers/renameHandler");
|
|
23
23
|
const setProject_1 = require("./handlers/setProject");
|
|
24
|
+
const sql_1 = require("./handlers/sql");
|
|
24
25
|
const validate_1 = require("./handlers/validate");
|
|
25
26
|
const styles = tslib_1.__importStar(require("./styles"));
|
|
26
27
|
// Trigger CLI tests
|
|
@@ -423,6 +424,15 @@ ${styles.bold('Examples:')}
|
|
|
423
424
|
.option('--verbose', 'Show detailed output', false)
|
|
424
425
|
.option('-f, --format <format>', 'Output format: cli (default) or json (SARIF format)', 'cli')
|
|
425
426
|
.action(lint_1.lintHandler);
|
|
427
|
+
commander_1.program
|
|
428
|
+
.command('sql')
|
|
429
|
+
.description('Run raw SQL query against the warehouse using project credentials')
|
|
430
|
+
.argument('<query>', 'SQL query to execute')
|
|
431
|
+
.requiredOption('-o, --output <file>', 'Output file path for CSV results')
|
|
432
|
+
.option('--limit <number>', 'Maximum rows to return from query', parseIntArgument)
|
|
433
|
+
.option('--page-size <number>', 'Number of rows per page (default: 500, max: 5000)', parseIntArgument)
|
|
434
|
+
.option('--verbose', 'Show detailed output', false)
|
|
435
|
+
.action(sql_1.sqlHandler);
|
|
426
436
|
const errorHandler = (err) => {
|
|
427
437
|
// Use error message with fallback for safety
|
|
428
438
|
const errorMessage = (0, common_1.getErrorMessage)(err) || 'An unexpected error occurred';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightdash/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2396.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"unique-names-generator": "^4.7.1",
|
|
39
39
|
"uuid": "^11.0.3",
|
|
40
40
|
"yaml": "^2.7.0",
|
|
41
|
-
"@lightdash/common": "0.
|
|
42
|
-
"@lightdash/warehouses": "0.
|
|
41
|
+
"@lightdash/common": "0.2396.0",
|
|
42
|
+
"@lightdash/warehouses": "0.2396.0"
|
|
43
43
|
},
|
|
44
44
|
"description": "Lightdash CLI tool",
|
|
45
45
|
"devDependencies": {
|