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
561 changes: 478 additions & 83 deletions __tests__/utils/api.test.ts

Large diffs are not rendered by default.

68 changes: 34 additions & 34 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"url": "https://github.com/quantcdn/quant-cloud-cli/issues"
},
"dependencies": {
"@quantcdn/quant-client": "^4.3.0",
"@quantcdn/quant-client": "^4.12.0",
"axios": "^1.6.0",
"chalk": "^5.3.0",
"commander": "^11.1.0",
Expand Down
26 changes: 24 additions & 2 deletions src/commands/backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,18 @@ async function handleBackupList(options: BackupListOptions): Promise<void> {

try {
const backupType = (options.type || 'database') as 'database' | 'filesystem';
const response = await client.backupManagementApi.listBackups(orgId, appId, envId, backupType);
const response = await client.backupManagementApi.listBackups(
orgId,
appId,
envId,
backupType,
undefined, // order
undefined, // limit
undefined, // createdBefore
undefined, // createdAfter
undefined, // status
undefined, // nextToken
);
const backups = response.data?.backups || [];

spinner.succeed(`Found ${backups.length} backups`);
Expand Down Expand Up @@ -293,7 +304,18 @@ async function handleBackupDownload(backupId: string | undefined, options: Backu
try {
// First, get the list of backups
const backupType = (options.type || 'database') as 'database' | 'filesystem';
const listResponse = await client.backupManagementApi.listBackups(orgId, appId, envId, backupType);
const listResponse = await client.backupManagementApi.listBackups(
orgId,
appId,
envId,
backupType,
undefined, // order
undefined, // limit
undefined, // createdBefore
undefined, // createdAfter
undefined, // status
undefined, // nextToken
);
const backups = listResponse.data?.backups || [];

if (backups.length === 0) {
Expand Down
101 changes: 81 additions & 20 deletions src/commands/crawler.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { Command } from 'commander';
import chalk from 'chalk';
import inquirer from 'inquirer';
import inquirerAutocompletePrompt from 'inquirer-autocomplete-prompt';
import { createSpinner } from '../utils/spinner.js';
import { ApiClient } from '../utils/api.js';
import { Logger } from '../utils/logger.js';
import { getActivePlatformConfig } from '../utils/config.js';

// Register the autocomplete prompt
inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt);

const logger = new Logger('Crawler');

export function crawlerCommand(program: Command) {
Expand Down Expand Up @@ -69,18 +73,37 @@ async function handleCrawlerRun(crawlerId?: string, options?: CrawlerOptions) {
}

if (projects.length === 1) {
projectName = projects[0].name || projects[0].machine_name;
projectName = projects[0].machine_name;
logger.info(`Using project: ${chalk.cyan(projectName)}`);
} else {
const projectChoices = projects.map((p: any) => ({
name: `${p.name || p.machine_name} ${p.url ? chalk.gray(`(${p.url})`) : ''}`,
value: p.machine_name // Always use machine_name for API calls
}));

const { selectedProject } = await inquirer.prompt([
{
type: 'list',
type: 'autocomplete',
name: 'selectedProject',
message: 'Select a project:',
choices: projects.map((p: any) => ({
name: `${p.name || p.machine_name} ${p.url ? chalk.gray(`(${p.url})`) : ''}`,
value: p.name || p.machine_name
}))
message: 'Select a project (type to filter):',
source: async (answersSoFar: any, input: string) => {
if (!input) {
return projectChoices;
}

// Filter projects based on user input
const filtered = projectChoices.filter((choice: any) =>
choice.name.toLowerCase().includes(input.toLowerCase()) ||
choice.value.toLowerCase().includes(input.toLowerCase())
);

return filtered.length > 0 ? filtered : [
{ name: chalk.red(`No projects matching "${input}"`), value: null, disabled: true }
];
},
pageSize: 10,
searchText: 'Searching...',
emptyText: 'No projects found'
}
]);
projectName = selectedProject;
Expand Down Expand Up @@ -108,15 +131,34 @@ async function handleCrawlerRun(crawlerId?: string, options?: CrawlerOptions) {
crawlerId = crawlers[0].uuid || crawlers[0].machine_name;
logger.info(`Using crawler: ${chalk.cyan(crawlerId)}`);
} else {
const crawlerChoices = crawlers.map((c: any) => ({
name: `${c.name || c.machine_name} ${c.description ? chalk.gray(`(${c.description})`) : ''}`,
value: c.uuid || c.machine_name
}));

const { selectedCrawler } = await inquirer.prompt([
{
type: 'list',
type: 'autocomplete',
name: 'selectedCrawler',
message: 'Select a crawler:',
choices: crawlers.map((c: any) => ({
name: `${c.name || c.machine_name} ${c.description ? chalk.gray(`(${c.description})`) : ''}`,
value: c.uuid || c.machine_name
}))
message: 'Select a crawler (type to filter):',
source: async (answersSoFar: any, input: string) => {
if (!input) {
return crawlerChoices;
}

// Filter crawlers based on user input
const filtered = crawlerChoices.filter((choice: any) =>
choice.name.toLowerCase().includes(input.toLowerCase()) ||
choice.value.toLowerCase().includes(input.toLowerCase())
);

return filtered.length > 0 ? filtered : [
{ name: chalk.red(`No crawlers matching "${input}"`), value: null, disabled: true }
];
},
pageSize: 10,
searchText: 'Searching...',
emptyText: 'No crawlers found'
}
]);
crawlerId = selectedCrawler;
Expand Down Expand Up @@ -181,18 +223,37 @@ async function handleCrawlerList(options?: CrawlerOptions) {
}

if (projects.length === 1) {
projectName = projects[0].name || projects[0].machine_name;
projectName = projects[0].machine_name;
logger.info(`Using project: ${chalk.cyan(projectName)}`);
} else {
const projectChoices = projects.map((p: any) => ({
name: `${p.name || p.machine_name} ${p.url ? chalk.gray(`(${p.url})`) : ''}`,
value: p.machine_name // Always use machine_name for API calls
}));

const { selectedProject } = await inquirer.prompt([
{
type: 'list',
type: 'autocomplete',
name: 'selectedProject',
message: 'Select a project:',
choices: projects.map((p: any) => ({
name: `${p.name || p.machine_name} ${p.url ? chalk.gray(`(${p.url})`) : ''}`,
value: p.name || p.machine_name
}))
message: 'Select a project (type to filter):',
source: async (answersSoFar: any, input: string) => {
if (!input) {
return projectChoices;
}

// Filter projects based on user input
const filtered = projectChoices.filter((choice: any) =>
choice.name.toLowerCase().includes(input.toLowerCase()) ||
choice.value.toLowerCase().includes(input.toLowerCase())
);

return filtered.length > 0 ? filtered : [
{ name: chalk.red(`No projects matching "${input}"`), value: null, disabled: true }
];
},
pageSize: 10,
searchText: 'Searching...',
emptyText: 'No projects found'
}
]);
projectName = selectedProject;
Expand Down
Loading
Loading