generated from voxpelli/node-module-template
-
-
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.
Added
UmzeptionStorage
+ pg context and storage
- Loading branch information
Showing
11 changed files
with
301 additions
and
10 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,26 @@ | ||
export type { | ||
UmzeptionDependency, | ||
UmzeptionLookupOptions, | ||
AnyUmzeptionContext, | ||
DefineUmzeptionContexts, | ||
FastifyPostgresStyleDb, | ||
UmzeptionContext, | ||
UmzeptionDependency, | ||
UmzeptionLookupOptions, | ||
UmzeptionStorage, | ||
} from './lib/advanced-types.d.ts'; | ||
|
||
export { | ||
createUmzeptionPgContext, | ||
UmzeptionPgStorage, | ||
} from './lib/context-pg/main.js'; | ||
|
||
export { | ||
createUmzeptionContext, | ||
} from './lib/context.js'; | ||
|
||
export { | ||
umzeption, | ||
} from './lib/main.js'; | ||
|
||
export { | ||
BaseUmzeptionStorage, | ||
} from './lib/storage.js'; |
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,17 @@ | ||
// TODO: Extract pg context into a "umzeption-pg" module | ||
export { | ||
createUmzeptionPgContext, | ||
UmzeptionPgStorage, | ||
} from './lib/context-pg/main.js'; | ||
|
||
export { | ||
createUmzeptionContext, | ||
} from './lib/context.js'; | ||
|
||
export { | ||
umzeption, | ||
} from './lib/main.js'; | ||
|
||
export { | ||
BaseUmzeptionStorage, | ||
} from './lib/storage.js'; |
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,16 @@ | ||
import { createUmzeptionContext } from '../context.js'; | ||
import { createFastifyPostgresStyleDb } from './utils.js'; | ||
|
||
// TODO: Extract pg context into a "umzeption-pg" module | ||
|
||
/** @typedef {import('../advanced-types.js').FastifyPostgresStyleDb} FastifyPostgresStyleDb */ | ||
|
||
/** | ||
* @param {FastifyPostgresStyleDb["pool"]} pool | ||
* @returns {import('../advanced-types.js').UmzeptionContext<'pg', FastifyPostgresStyleDb>} | ||
*/ | ||
export function createUmzeptionPgContext (pool) { | ||
const db = createFastifyPostgresStyleDb(pool); | ||
|
||
return createUmzeptionContext('pg', db); | ||
} |
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,2 @@ | ||
export { createUmzeptionPgContext } from './context.js'; | ||
export { UmzeptionPgStorage } from './storage.js'; |
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,17 @@ | ||
import { BaseUmzeptionStorage } from '../storage.js'; | ||
|
||
/** @typedef {import('../advanced-types.js').DefineUmzeptionContexts['pg']} UmzeptionPgContext */ | ||
|
||
/** @augments {BaseUmzeptionStorage<UmzeptionPgContext>} */ | ||
export class UmzeptionPgStorage extends BaseUmzeptionStorage { | ||
/** | ||
* @override | ||
* @type {BaseUmzeptionStorage<UmzeptionPgContext>["query"]} | ||
*/ | ||
async query (context, query, ...values) { | ||
if (context.type === 'pg') { | ||
return context.value.query(query, values); | ||
} | ||
throw new Error(`Unsupported context type: ${context.type}`); | ||
} | ||
} |
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,32 @@ | ||
/** @typedef {import('../advanced-types.js').FastifyPostgresStyleDb} FastifyPostgresStyleDb */ | ||
|
||
/** | ||
* @this {FastifyPostgresStyleDb["pool"]} | ||
* @param {Parameters<FastifyPostgresStyleDb["transact"]>[0]} fn | ||
*/ | ||
async function transact (fn) { | ||
const client = await this.connect(); | ||
|
||
try { | ||
await client.query('BEGIN'); | ||
await fn(client); | ||
await client.query('COMMIT'); | ||
} catch { | ||
await client.query('ROLLBACK'); | ||
} finally { | ||
client.release(); | ||
} | ||
} | ||
|
||
/** | ||
* @param {FastifyPostgresStyleDb["pool"]} pool | ||
* @returns {FastifyPostgresStyleDb} | ||
*/ | ||
export function createFastifyPostgresStyleDb (pool) { | ||
return { | ||
connect: pool.connect.bind(pool), | ||
pool, | ||
query: pool.query.bind(pool), | ||
transact: transact.bind(pool), | ||
}; | ||
} |
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,52 @@ | ||
/** @typedef {import('./advanced-types.js').AnyUmzeptionContext} AnyUmzeptionContext */ | ||
|
||
/** | ||
* @template {AnyUmzeptionContext} T | ||
* @typedef {import('./advanced-types.js').UmzeptionStorage<T>} UmzeptionStorage | ||
*/ | ||
|
||
/** | ||
* @template {AnyUmzeptionContext} T | ||
* @augments {UmzeptionStorage<T>} | ||
*/ | ||
export class BaseUmzeptionStorage { | ||
#tableEnsured = false; | ||
|
||
/** @type {UmzeptionStorage<T>["logMigration"]} */ | ||
async logMigration ({ context, name }) { | ||
await this.ensureTable(context); | ||
await this.query(context, 'INSERT INTO umzeption_migrations (name) VALUES ($1)', name); | ||
} | ||
|
||
/** @type {UmzeptionStorage<T>["unlogMigration"]} */ | ||
async unlogMigration ({ context, name }) { | ||
await this.ensureTable(context); | ||
await this.query(context, 'DELETE FROM umzeption_migrations WHERE name = $1', name); | ||
} | ||
|
||
/** @type {UmzeptionStorage<T>["executed"]} */ | ||
async executed ({ context }) { | ||
await this.ensureTable(context); | ||
const { rows } = await this.query(context, 'SELECT name FROM umzeption_migrations'); | ||
return rows.map(row => typeof row['name'] === 'string' ? row['name'] : ''); | ||
} | ||
|
||
/** @type {UmzeptionStorage<T>["query"]} */ | ||
async query (context, _query, ..._values) { | ||
throw new Error(`Unsupported context type: ${context.type}`); | ||
} | ||
|
||
/** @type {UmzeptionStorage<T>["ensureTable"]} */ | ||
async ensureTable (context) { | ||
if (this.#tableEnsured) return; | ||
|
||
this.#tableEnsured = true; | ||
|
||
await this.query(context, ` | ||
CREATE TABLE IF NOT EXISTS umzeption_migrations ( | ||
name VARCHAR(255) PRIMARY KEY, | ||
created_at TIMESTAMP NOT NULL DEFAULT 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,104 @@ | ||
import { describe, it, afterEach } from 'node:test'; | ||
import assert from 'node:assert/strict'; | ||
|
||
import sinon from 'sinon'; | ||
|
||
import pg from 'pg'; | ||
import { Umzug } from 'umzug'; | ||
|
||
import { UmzeptionPgStorage, createUmzeptionPgContext, umzeption } from '../index.js'; | ||
|
||
import { down as downMain, up as upMain } from './fixtures/migrations/foo-01.js'; | ||
import { installSchema as installSchemaTestDependency } from './fixtures/test-dependency/index.js'; | ||
import { down as downTestDependency, up as upTestDependency } from './fixtures/test-dependency/migrations/foo-01.js'; | ||
|
||
function getDependencyStubCallCount ({ | ||
down = 0, | ||
installSchema = 0, | ||
up = 0, | ||
} = {}) { | ||
return { | ||
downMain: downMain.callCount + down, | ||
downTestDependency: downTestDependency.callCount + down, | ||
installSchemaTestDependency: installSchemaTestDependency.callCount + installSchema, | ||
upMain: upMain.callCount + up, | ||
upTestDependency: upTestDependency.callCount + up, | ||
}; | ||
} | ||
|
||
describe('PG Integration', () => { | ||
afterEach(() => { | ||
sinon.restore(); | ||
}); | ||
|
||
it('should resolve dependencies and create proper migrations', async () => { | ||
const queryStub = sinon.stub(pg.Pool.prototype, 'query').resolves({ rows: [] }); | ||
|
||
const context = createUmzeptionPgContext(new pg.Pool({ | ||
connectionString: 'postgresql://dbuser:secretpassword@localhost:3211/mydb', | ||
})); | ||
|
||
const storage = new UmzeptionPgStorage(); | ||
|
||
const expectedCallCount = getDependencyStubCallCount({ up: 1 }); | ||
|
||
const umzug = new Umzug({ | ||
migrations: umzeption({ | ||
dependencies: ['./fixtures/test-dependency'], | ||
glob: ['fixtures/migrations/*.js'], | ||
meta: import.meta, | ||
}), | ||
context, | ||
storage, | ||
logger: sinon.stub(console), | ||
}); | ||
|
||
await umzug.up(); | ||
|
||
assert.deepEqual( | ||
getDependencyStubCallCount(), | ||
expectedCallCount, | ||
'Unexpected call count of dependency stubs' | ||
); | ||
|
||
assert.deepStrictEqual(queryStub.args, [ | ||
[ | ||
'\n' + | ||
' CREATE TABLE IF NOT EXISTS umzeption_migrations (\n' + | ||
' name VARCHAR(255) PRIMARY KEY,\n' + | ||
' created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\n' + | ||
' )\n' + | ||
' ', | ||
[], | ||
], | ||
[ | ||
'SELECT name FROM umzeption_migrations', | ||
[], | ||
], | ||
[ | ||
'INSERT INTO umzeption_migrations (name) VALUES ($1)', | ||
[ | ||
'test-dependency:install', | ||
], | ||
], | ||
[ | ||
'INSERT INTO umzeption_migrations (name) VALUES ($1)', | ||
[ | ||
'main:install', | ||
], | ||
], | ||
[ | ||
'INSERT INTO umzeption_migrations (name) VALUES ($1)', | ||
[ | ||
'test-dependency|foo-01.js', | ||
], | ||
], | ||
[ | ||
'INSERT INTO umzeption_migrations (name) VALUES ($1)', | ||
[ | ||
'main|foo-01.js', | ||
], | ||
], | ||
]); | ||
}); | ||
}); |