diff --git a/src/packages/pongo/package.json b/src/packages/pongo/package.json index e7b42d3..ff862d2 100644 --- a/src/packages/pongo/package.json +++ b/src/packages/pongo/package.json @@ -16,7 +16,9 @@ "test:int:watch": "glob -c \"node --import tsx --test --watch\" **/*.int.spec.ts", "test:e2e:watch": "glob -c \"node --import tsx --test --watch\" **/*.e2e.spec.ts", "cli:sql:print": "tsx src/cli.ts migrate sql --collection users", - "cli:migrate:dryRun": "tsx src/cli.ts migrate run --config src/e2e/cli-config.ts -cs postgresql://postgres:postgres@localhost:5432/postgres " + "cli:migrate:dryRun": "tsx src/cli.ts migrate run --config src/e2e/cli-config.ts -cs postgresql://postgres:postgres@localhost:5432/postgres", + "cli:config:print": "tsx src/cli.ts config sample --print", + "cli:config:generate": "tsx src/cli.ts config sample --generate --file ./pongoConfig.ts " }, "repository": { "type": "git", diff --git a/src/packages/pongo/src/cli.ts b/src/packages/pongo/src/cli.ts index 2c15a51..880236b 100644 --- a/src/packages/pongo/src/cli.ts +++ b/src/packages/pongo/src/cli.ts @@ -1,11 +1,12 @@ #!/usr/bin/env node import { Command } from 'commander'; -import { migrateCommand } from './commandLine'; +import { configCommand, migrateCommand } from './commandLine'; const program = new Command(); program.name('pongo').description('CLI tool for Pongo'); +program.addCommand(configCommand); program.addCommand(migrateCommand); program.parse(process.argv); diff --git a/src/packages/pongo/src/commandLine/config.ts b/src/packages/pongo/src/commandLine/config.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/packages/pongo/src/commandLine/configFile.ts b/src/packages/pongo/src/commandLine/configFile.ts index 61569b7..ab7766e 100644 --- a/src/packages/pongo/src/commandLine/configFile.ts +++ b/src/packages/pongo/src/commandLine/configFile.ts @@ -1,26 +1,58 @@ +import { Command } from 'commander'; +import fs from 'node:fs'; import { objectEntries, type PongoSchemaConfig } from '../core'; import { toDbSchemaMetadata, type PongoDbSchemaMetadata, } from '../core/typing/schema'; -const sampleConfig = `import { pongoSchema } from '@event-driven-io/pongo'; +const formatTypeName = (input: string): string => { + if (input.length === 0) { + return input; + } + + let formatted = input.charAt(0).toUpperCase() + input.slice(1); + + if (formatted.endsWith('s')) { + formatted = formatted.slice(0, -1); + } + + return formatted; +}; -type User = { name: string }; +const sampleConfig = (collectionNames: string[] = ['users']) => { + const types = collectionNames + .map( + (name) => + `type ${formatTypeName(name)} = { name: string, description: string, date: Date }`, + ) + .join('\n'); + + const collections = collectionNames + .map( + (name) => + ` ${name}: pongoSchema.collection<${formatTypeName(name)}>('${name}'),`, + ) + .join('\n'); + + return `import { pongoSchema } from '@event-driven-io/pongo'; + +${types} export default { schema: pongoSchema.client({ database: pongoSchema.db({ - users: pongoSchema.collection('users'), +${collections} }), }), };`; +}; -const missingDefaultExport = `Error: Config should contain default export, e.g.\n\n${sampleConfig}`; -const missingSchema = `Error: Config should contain schema property, e.g.\n\n${sampleConfig}`; -const missingDbs = `Error: Config should have at least a single database defined, e.g.\n\n${sampleConfig}`; -const missingDefaultDb = `Error: Config should have a default database defined (without name or or with default database name), e.g.\n\n${sampleConfig}`; -const missingCollections = `Error: Database should have defined at least one collection, e.g.\n\n${sampleConfig}`; +const missingDefaultExport = `Error: Config should contain default export, e.g.\n\n${sampleConfig()}`; +const missingSchema = `Error: Config should contain schema property, e.g.\n\n${sampleConfig()}`; +const missingDbs = `Error: Config should have at least a single database defined, e.g.\n\n${sampleConfig()}`; +const missingDefaultDb = `Error: Config should have a default database defined (without name or or with default database name), e.g.\n\n${sampleConfig()}`; +const missingCollections = `Error: Database should have defined at least one collection, e.g.\n\n${sampleConfig()}`; export const loadConfigFile = async ( configPath: string, @@ -46,6 +78,20 @@ export const loadConfigFile = async ( } }; +export const generateConfigFile = ( + configPath: string, + collectionNames: string[], +): void => { + try { + fs.writeFileSync(configPath, sampleConfig(collectionNames), 'utf8'); + console.log(`Configuration file stored at: ${configPath}`); + } catch (error) { + console.error(`Error: Couldn't store config file: ${configPath}!`); + console.error(error); + process.exit(1); + } +}; + export const parseDefaultDbSchema = ( imported: Partial<{ default: PongoSchemaConfig }>, ): PongoDbSchemaMetadata | string => { @@ -81,3 +127,63 @@ export const parseDefaultDbSchema = ( return toDbSchemaMetadata(defaultDb); }; + +type SampleConfigOptions = + | { + collection: string[]; + print?: boolean; + } + | { + collection: string[]; + generate?: boolean; + file?: string; + }; + +export const configCommand = new Command('config').description( + 'Manage Pongo configuration', +); + +const sampleConfigCommand = configCommand + .command('sample') + .description('Generate or print sample configuration') + .option( + '-col, --collection ', + 'Specify the collection name', + (value: string, previous: string[]) => { + // Accumulate collection names into an array (explicitly typing `previous` as `string[]`) + return previous.concat([value]); + }, + [] as string[], + ) + .option( + '-f, --file ', + 'Path to configuration file with collection list', + ) + .option('-g, --generate', 'Generate sample config file') + .option('-p, --print', 'Print sample config file') + .action((options: SampleConfigOptions) => { + const collectionNames = + options.collection.length > 0 ? options.collection : ['users']; + + if (!('print' in options) && !('generate' in options)) { + console.error( + 'Error: Please provide either:\n--print param to print sample config or\n--generate to generate sample config file', + ); + process.exit(1); + } + + if ('print' in options) { + console.log(`${sampleConfig(collectionNames)}`); + } else if ('generate' in options) { + if (!options.file) { + console.error( + 'Error: You need to provide a config file through a --file', + ); + process.exit(1); + } + + generateConfigFile(options.file, collectionNames); + } + }); + +sampleConfigCommand.command('generate'); diff --git a/src/packages/pongo/src/commandLine/migrate.ts b/src/packages/pongo/src/commandLine/migrate.ts index cd3d09d..3d8a9b1 100644 --- a/src/packages/pongo/src/commandLine/migrate.ts +++ b/src/packages/pongo/src/commandLine/migrate.ts @@ -41,10 +41,7 @@ migrateCommand }, [] as string[], ) - .option( - '-f, --config ', - 'Path to configuration file with collection list', - ) + .option('-f, --config ', 'Path to configuration file with Pongo config') .option('-dr, --dryRun', 'Perform dry run without commiting changes', false) .action(async (options: MigrateRunOptions) => { const { collection, dryRun } = options;