query.ts•4.69 kB
/**
* Query command
*/
import chalk from 'chalk';
import * as fs from 'fs/promises';
import { WhatapClient } from '../../core/client/WhatapClient';
import { MxqlExecutor } from '../../core/executor/MxqlExecutor';
import { requireAuth, parseTime, parseTimeRange } from '../utils/session';
import { formatOutput, formatQuerySummary, printSuccess, printError, printInfo, printWarning } from '../utils/formatters';
interface QueryOptions {
file?: string;
start?: string;
end?: string;
range?: string;
limit?: string;
output?: 'table' | 'json' | 'csv';
header?: boolean;
}
/**
* Query command handler
*/
export async function queryCommand(
pcodeStr: string,
mxql: string | undefined,
options: QueryOptions
): Promise<void> {
try {
// Parse project code
const pcode = parseInt(pcodeStr, 10);
if (isNaN(pcode)) {
printError(`Invalid project code: ${pcodeStr}`);
process.exit(1);
}
// Get MXQL query
let queryStr = mxql;
if (options.file) {
// Read from file
printInfo(`Reading query from file: ${options.file}`);
try {
queryStr = await fs.readFile(options.file, 'utf-8');
queryStr = queryStr.trim();
} catch (error: any) {
printError(`Failed to read file: ${options.file}`, error);
process.exit(1);
}
}
if (!queryStr) {
printError('MXQL query is required. Provide it as argument or use -f to read from file');
console.log('');
console.log(chalk.gray('Examples:'));
console.log(chalk.white(' whatap-mxql query 27506 "CATEGORY app_counter"'));
console.log(chalk.white(' whatap-mxql query 27506 -f query.mxql'));
process.exit(1);
}
// Parse time range
let startTime: number;
let endTime: number;
if (options.start && options.end) {
// Use explicit start/end
try {
startTime = parseTime(options.start);
endTime = parseTime(options.end);
} catch (error: any) {
printError('Invalid time format', error);
console.log('');
console.log(chalk.gray('Valid formats:'));
console.log(chalk.white(' • Timestamp: 1699776000000'));
console.log(chalk.white(' • ISO string: 2025-01-01T00:00:00Z'));
process.exit(1);
}
} else {
// Use range preset
const range = options.range || '1h';
try {
const timeRange = parseTimeRange(range);
startTime = timeRange.start;
endTime = timeRange.end;
} catch (error: any) {
printError('Invalid time range', error);
process.exit(1);
}
}
// Parse limit
const limit = parseInt(options.limit || '100', 10);
if (isNaN(limit) || limit <= 0) {
printError(`Invalid limit: ${options.limit}`);
process.exit(1);
}
// Execute query
printInfo('Executing query...');
console.log('');
// Require authentication
const authManager = await requireAuth();
// Create client and executor
const client = new WhatapClient(authManager);
const executor = new MxqlExecutor(client);
// Execute
const result = await executor.execute(
pcode,
queryStr,
{ start: startTime, end: endTime },
limit
);
console.log('');
printSuccess('Query executed successfully');
console.log('');
// Show summary
console.log(formatQuerySummary(result));
console.log('');
// Show data
if (result.rowCount === 0) {
printWarning('No data returned');
console.log('');
console.log(chalk.gray('Possible reasons:'));
console.log(chalk.gray(' • No data in the specified time range'));
console.log(chalk.gray(' • Project has no monitoring data'));
console.log(chalk.gray(' • Query returned empty result'));
console.log('');
return;
}
const format = options.output || 'table';
const noHeader = options.header === false;
console.log(formatOutput(result.data, format, { noHeader }));
console.log('');
// Show data info
if (result.data.length > 0 && format === 'table') {
const firstRow = result.data[0];
if (typeof firstRow === 'object' && firstRow !== null) {
const columns = Object.keys(firstRow);
console.log(chalk.gray(`Columns (${columns.length}): ${columns.join(', ')}`));
console.log('');
}
}
} catch (error: any) {
console.log('');
printError('Query execution failed', error);
if (error.message.includes('syntax') || error.message.includes('Invalid')) {
console.log('');
console.log(chalk.yellow('Hint: Check your MXQL syntax'));
}
process.exit(1);
}
}