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
20 changes: 15 additions & 5 deletions src/package-lock.json

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

1 change: 1 addition & 0 deletions src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"@types/benchmark": "^2.1.5",
"0x": "^4.1.4",
"benchmark": "^2.1.4",
"commander": "^12.1.0",
"cpy-cli": "^5.0.0",
"dotenv": "^16.4.5"
},
Expand Down
5 changes: 4 additions & 1 deletion src/packages/dumbo/src/core/schema/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type MigratorOptions = {
options?: Omit<DatabaseLockOptions, 'lockId'> &
Partial<Pick<DatabaseLockOptions, 'lockId'>>;
};
dryRun?: boolean | undefined;
};

export const runSQLMigrations = (
Expand Down Expand Up @@ -71,6 +72,8 @@ export const runSQLMigrations = (
},
lockOptions,
);

return { success: options.dryRun ? false : true, result: undefined };
});

const runSQLMigration = async (
Expand Down Expand Up @@ -111,7 +114,7 @@ const getMigrationHash = async (content: string): Promise<string> => {
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
};

const combineMigrations = (...migration: Pick<SQLMigration, 'sqls'>[]) =>
export const combineMigrations = (...migration: Pick<SQLMigration, 'sqls'>[]) =>
migration.flatMap((m) => m.sqls).join('\n');

const ensureMigrationWasNotAppliedYet = async (
Expand Down
2 changes: 2 additions & 0 deletions src/packages/dumbo/src/postgres/core/schema/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type PostgreSQLMigratorOptions = {
options?: Omit<DatabaseLockOptions, 'lockId'> &
Partial<Pick<DatabaseLockOptions, 'lockId'>>;
};
dryRun?: boolean | undefined;
};

const migrationTableSQL = rawSql(`
Expand Down Expand Up @@ -50,4 +51,5 @@ export const runPostgreSQLMigrations = (
lockId: MIGRATIONS_LOCK_ID,
},
},
dryRun: options?.dryRun,
});
22 changes: 20 additions & 2 deletions src/packages/pongo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
"test:watch": "node --import tsx --test --watch",
"test:unit:watch": "glob -c \"node --import tsx --test --watch\" **/*.unit.spec.ts",
"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"
"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: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",
Expand Down Expand Up @@ -48,6 +52,16 @@
"types": "./dist/shim.d.cts",
"default": "./dist/shim.cjs"
}
},
"./cli": {
"import": {
"types": "./dist/cli.d.ts",
"default": "./dist/cli.js"
},
"require": {
"types": "./dist/cli.d.cts",
"default": "./dist/cli.cjs"
}
}
},
"typesVersions": {
Expand All @@ -57,6 +71,9 @@
],
"shim": [
"./dist/shim.d.ts"
],
"cli": [
"./dist/cli.d.ts"
]
}
},
Expand All @@ -72,7 +89,8 @@
"@types/pg": "^8.11.6",
"@types/uuid": "^10.0.0",
"pg": "^8.12.0",
"uuid": "^10.0.0"
"uuid": "^10.0.0",
"commander": "^12.1.0"
},
"devDependencies": {
"@types/node": "22.4.1"
Expand Down
14 changes: 14 additions & 0 deletions src/packages/pongo/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env node
import { Command } from 'commander';
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);

export default program;
Empty file.
189 changes: 189 additions & 0 deletions src/packages/pongo/src/commandLine/configFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import { Command } from 'commander';
import fs from 'node:fs';
import { objectEntries, type PongoSchemaConfig } from '../core';
import {
toDbSchemaMetadata,
type PongoDbSchemaMetadata,
} from '../core/typing/schema';

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;
};

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({
${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()}`;

export const loadConfigFile = async (
configPath: string,
): Promise<PongoDbSchemaMetadata> => {
const configUrl = new URL(configPath, `file://${process.cwd()}/`);
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const imported: Partial<{ default: PongoSchemaConfig }> = await import(
configUrl.href
);

const parsed = parseDefaultDbSchema(imported);

if (typeof parsed === 'string') {
console.error(parsed);
process.exit(1);
}

return parsed;
} catch {
console.error(`Error: Couldn't load file: ${configUrl.href}`);
process.exit(1);
}
};

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 => {
if (!imported.default) {
return missingDefaultExport;
}

if (!imported.default.schema) {
return missingSchema;
}

if (!imported.default.schema.dbs) {
return missingDbs;
}

const dbs = objectEntries(imported.default.schema.dbs).map((db) => db[1]);

const defaultDb = dbs.find((db) => db.name === undefined);

if (!defaultDb) {
return missingDefaultDb;
}

if (!defaultDb.collections) {
return missingCollections;
}

const collections = objectEntries(defaultDb.collections).map((col) => col[1]);

if (collections.length === 0) {
return missingCollections;
}

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 <name>',
'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>',
'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');
2 changes: 2 additions & 0 deletions src/packages/pongo/src/commandLine/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './configFile';
export * from './migrate';
Loading