import { Command } from 'commander'; import { SetIndexGroupsCommand } from './setIndexGroupsCommand'; import { findEntityById, saveEntity, searchEntityByName, setEntityColumnsKeys, setEntityColumnsPosition, setEntityColumnsSourceId, setEntityColumnsTrackForChanges, setEntityColumnsVisibility, } from '../../helpers/entityHelper'; import inquirer from 'inquirer'; import { IEntitySearchResult } from '../../helpers/@types/IEntitySearchResult'; import chalk from 'chalk'; import { getApiResponseFilterFields } from '../../helpers/responseFilterHelper'; import { findTaskAssociation, saveTaskAssociations, } from '../../helpers/taskAssociationHelper'; import { showTaskFiles } from '../tasks/filesCommand'; import { composeProfile } from '../compose/composeCommand'; export function SetKeysCommand(): Command { const cmd = new Command('set-keys'); cmd .alias('sk') .description('Set KEYS for the related table of the report.') .option('-i, --id ', 'Search the task by id') .option('-n, --name ', 'Search the task by name') .action(async ({ id, name }: { id?: string; name?: string }) => { await setEntityColumnsKeys(id, name, 'table'); }); return cmd; } export function SetSourceIdCommand(): Command { const cmd = new Command('set-source-id'); cmd .alias('ss') .description('Set SOURCE_ID for the related table of the report.') .option('-i, --id ', 'Search the task by id') .option('-n, --name ', 'Search the task by name') .action(async ({ id, name }: { id?: string; name?: string }) => { await setEntityColumnsSourceId(id, name, 'table'); }); return cmd; } export function TrackChangesCommand(): Command { const cmd = new Command('track-changes'); cmd .alias('tc') .description('Track changes for table columns.') .option('-i, --id ', 'Search the task by id') .option('-n, --name ', 'Search the task by name') .action(async ({ id, name }: { id?: string; name?: string }) => { await setEntityColumnsTrackForChanges(id, name, 'table'); }); return cmd; } export function SetVisibilityCommand(): Command { const cmd = new Command('set-visibility'); cmd .alias('sv') .description("Set Visibility of columns in a Task's Table.") .option('-i, --id ', 'Search the task by id') .option('-n, --name ', 'Search the task by name') .action(async ({ id, name }: { id?: string; name?: string }) => { await setEntityColumnsVisibility(id, name, 'table'); }); return cmd; } export function SetPositionCommand(): Command { const cmd = new Command('set-position'); cmd .alias('sp') .description('Set Position of columns in a Table.') .option('-i, --id ', 'Search the table by id') .option('-n, --name ', 'Search the table by name') .action(async ({ id, name }: { id?: string; name?: string }) => { await setEntityColumnsPosition(id, name, 'table'); }); return cmd; } export function GenerateColumnsCommand(entityType: 'task') { const cmd = new Command('generate'); cmd .alias('g') .description(`Generate columns for the related table of the ${entityType}.`) .option('-n, --name ', `Search the ${entityType} by name`) .action(async ({ name }: { name?: string }) => { var taskName = name; var searchedEntity: IEntitySearchResult | null = null; var searchedTable: IEntitySearchResult | null = null; do { if (!taskName) { taskName = await inquirer .prompt<{ val: string }>({ name: 'val', type: 'input', message: `Please provide the ${entityType} name:`, }) .then((answers) => answers.val); if (!taskName) continue; } searchedEntity = await searchEntityByName(taskName, entityType); } while (!searchedEntity); var selectedFields: { filterType: string; filterKey: string; fields: string[]; } | null = null; switch (entityType) { case 'task': var taskAssociation = findTaskAssociation(searchedEntity.entityId); var pullerId = taskAssociation?.puller?.id; var tableId = taskAssociation?.table?.id; if (!pullerId) { console.error( `${chalk.red('✗')} No puller found for ${entityType} ${taskName}`, ); return; } if (!tableId) { console.error( `${chalk.red('✗')} No table found for ${entityType} ${taskName}`, ); return; } searchedTable = await findEntityById(tableId, 'table', true); if (!searchedTable) { console.error( `${chalk.red('✗')} No table found for ${entityType} ${taskName}`, ); return; } var searchedPuller = await findEntityById(pullerId, 'puller', false); if (!searchedPuller) { console.error( `${chalk.red('✗')} No puller found for ${entityType} ${taskName}`, ); return; } var steps = searchedPuller?.content.pullSteps || []; var step = steps.find((s: any) => s.key === 'PULL_DATA'); while (!step) { var stepKey = await inquirer .prompt<{ val: string }>({ name: 'val', type: 'list', message: `Please provide the step key (default PULL_DATA):`, choices: steps.map((s: any) => { return { name: `${s.key} (${s.displayType})`, value: s.key, checked: s.key === 'PULL_DATA', }; }), }) .then((answers) => answers.val); step = steps.find((s: any) => s.key === stepKey); if (!step) continue; } switch (step.displayType) { case 'API': selectedFields = await getApiResponseFilterFields( step?.data?.responseFilters || {}, ); if (!selectedFields?.fields.length) { console.error( chalk.red( '✗ Failed to extract fields from response filters.', ), ); return; } // console.log(chalk.green("✓ Extracted fields:"), selectedFields); break; case 'SQL': // TODO: implement this default: console.error( `${chalk.red('✗')} ${step.displayType} not supported, you need to add columns manually using ${chalk.yellow('hexasync task add-columns --name ')}`, ); return; } if (selectedFields.filterKey) { taskAssociation.puller = { ...taskAssociation.puller, ...{ resultKey: selectedFields.filterKey, }, }; saveTaskAssociations(searchedEntity.entityId, taskAssociation); console.log( chalk.green('✓ Updated task association with puller result key.'), ); } break; default: console.error(`${chalk.red('✗')} ${entityType} not supported`); return; } var tableColumns = selectedFields?.fields?.reduce( (acc: Record, f: string) => { if (!searchedTable!.content.columns[f]) { acc[f] = { dataType: 'String', dbType: 'varchar(65535)', visibility: 'Hidden', }; } return acc; }, {}, ) || {}; searchedTable.content = { ...searchedTable.content, columns: { ...searchedTable.content.columns, ...tableColumns, }, }; saveEntity(searchedTable.content, 'table', searchedTable.filePath); await setEntityColumnsSourceId(searchedTable.entityId, '', 'table'); await setEntityColumnsKeys(searchedTable.entityId, '', 'table'); await setEntityColumnsTrackForChanges( searchedTable.entityId, '', 'table', ); await setEntityColumnsVisibility(searchedTable.entityId, '', 'table'); await setEntityColumnsPosition(searchedTable.entityId, '', 'table'); await composeProfile(); await showTaskFiles(searchedEntity.entityId, ''); }); return cmd; } export function AddColumnsCommand(entityType: 'task') { const cmd = new Command('add'); cmd .alias('a') .description(`Add columns to a ${entityType}'s Table.`) .option('-i, --id ', `Search the ${entityType} by id`) .option('-n, --name ', `Search the ${entityType} by name`) .action(async ({ name }: { name?: string }) => { var taskName = name; var searchedEntity: IEntitySearchResult | null = null; var searchedTable: IEntitySearchResult | null = null; do { if (!taskName) { taskName = await inquirer .prompt<{ val: string }>({ name: 'val', type: 'input', message: `Please provide the task name:`, }) .then((answers) => answers.val); if (!taskName) continue; } searchedEntity = await searchEntityByName(taskName, 'task'); } while (!searchedEntity); switch (entityType) { case 'task': var taskAssociation = findTaskAssociation(searchedEntity.entityId); var tableId = taskAssociation?.table?.id; if (!tableId) { console.error( `${chalk.red('✗')} No table found for ${entityType} ${taskName}`, ); return; } searchedTable = await findEntityById(tableId, 'table', true); if (!searchedTable) { console.error( `${chalk.red('✗')} No table found for ${entityType} ${taskName}`, ); return; } var tableColumns = await inquirer .prompt<{ val: string }>({ name: 'val', type: 'input', message: `Please provide the column name (comma separated):`, }) .then((answers) => answers.val); if (!tableColumns) { console.error(`${chalk.red('✗')} No column name provided`); return; } var columns = tableColumns .split(',') .reduce((acc: Record, f: string) => { if (!searchedTable!.content.columns[f]) { acc[f] = { dataType: 'String', dbType: 'varchar(65535)', visibility: 'Hidden', }; } return acc; }, {}) || {}; searchedTable.content = { ...searchedTable.content, columns: { ...searchedTable.content.columns, ...columns, }, }; saveEntity(searchedTable.content, 'table', searchedTable.filePath); await setEntityColumnsSourceId(searchedTable.entityId, '', 'table'); await setEntityColumnsKeys(searchedTable.entityId, '', 'table'); await setEntityColumnsTrackForChanges( searchedTable.entityId, '', 'table', ); await setEntityColumnsVisibility(searchedTable.entityId, '', 'table'); await setEntityColumnsPosition(searchedTable.entityId, '', 'table'); await composeProfile(); await showTaskFiles(searchedEntity.entityId, ''); } }); return cmd; } export function RemoveColumnsCommand(entityType: 'task') { const cmd = new Command('remove'); cmd .alias('rm') .description(`Remove columns from a ${entityType}'s Table.`) .option('-i, --id ', `Search the ${entityType} by id`) .option('-n, --name ', `Search the ${entityType} by name`) .action(async ({ name }: { name?: string }) => { var taskName = name; var searchedEntity: IEntitySearchResult | null = null; var searchedTable: IEntitySearchResult | null = null; do { if (!taskName) { taskName = await inquirer .prompt<{ val: string }>({ name: 'val', type: 'input', message: `Please provide the task name:`, }) .then((answers) => answers.val); if (!taskName) continue; } searchedEntity = await searchEntityByName(taskName, 'task'); } while (!searchedEntity); switch (entityType) { case 'task': var taskAssociation = findTaskAssociation(searchedEntity.entityId); var tableId = taskAssociation?.table?.id; if (!tableId) { console.error( `${chalk.red('✗')} No table found for ${entityType} ${taskName}`, ); return; } searchedTable = await findEntityById(tableId, 'table', true); if (!searchedTable) { console.error( `${chalk.red('✗')} No table found for ${entityType} ${taskName}`, ); return; } var tableColumns = await inquirer .prompt<{ val: string[] }>({ name: 'val', type: 'checkbox', choices: Object.keys(searchedTable!.content.columns).map( (f: string) => { return { name: f, value: f, checked: false, }; }, ), message: `Please select columns to remove:`, }) .then((answers) => answers.val); if (!tableColumns || tableColumns.length === 0) { console.error(`${chalk.red('✗')} No columns selected for removal`); return; } var updatedColumns = Object.keys( searchedTable!.content.columns, ).reduce((acc: Record, f: string) => { if (!tableColumns.includes(f)) { acc[f] = searchedTable!.content.columns[f]; } return acc; }, {}); searchedTable.content = { ...searchedTable.content, columns: updatedColumns, }; saveEntity(searchedTable.content, 'table', searchedTable.filePath); await setEntityColumnsSourceId(searchedTable.entityId, '', 'table'); await setEntityColumnsKeys(searchedTable.entityId, '', 'table'); await setEntityColumnsTrackForChanges( searchedTable.entityId, '', 'table', ); await setEntityColumnsVisibility(searchedTable.entityId, '', 'table'); await setEntityColumnsPosition(searchedTable.entityId, '', 'table'); await composeProfile(); await showTaskFiles(searchedEntity.entityId, ''); } }); return cmd; } export function TableCommand(): Command { const cmd = new Command('table'); cmd.alias('tb').description('Table related commands.'); cmd.hook('preAction', async () => { console.log(''); console.log( chalk.yellow('⚠︎ WARNING: "table" is obsolete and will not be approved.'), ); console.log( chalk.yellow( ' Please use "task table-columns" and "task set-*" commands instead.', ), ); console.log(''); const { proceed } = await inquirer.prompt([ { type: 'confirm', name: 'proceed', message: 'Do you still want to continue?', default: false, }, ]); if (!proceed) { process.exit(0); } }); cmd.addCommand(SetIndexGroupsCommand()); cmd.addCommand(SetSourceIdCommand()); cmd.addCommand(SetKeysCommand()); cmd.addCommand(TrackChangesCommand()); cmd.addCommand(SetVisibilityCommand()); cmd.addCommand(SetPositionCommand()); return cmd; }