first commit
This commit is contained in:
54
services/web/migrations/lib/adapter.mjs
Normal file
54
services/web/migrations/lib/adapter.mjs
Normal file
@@ -0,0 +1,54 @@
|
||||
import Path from 'node:path'
|
||||
import { db } from '../../app/src/infrastructure/mongodb.js'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = Path.dirname(__filename)
|
||||
|
||||
class Adapter {
|
||||
constructor(params) {
|
||||
if (
|
||||
!process.env.SKIP_TAG_CHECK &&
|
||||
!process.argv.includes('create') &&
|
||||
!(process.argv.includes('-t') || process.argv.includes('--tags'))
|
||||
) {
|
||||
console.error("ERROR: must pass tags using '-t' or '--tags', exiting")
|
||||
process.exit(1)
|
||||
}
|
||||
this.params = params || {}
|
||||
}
|
||||
|
||||
getTemplatePath() {
|
||||
return Path.resolve(__dirname, 'template.mjs')
|
||||
}
|
||||
|
||||
async connect() {
|
||||
return { db }
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
async getExecutedMigrationNames() {
|
||||
const migrations = await db.migrations
|
||||
.find({}, { sort: { migratedAt: 1 }, projection: { name: 1 } })
|
||||
.toArray()
|
||||
return migrations.map(migration => migration.name)
|
||||
}
|
||||
|
||||
async markExecuted(name) {
|
||||
return db.migrations.insertOne({
|
||||
name,
|
||||
migratedAt: new Date(),
|
||||
})
|
||||
}
|
||||
|
||||
async unmarkExecuted(name) {
|
||||
return db.migrations.deleteOne({
|
||||
name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default Adapter
|
84
services/web/migrations/lib/helpers.mjs
Normal file
84
services/web/migrations/lib/helpers.mjs
Normal file
@@ -0,0 +1,84 @@
|
||||
// @ts-check
|
||||
|
||||
import {
|
||||
db,
|
||||
getCollectionNames,
|
||||
getCollectionInternal,
|
||||
} from '../../app/src/infrastructure/mongodb.js'
|
||||
|
||||
/**
|
||||
* @typedef {import('mongodb-legacy').Document} Collection
|
||||
* @typedef {import('mongodb-legacy').Collection<Document>} Collection
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Collection} collection
|
||||
* @param {Array<{ name: string }>} indexes
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function addIndexesToCollection(collection, indexes) {
|
||||
await Promise.all(
|
||||
indexes.map(index => {
|
||||
index.background = true
|
||||
return collection.createIndex(index.key, index)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Collection} collection
|
||||
* @param {Array<{ name: string }>} indexes
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function dropIndexesFromCollection(collection, indexes) {
|
||||
await Promise.all(
|
||||
indexes.map(async index => {
|
||||
try {
|
||||
await collection.dropIndex(index.name)
|
||||
} catch (err) {
|
||||
if (err.code === 27 /* IndexNotFound */) {
|
||||
console.log(`Index ${index.name} not found; drop was a no-op.`)
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} collectionName
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function dropCollection(collectionName) {
|
||||
if (db[collectionName]) {
|
||||
throw new Error(`blocking drop of an active collection: ${collectionName}`)
|
||||
}
|
||||
|
||||
const allCollections = await getCollectionNames()
|
||||
if (!allCollections.includes(collectionName)) return
|
||||
const collection = await getCollectionInternal(collectionName)
|
||||
await collection.drop()
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a dependent migration has run. Throws an error otherwise.
|
||||
*
|
||||
* @param {string} migrationName
|
||||
*/
|
||||
async function assertDependency(migrationName) {
|
||||
const migrations = await getCollectionInternal('migrations')
|
||||
const migration = await migrations.findOne({ name: migrationName })
|
||||
if (migration == null) {
|
||||
throw new Error(
|
||||
`Bad migration order: ${migrationName} should run before this migration`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
addIndexesToCollection,
|
||||
dropIndexesFromCollection,
|
||||
dropCollection,
|
||||
assertDependency,
|
||||
}
|
26
services/web/migrations/lib/template.mjs
Normal file
26
services/web/migrations/lib/template.mjs
Normal file
@@ -0,0 +1,26 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
import Helpers from './lib/helpers.mjs'
|
||||
|
||||
const tags = ['server-ce', 'server-pro', 'saas']
|
||||
|
||||
const migrate = async client => {
|
||||
const { db } = client
|
||||
// Are there migrations that need to run before this migration?
|
||||
// Use the following helper to enforce the dependency:
|
||||
//
|
||||
// await Helpers.assertDependency('20200101000000_another_migration')
|
||||
|
||||
// await Helpers.addIndexesToCollection(db.wombats, [{ name: 1 }])
|
||||
}
|
||||
|
||||
const rollback = async client => {
|
||||
const { db } = client
|
||||
// await Helpers.dropIndexesFromCollection(db.wombats, [{ name: 1 }])
|
||||
}
|
||||
|
||||
export default {
|
||||
tags,
|
||||
migrate,
|
||||
rollback,
|
||||
}
|
Reference in New Issue
Block a user