feat: conditionally use transactions

This commit is contained in:
Elliot DeNolf
2023-08-10 13:54:04 -04:00
parent 7345fa8fcd
commit 791ed3be50
7 changed files with 47 additions and 100 deletions

61
.vscode/launch.json vendored
View File

@@ -33,54 +33,39 @@
"cwd": "${workspaceFolder}" "cwd": "${workspaceFolder}"
}, },
{ {
"type": "node", "type": "node-terminal",
"request": "launch", "command": "ts-node src/bin/migrate.ts migrate",
"name": "Migrate CLI - create",
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"src/bin/migrate.ts",
"migrate:create",
"second"
],
"env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts"
},
"outputCapture": "std",
},
{
"type": "node",
"request": "launch", "request": "launch",
"name": "Migrate CLI - migrate", "name": "Migrate CLI - migrate",
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"src/bin/migrate.ts",
"migrate",
],
"env": { "env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts" "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "postgres",
"PAYLOAD_DROP_DATABASE": "true",
}, },
"outputCapture": "std", "outputCapture": "std",
}, },
{ {
"type": "node", "type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:status",
"request": "launch", "request": "launch",
"name": "Migrate CLI - status", "name": "Migrate CLI - status",
"runtimeArgs": [
"-r",
"ts-node/register"
],
"args": [
"src/bin/migrate.ts",
"migrate:status",
],
"env": { "env": {
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts" "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_DATABASE": "postgres",
"PAYLOAD_DROP_DATABASE": "true",
},
"outputCapture": "std",
},
{
"type": "node-terminal",
"command": "ts-node src/bin/migrate.ts migrate:create yass",
"request": "launch",
"name": "Migrate CLI - create",
"env": {
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
"PAYLOAD_DATABASE": "postgres",
// "PAYLOAD_DROP_DATABASE": "true",
}, },
"outputCapture": "std", "outputCapture": "std",
}, },

View File

