Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/atxp/src/commands/paas/analytics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
analyticsQueryCommand,
analyticsEventsCommand,
analyticsStatsCommand,
analyticsListCommand,
analyticsSchemaCommand,
} from './analytics.js';

describe('Analytics Commands', () => {
Expand Down Expand Up @@ -147,4 +149,32 @@ describe('Analytics Commands', () => {
expect(console.error).toHaveBeenCalled();
});
});

describe('analyticsListCommand', () => {
it('should list all analytics datasets', async () => {
vi.mocked(callTool).mockResolvedValue('{"datasets": ["events", "page_views"]}');

await analyticsListCommand();

expect(callTool).toHaveBeenCalledWith('paas.mcp.atxp.ai', 'list_analytics_datasets', {});
expect(console.log).toHaveBeenCalledWith('{"datasets": ["events", "page_views"]}');
});
});

describe('analyticsSchemaCommand', () => {
it('should get schema for a dataset', async () => {
vi.mocked(callTool).mockResolvedValue('{"columns": ["timestamp", "event_name", "user_id"]}');

await analyticsSchemaCommand({ dataset: 'events' });

expect(callTool).toHaveBeenCalledWith('paas.mcp.atxp.ai', 'get_dataset_schema', {
dataset: 'events',
});
});

it('should exit with error when dataset is missing', async () => {
await expect(analyticsSchemaCommand({})).rejects.toThrow('process.exit called');
expect(console.error).toHaveBeenCalled();
});
});
});
22 changes: 22 additions & 0 deletions packages/atxp/src/commands/paas/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,25 @@ export async function analyticsStatsCommand(options: AnalyticsStatsOptions): Pro
const result = await callTool(SERVER, 'get_analytics_stats', args);
console.log(result);
}

export async function analyticsListCommand(): Promise<void> {
const result = await callTool(SERVER, 'list_analytics_datasets', {});
console.log(result);
}

interface AnalyticsSchemaOptions {
dataset?: string;
}

export async function analyticsSchemaCommand(options: AnalyticsSchemaOptions): Promise<void> {
if (!options.dataset) {
console.error(chalk.red('Error: Dataset name is required'));
console.log(`Usage: ${chalk.cyan('npx atxp paas analytics schema <dataset>')}`);
console.log();
console.log('Run ' + chalk.cyan('npx atxp paas analytics list') + ' to see available datasets.');
process.exit(1);
}

const result = await callTool(SERVER, 'get_dataset_schema', { dataset: options.dataset });
console.log(result);
}
18 changes: 16 additions & 2 deletions packages/atxp/src/commands/paas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import {
analyticsQueryCommand,
analyticsEventsCommand,
analyticsStatsCommand,
analyticsListCommand,
analyticsSchemaCommand,
} from './analytics.js';
import {
secretsSetCommand,
Expand Down Expand Up @@ -112,6 +114,8 @@ function showPaasHelp(): void {
console.log();

console.log(chalk.bold('Analytics Commands:'));
console.log(' ' + chalk.cyan('paas analytics list') + ' List analytics datasets');
console.log(' ' + chalk.cyan('paas analytics schema') + ' ' + chalk.yellow('<dataset>') + ' Show dataset columns');
console.log(' ' + chalk.cyan('paas analytics query') + ' Query analytics data');
console.log(' ' + chalk.cyan('paas analytics events') + ' List analytics events');
console.log(' ' + chalk.cyan('paas analytics stats') + ' Get analytics statistics');
Expand Down Expand Up @@ -457,10 +461,20 @@ async function handleDnsCommand(

async function handleAnalyticsCommand(
subCommand: string,
_args: string[],
args: string[],
options: PaasOptions
): Promise<void> {
switch (subCommand) {
case 'list':
await analyticsListCommand();
break;

case 'schema': {
const dataset = args[0];
await analyticsSchemaCommand({ dataset });
break;
}

case 'query':
await analyticsQueryCommand({
sql: options.sql,
Expand All @@ -485,7 +499,7 @@ async function handleAnalyticsCommand(

default:
console.error(chalk.red(`Unknown analytics command: ${subCommand}`));
console.log('Available commands: query, events, stats');
console.log('Available commands: list, schema, query, events, stats');
process.exit(1);
}
}
Expand Down
29 changes: 28 additions & 1 deletion packages/atxp/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,16 +257,43 @@ describe('ATXP CLI', () => {
});

it('should identify analytics subcommands', () => {
const analyticsCommands = ['query', 'events', 'stats'];
const analyticsCommands = ['list', 'schema', 'query', 'events', 'stats'];

const isAnalyticsCommand = (subCommand: string) => {
return analyticsCommands.includes(subCommand);
};

expect(isAnalyticsCommand('list')).toBe(true);
expect(isAnalyticsCommand('schema')).toBe(true);
expect(isAnalyticsCommand('query')).toBe(true);
expect(isAnalyticsCommand('events')).toBe(true);
expect(isAnalyticsCommand('stats')).toBe(true);
expect(isAnalyticsCommand('deploy')).toBe(false);
});

it('should parse analytics schema dataset argument', () => {
const parseAnalyticsSchemaArgs = (argv: string[]) => {
// npx atxp paas analytics schema <dataset>
if (argv[2] === 'paas' && argv[3] === 'analytics' && argv[4] === 'schema') {
return argv[5];
}
return undefined;
};

expect(parseAnalyticsSchemaArgs(['node', 'script', 'paas', 'analytics', 'schema', 'events'])).toBe('events');
expect(parseAnalyticsSchemaArgs(['node', 'script', 'paas', 'analytics', 'schema', 'page_views'])).toBe('page_views');
expect(parseAnalyticsSchemaArgs(['node', 'script', 'paas', 'analytics', 'schema'])).toBe(undefined);
expect(parseAnalyticsSchemaArgs(['node', 'script', 'paas', 'analytics', 'list'])).toBe(undefined);
});

it('should parse analytics list command', () => {
const isAnalyticsListCommand = (argv: string[]) => {
return argv[2] === 'paas' && argv[3] === 'analytics' && argv[4] === 'list';
};

expect(isAnalyticsListCommand(['node', 'script', 'paas', 'analytics', 'list'])).toBe(true);
expect(isAnalyticsListCommand(['node', 'script', 'paas', 'analytics', 'query'])).toBe(false);
expect(isAnalyticsListCommand(['node', 'script', 'paas', 'db', 'list'])).toBe(false);
});
});
});
Loading