-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
249 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export function help() { | ||
console.log(`These are common Migrate commands: | ||
npm run migrate up Run up method for all new and updated migrations | ||
npm run migrate down [filename] Run down method of [filename] | ||
npm run migrate status Check status of migrations table`); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { reportStatusMigrate } from '../internals/report.js'; | ||
|
||
export function status(ctx, migrations) { | ||
for (let index = 0; index < migrations.length; ) { | ||
const migration = migrations[index]; | ||
|
||
reportStatusMigrate(migration, ++index); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { lockMigrate, unlockMigrate } from '../internals/connect.js'; | ||
import { reportUpMigrate } from '../internals/report.js'; | ||
import { saveMigrations } from '../internals/state.js'; | ||
import { STATUS_DONE, STATUS_NEW, STATUS_UPDATED } from '../constants.js'; | ||
|
||
async function upMigration(ctx, migrations) { | ||
await lockMigrate(ctx); | ||
|
||
for (let index = 0; index < migrations.length; ) { | ||
const migration = migrations[index]; | ||
|
||
reportUpMigrate(migration, ++index); | ||
await migration.up(); | ||
} | ||
|
||
await saveMigrations(ctx, migrations); | ||
await unlockMigrate(ctx); | ||
} | ||
|
||
export async function up(ctx, migrations) { | ||
const upMigrations = []; | ||
const wasDoneMigrations = []; | ||
const afterCommitMigrations = []; | ||
|
||
for (const migration of migrations) | ||
switch (migration.status) { | ||
case STATUS_NEW: | ||
case STATUS_UPDATED: { | ||
upMigrations.push(migration); | ||
|
||
if (Object.hasOwn(migration, 'onAfterCommit')) | ||
afterCommitMigrations.push(migration); | ||
|
||
break; | ||
} | ||
|
||
case STATUS_DONE: { | ||
if (Object.hasOwn(migration, 'onWasDone')) | ||
wasDoneMigrations.push(migration); | ||
|
||
break; | ||
} | ||
} | ||
|
||
if (upMigrations.length) { | ||
await ctx.startTransaction(upMigration, upMigrations); | ||
} | ||
|
||
for (const migration of afterCommitMigrations) { | ||
await migration.onAfterCommit(); | ||
} | ||
|
||
for (const migration of wasDoneMigrations) { | ||
await migration.onWasDone(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,29 @@ | ||
import { connect, disconnect } from './database.js'; | ||
import { argv } from 'node:process'; | ||
|
||
import { setMigrations } from './internals/state.js'; | ||
import { connect, disconnect } from './internals/connect.js'; | ||
|
||
import { up } from './actions/up.js'; | ||
import { help } from './actions/help.js'; | ||
import { status } from './actions/status.js'; | ||
|
||
const actions = { | ||
up, | ||
status, | ||
}; | ||
|
||
export async function migrate(context, migrations) { | ||
const ctx = await connect(context); | ||
const action = actions[argv[2] ?? 'up']; | ||
|
||
if (action) { | ||
const ctx = await connect(context); | ||
|
||
await disconnect(ctx); | ||
try { | ||
await action(ctx, await setMigrations(ctx, migrations), ...argv.slice(3)); | ||
} finally { | ||
await disconnect(ctx); | ||
} | ||
} else { | ||
help(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export const LOCK_ID = 0; | ||
|
||
export const STATUS_NEW = 'new'; | ||
export const STATUS_UPDATED = 'updated'; | ||
export const STATUS_DONE = 'done'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,11 @@ | ||
import { Context } from '../context.js'; | ||
import { STATUS_NEW } from './constants.js'; | ||
|
||
export class MigrationContext extends Context {} | ||
export class MigrationContext extends Context { | ||
static hash = null; | ||
static version = null; | ||
static wasDone = false; | ||
|
||
status = STATUS_NEW; | ||
isBootStrap = true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { green, blue, red } from '#utils/console.js'; | ||
|
||
function report(migration, command, index) { | ||
console.log( | ||
green('Migrate: ') + | ||
index + | ||
' ' + | ||
command + | ||
' ' + | ||
migration.constructor.path | ||
); | ||
} | ||
|
||
export function reportUpMigrate(migration, index) { | ||
report(migration, green('up'), index); | ||
} | ||
|
||
export function reportDownMigrate(migration, index) { | ||
report(migration, red('down'), index); | ||
} | ||
|
||
export function reportStatusMigrate(migration, index) { | ||
const status = migration.status.toUpperCase(); | ||
|
||
console.log( | ||
index + ' ' + blue(status) + ' ' + green(migration.constructor.path) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { ERRORS } from '@uah/postgres/src/constants.js'; | ||
import { STATUS_DONE, STATUS_NEW, STATUS_UPDATED } from '../constants.js'; | ||
|
||
async function createTableMigrations(ctx) { | ||
await ctx.sql` | ||
CREATE TABLE public.migrations ( | ||
name text COLLATE "C" PRIMARY KEY, | ||
hash bytea, | ||
updated_at timestamptz not null default CURRENT_TIMESTAMP, | ||
created_at timestamptz not null default CURRENT_TIMESTAMP | ||
)`; | ||
} | ||
|
||
async function getStateMigrations(ctx, migrations) { | ||
const names = migrations.map(({ path }) => path); | ||
const hashes = migrations.map(({ hash }) => hash); | ||
|
||
const query = ctx.sql` | ||
SELECT json_object_agg(files.name, CASE | ||
WHEN migrations.name IS NULL THEN ${STATUS_NEW} | ||
WHEN migrations.hash IS DISTINCT FROM files.hash THEN ${STATUS_UPDATED} | ||
ELSE ${STATUS_DONE} | ||
END) | ||
FROM unnest(${names}::text[], ${hashes}::bytea[]) AS files(name, hash) | ||
LEFT JOIN public.migrations USING(name)`.asValue(); | ||
|
||
try { | ||
return await query; | ||
} catch (error) { | ||
if (error.code === ERRORS.RELATION_NOT_EXIST) { | ||
await createTableMigrations(ctx); | ||
return await query; | ||
} | ||
throw error; | ||
} | ||
} | ||
|
||
export async function setMigrations(ctx, migrations) { | ||
const state = await getStateMigrations(ctx, migrations); | ||
|
||
ctx.isBootStrap = Object.values(state).every(status => status === STATUS_NEW); | ||
|
||
for (let i = 0; i < migrations.length; i++) { | ||
const migration = migrations[i]; | ||
const status = state[migration.path]; | ||
|
||
migration.wasDone = status === STATUS_DONE; | ||
|
||
migrations[i] = new migration(); | ||
migrations[i].status = status; | ||
} | ||
|
||
return migrations; | ||
} | ||
|
||
export async function saveMigrations(ctx, migrations) { | ||
const names = migrations.map(m => m.constructor.path); | ||
const hashes = migrations.map(m => m.constructor.hash); | ||
|
||
await ctx.sql` | ||
INSERT INTO public.migrations (name, hash) | ||
SELECT name, hash | ||
FROM unnest(${names}::text[], ${hashes}::bytea[]) AS files(name, hash) | ||
ON CONFLICT (name) DO UPDATE SET | ||
hash = EXCLUDED.hash, | ||
updated_at = CURRENT_TIMESTAMP`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { | ||
TRANSACTION_ACTIVE, | ||
TRANSACTION_INACTIVE, | ||
} from '@uah/postgres/src/constants.js'; | ||
|
||
export async function startTransaction(action, payload) { | ||
try { | ||
await this.postgres.query('BEGIN'); | ||
const result = await action(this, payload); | ||
|
||
if (this.postgres.state === TRANSACTION_ACTIVE) { | ||
await this.postgres.query('COMMIT'); | ||
} | ||
|
||
return result; | ||
} catch (error) { | ||
if (this.postgres.state !== TRANSACTION_INACTIVE) { | ||
await this.postgres.query('ROLLBACK'); | ||
} | ||
throw error; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
import process from 'node:process'; | ||
|
||
let FORCE_COLOR, | ||
NODE_DISABLE_COLORS, | ||
NO_COLOR, | ||
|