@@ -12,11 +12,11 @@ import prompts from 'prompts';
import { buildTable } from './schema/build'; import { buildTable } from './schema/build';
import type { GenericEnum, GenericRelation, GenericTable, PostgresAdapter } from './types'; import type { GenericEnum, GenericRelation, GenericTable, PostgresAdapter } from './types';
const migrationTemplate = ` const migrationTemplate = (upSQL?: string) => `
import payload, { Payload } from 'payload'; import payload, { Payload } from 'payload';
export async function up(payload: Payload): Promise<void> { export async function up(payload: Payload): Promise<void> {
await payload.db.db.execute(\`{{SQL}}\`); ${upSQL ? `await payload.db.db.execute(\`${upSQL}\`);` : '// Migration code'}
}; };
export async function down(payload: Payload): Promise<void> { export async function down(payload: Payload): Promise<void> {
@@ -50,7 +50,7 @@ export const createMigration: CreateMigration = async function createMigration(
const drizzleJsonBefore = generateDrizzleJson(JSON.parse(snapshotJSON)); const drizzleJsonBefore = generateDrizzleJson(JSON.parse(snapshotJSON));
const drizzleJsonAfter = generateDrizzleJson(this.schema, drizzleJsonBefore.id); const drizzleJsonAfter = generateDrizzleJson(this.schema, drizzleJsonBefore.id);
const sqlStatements = await generateMigration(drizzleJsonBefore, drizzleJsonAfter); const sqlStatements = await generateMigration(drizzleJsonBefore, drizzleJsonAfter);
fs.writeFileSync(filePath, migrationTemplate.replace('{{SQL}}', sqlStatements.join('\n'))); fs.writeFileSync(filePath, migrationTemplate(sqlStatements.length ? sqlStatements?.join('\n') : undefined));
// TODO: // TODO:
// Get the most recent migration schema from the file system // Get the most recent migration schema from the file system

View File

@@ -7,9 +7,13 @@ import { migrateStatus } from './migrations/migrateStatus';
import { migrateDown } from './migrations/migrateDown'; import { migrateDown } from './migrations/migrateDown';
import { migrateRefresh } from './migrations/migrateRefresh'; import { migrateRefresh } from './migrations/migrateRefresh';
import { migrateReset } from './migrations/migrateReset'; import { migrateReset } from './migrations/migrateReset';
import { DatabaseAdapter } from './types'; import { BeginTransaction, CommitTransaction, DatabaseAdapter, RollbackTransaction } from './types';
import { createMigration } from './migrations/createMigration'; import { createMigration } from './migrations/createMigration';
const beginTransaction: BeginTransaction = async () => null;
const rollbackTransaction: RollbackTransaction = async () => null;
const commitTransaction: CommitTransaction = async () => null;
export function createDatabaseAdapter<T extends DatabaseAdapter>(args: MarkOptional<T, export function createDatabaseAdapter<T extends DatabaseAdapter>(args: MarkOptional<T,
| 'transaction' | 'transaction'
| 'migrate' | 'migrate'
@@ -19,7 +23,8 @@ export function createDatabaseAdapter<T extends DatabaseAdapter>(args: MarkOptio
| 'migrateRefresh' | 'migrateRefresh'
| 'migrateReset' | 'migrateReset'
| 'migrateFresh' | 'migrateFresh'
| 'migrationDir'>): T { | 'migrationDir'
>): T {
// Need to implement DB Webpack config extensions here // Need to implement DB Webpack config extensions here
if (args.webpack) { if (args.webpack) {
const existingWebpackConfig = args.payload.config.admin.webpack ? args.payload.config.admin.webpack : (webpackConfig) => webpackConfig; const existingWebpackConfig = args.payload.config.admin.webpack ? args.payload.config.admin.webpack : (webpackConfig) => webpackConfig;
@@ -39,6 +44,12 @@ export function createDatabaseAdapter<T extends DatabaseAdapter>(args: MarkOptio
migrateRefresh, migrateRefresh,
migrateReset, migrateReset,
migrateFresh: async () => null, migrateFresh: async () => null,
// Default 'null' transaction functions
beginTransaction,
commitTransaction,
rollbackTransaction,
...args, ...args,
} as T; } as T;
} }

View File

@@ -19,7 +19,7 @@ export async function getMigrations({
const existingMigrations = migrationQuery.docs as unknown as MigrationData[]; const existingMigrations = migrationQuery.docs as unknown as MigrationData[];
// Get the highest batch number from existing migrations // Get the highest batch number from existing migrations
const latestBatch = existingMigrations?.[0]?.batch || 0; const latestBatch = Number(existingMigrations?.[0]?.batch) || 0;
return { return {
existingMigrations, existingMigrations,

View File

@@ -21,10 +21,11 @@ export async function migrate(this: DatabaseAdapter): Promise<void> {
} }
const start = Date.now(); const start = Date.now();
let transactionID; let transactionID: string | number | undefined;
payload.logger.info({ msg: `Migrating: ${migration.name}` });
try { try {
payload.logger.info({ msg: `Migrating: ${migration.name}` });
transactionID = await this.beginTransaction(); transactionID = await this.beginTransaction();
await migration.up({ payload }); await migration.up({ payload });
payload.logger.info({ msg: `Migrated: ${migration.name} (${Date.now() - start}ms)` }); payload.logger.info({ msg: `Migrated: ${migration.name} (${Date.now() - start}ms)` });
@@ -34,9 +35,7 @@ export async function migrate(this: DatabaseAdapter): Promise<void> {
name: migration.name, name: migration.name,
batch: newBatch, batch: newBatch,
}, },
req: { ...(transactionID && { req: { transactionID } as PayloadRequest }),
transactionID,
} as PayloadRequest,
}); });
await this.commitTransaction(transactionID); await this.commitTransaction(transactionID);
} catch (err: unknown) { } catch (err: unknown) {

View File

@@ -16,6 +16,7 @@ export const readMigrationFiles = async ({
const files = fs const files = fs
.readdirSync(payload.db.migrationDir) .readdirSync(payload.db.migrationDir)
.sort() .sort()
.filter((f) => f.endsWith('.ts'))
.map((file) => { .map((file) => {
return path.resolve(payload.db.migrationDir, file); return path.resolve(payload.db.migrationDir, file);
}); });

View File

@@ -1,52 +1,3 @@
/* eslint-disable no-console */ import { buildConfigWithDefaults } from '../buildConfigWithDefaults';
import path from 'path'; export default buildConfigWithDefaults({});
import { buildConfig } from '../buildConfig';
import { CollectionConfig } from '../../types';
import { mongooseAdapter } from '../../src/mongoose';
import payload from '../../src';
const Users: CollectionConfig = {
slug: 'users',
auth: true,
fields: [
{
name: 'custom',
type: 'text',
},
{
name: 'checkbox',
type: 'checkbox',
},
],
};
// // @ts-expect-error partial
// const mockAdapter: DatabaseAdapter = {
// // payload: undefined,
// migrationDir: path.resolve(__dirname, '.migrations'),
// migrateStatus: async () => console.log('TODO: migrateStatus not implemented.'),
// createMigration: async (): Promise<void> =>
// console.log('TODO: createMigration not implemented.'),
// migrate: async (): Promise<void> => console.log('TODO: migrate not implemented.'),
// migrateDown: async (): Promise<void> =>
// console.log('TODO: migrateDown not implemented.'),
// migrateRefresh: async (): Promise<void> =>
// console.log('TODO: migrateRefresh not implemented.'),
// migrateReset: async (): Promise<void> =>
// console.log('TODO: migrateReset not implemented.'),
// migrateFresh: async (): Promise<void> =>
// console.log('TODO: migrateFresh not implemented.'),
// };
export default buildConfig({
serverURL: 'http://localhost:3000',
admin: {
user: Users.slug,
},
collections: [Users],
typescript: {
outputFile: path.resolve(__dirname, 'payload-types.ts'),
},
db: mongooseAdapter({ payload, url: 'mongodb://127.0.0.1:27017/migrations-cli-test' }),
});