diff --git a/.gitignore b/.gitignore index efc159282d..a1de6c4efc 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ packages/testing/**/.cursor/rules/ __pycache__ packages/cli/THIRD_PARTY_LICENSES.md .coverage +packages/cli/src/commands/export/outputs \ No newline at end of file diff --git a/packages/cli/src/commands/export/__tests__/entities.test.ts b/packages/cli/src/commands/export/__tests__/entities.test.ts new file mode 100644 index 0000000000..e6f070311d --- /dev/null +++ b/packages/cli/src/commands/export/__tests__/entities.test.ts @@ -0,0 +1,42 @@ +import { ExportEntitiesCommand } from '../entities'; +import { ensureDir } from 'fs-extra'; + +jest.mock('fs-extra'); + +describe('ExportEntitiesCommand', () => { + describe('run', () => { + it('should export entities', async () => { + const command = new ExportEntitiesCommand(); + // @ts-expect-error Protected property + command.flags = { + outputDir: './exports', + }; + // @ts-expect-error Protected property + command.logger = { + info: jest.fn(), + error: jest.fn(), + }; + await command.run(); + + expect(ensureDir).toHaveBeenCalledWith('./exports'); + // @ts-expect-error Protected property + expect(command.logger.info).toHaveBeenCalledTimes(4); + // @ts-expect-error Protected property + expect(command.logger.error).not.toHaveBeenCalled(); + }); + }); + + describe('catch', () => { + it('should log error', () => { + const command = new ExportEntitiesCommand(); + // @ts-expect-error Protected property + command.logger = { + error: jest.fn(), + }; + command.catch(new Error('test')); + + // @ts-expect-error Protected property + expect(command.logger.error).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/cli/src/commands/export/entities.ts b/packages/cli/src/commands/export/entities.ts new file mode 100644 index 0000000000..0e02db9a89 --- /dev/null +++ b/packages/cli/src/commands/export/entities.ts @@ -0,0 +1,42 @@ +import { Command } from '@n8n/decorators'; +import { z } from 'zod'; +import path from 'path'; +import { ensureDir } from 'fs-extra'; + +import { BaseCommand } from '../base-command'; + +const flagsSchema = z.object({ + outputDir: z + .string() + .describe('Output directory path') + .default(path.join(__dirname, './outputs')), +}); + +@Command({ + name: 'export:entities', + description: 'Export database entities to JSON files', + examples: ['', '--outputDir=./exports', '--outputDir=/path/to/backup'], + flagsSchema, +}) +export class ExportEntitiesCommand extends BaseCommand> { + async run() { + const outputDir = this.flags.outputDir; + + this.logger.info('\n⚠️⚠️ This feature is currently under development. ⚠️⚠️'); + this.logger.info('\n🚀 Starting entity export...'); + this.logger.info(`📁 Output directory: ${outputDir}`); + + await ensureDir(outputDir); + + // TODO: Export entities + + this.logger.info('✅ Task completed successfully! \n'); + } + + catch(error: Error) { + this.logger.error('❌ Error exporting entities. See log messages for details. \n'); + this.logger.error('Error details:'); + this.logger.error('\n====================================\n'); + this.logger.error(`${error.message} \n`); + } +}