Skip to content

Commit

Permalink
Merge pull request #99 from ELEVATE-Project/migrations
Browse files Browse the repository at this point in the history
Migrations
  • Loading branch information
rakeshSgr authored Aug 2, 2022
2 parents 833b7ae + f9c4836 commit 5a17bfe
Show file tree
Hide file tree
Showing 16 changed files with 557 additions and 2 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,21 @@ Elevate services can be setup in local using three methods:
```
ELEVATE/mentoring/src/scripts$ node schedulerScript.js
```

## Migrations Commands

### Check migrations

npm run elevate-migrations s

### Create migrations

npm run elevate-migrations create <migration-name>

### Run migrations

npm run elevate-migrations up

## Down migrations

npm run elevate-migrations down
1 change: 1 addition & 0 deletions src/constants/endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ module.exports = {
GET_RECORDINGS: 'api/getRecordings',
USERS_LIST: 'v1/account/list',
SHARE_MENTOR_PROFILE: 'v1/profile/share',
USERS_ENTITY_READ: 'v1/userentity/read',
}
2 changes: 1 addition & 1 deletion src/globalConfig.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{ "mongoUri": "mongodb://127.0.0.1:54466/", "mongoDBName": "jest" }
{ "mongoUri": "mongodb://127.0.0.1:59200/", "mongoDBName": "jest" }
117 changes: 117 additions & 0 deletions src/migrations/20220802074933-session_form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
const endpoints = require('../constants/endpoints')
const request = require('request')
module.exports = {
async up(db) {
global.migrationMsg = 'Session Form Created'

const form = await db.collection('forms').findOne({ type: 'session' })
if (!form) {
const categories = await db
.collection('entities')
.find(
{ type: 'categories', status: 'ACTIVE', deleted: false },
{ projection: { value: 1, label: 1, image: 1, _id: 0 } }
)
.toArray()

const controls = [
{
name: 'title',
label: 'Session Title',
type: 'text',
position: 'floating',
},
{
name: 'description',
label: 'Description',
type: 'textarea',
position: 'floating',
},
{
name: 'startDate',
label: 'Start Date',
displayFormat: 'DD/MMM/YYYY HH:mm',
dependedChild: 'endDate',
type: 'date',
position: 'floating',
},
{
name: 'endDate',
label: 'End Date',
displayFormat: 'DD/MMM/YYYY HH:mm',
type: 'date',
position: 'floating',
},
{
name: 'recommendedFor',
label: 'Recommended For',
type: 'chip',
disabled: false,
showSelectAll: true,
showAddOption: true,
optionsUrl: { url: 'roles/search', method: 'POST' },
options: [
{ value: 'deo', label: 'District education officer' },
{ value: 'beo', label: 'Block education officer' },
{ value: 'hm', label: 'Head Master' },
{ value: 'TE', label: 'Teacher' },
{ value: 'CO', label: 'Cluster Officials' },
],
},
{
name: 'categories',
label: 'Categories',
type: 'chip',
disabled: false,
showSelectAll: true,
showAddOption: true,
options: categories,
},
{
name: 'medium',
label: 'Select Medium',
type: 'chip',
disabled: false,
showSelectAll: true,
showAddOption: true,
options: [
{ label: 'English', value: '1' },
{ label: 'Hindi', value: '2' },
],
},
]
const nullPosition = ['medium', 'categories', 'recommendedFor']
controls.forEach((element) => {
if (nullPosition.includes(element.name)) {
element.position = ''
} else {
element.position = 'floating'
}
element.validators = { required: true }
element.class = 'ion-margin'
element.value = ''
})

let formsData = {
type: 'session',
subType: 'sessionForm',
action: 'sessionFields',
ver: '1.0',
data: {
templateName: 'defaultTemplate',
fields: {
controls: controls,
},
},
}
await db.collection('forms').insertOne(formsData)
}
},

async down(db) {
const form = await db.collection('forms').findOne({ type: 'session' })
if (form) {
await db.collection('forms').deleteOne({ type: 'session' })
}
},
}
17 changes: 17 additions & 0 deletions src/module/migrations/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Mentoring Service

## Check migrations

npm run elevate-migrations s

## Create migrations

npm run elevate-migrations create <migration-name>

## Run migrations

npm run elevate-migrations up

## Down migrations

npm run elevate-migrations down
125 changes: 125 additions & 0 deletions src/module/migrations/bin/migrations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#! /usr/bin/env node

const program = require('commander')
global._ = require('lodash')
const Table = require('cli-table')
const migrateMongo = require('../lib/migrate')
const pkgjson = require('../../../package.json')

function printMigrated(migrated = []) {
migrated.forEach((migratedItem) => {
console.log(`MIGRATED UP: ${migratedItem}`)
})
}

function handleError(err) {
console.error(`ERROR: ${err.message}`)
process.exit(1)
}

function printStatusTable(statusItems) {
const table = new Table({ head: ['Filename', 'Applied At'] })
statusItems.forEach((item) => table.push(_.values(item)))
console.log(table.toString())
}

program.version(pkgjson.version)

program
.command('initialization')
.alias('i')
.description('initialize a new migration project')
.action(() =>
migrateMongo
.init()
.then(() => console.log(`Initialization successful. Please edit the generated file`))
.catch((err) => handleError(err))
)

program
.command('create [description]')
.alias('c')
.description('create a new database migration with the provided description')
.option('-f --file <file>', 'use a custom config file')
.action((description, options) => {
global.options = options
migrateMongo
.create(description)
.then((fileName) => console.log(`Created: ${fileName}`))
.catch((err) => handleError(err))
})

program
.command('up')
.alias('u')
.description('run all pending database migrations')
.option('-n --name <name>', 'use a custom config name')
.action((env, options) => {
global.alias = env._alias
if (env.name !== '') {
global.upgradeOneItem = env.name
}
global.options = options
migrateMongo.database
.connect()
.then((db) => migrateMongo.up(db))
.then((migrated) => {
printMigrated(migrated)
process.exit(0)
})
.catch((err) => {
handleError(err)
printMigrated(err.migrated)
})
})

program
.command('down')
.alias('d')
.description('undo the last applied database migration')
.option('-n --name <name>', 'use a custom config name')
.action((env, options) => {
global.alias = env._alias
if (env.name !== '') {
global.downgradeOneItem = env.name
}
global.options = options
migrateMongo.database
.connect()
.then((db) => migrateMongo.down(db))
.then((migrated) => {
migrated.forEach((migratedItem) => {
console.log(`MIGRATED DOWN: ${migratedItem}`)
})
process.exit(0)
})
.catch((err) => {
handleError(err)
})
})

program
.command('status')
.alias('s')
.description('print the changelog of the database')
.option('-f --file <file>', 'use a custom config file')
.action((env, options) => {
global.alias = env._alias
global.options = options
migrateMongo.database
.connect()
.then((db) => migrateMongo.status(db))
.then((statusItems) => {
printStatusTable(statusItems)
process.exit(0)
})
.catch((err) => {
handleError(err)
})
})

program.parse(process.argv)

if (_.isEmpty(program.args)) {
program.outputHelp()
}
16 changes: 16 additions & 0 deletions src/module/migrations/lib/actions/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const fs = require('fs-extra')
const path = require('path')
const moment = require('moment-timezone')
const migrationsDir = require('../env/migrationsDir')

module.exports = async (description) => {
if (!description) {
throw new Error('Missing parameter: description')
}
await migrationsDir.shouldExist()
const source = path.join(__dirname, '../../samples/migration.js')
const filename = `${moment().format('YYYYMMDDHHmmss')}-${description.split(' ').join('_')}.js`
const destination = path.join(await migrationsDir.resolve(), filename)
await fs.copy(source, destination)
return filename
}
34 changes: 34 additions & 0 deletions src/module/migrations/lib/actions/down.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const _ = require('lodash')
const fnArgs = require('fn-args')
const { promisify } = require('util')
const status = require('./status')
const migrationsDir = require('../env/migrationsDir')

module.exports = async (db) => {
const downgraded = []
const statusItems = await status(db)
const appliedItems = statusItems.filter((item) => item.appliedAt !== 'PENDING')
const lastAppliedItem = _.last(appliedItems)

if (lastAppliedItem) {
try {
const migration = await migrationsDir.loadMigration(lastAppliedItem.fileName)
const args = fnArgs(migration.down)
const down = args.length > 1 ? promisify(migration.down) : migration.down
await down(db)
} catch (err) {
throw new Error(`Could not migrate down ${lastAppliedItem.fileName}: ${err.message}`)
}

const collectionName = process.env.MIGRATION_COLLECTION || 'migrations'
const collection = db.collection(collectionName)
try {
await collection.deleteOne({ fileName: lastAppliedItem.fileName })
downgraded.push(lastAppliedItem.fileName)
} catch (err) {
throw new Error(`Could not update changelog: ${err.message}`)
}
}

return downgraded
}
13 changes: 13 additions & 0 deletions src/module/migrations/lib/actions/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const fs = require('fs-extra')
const path = require('path')

const migrationsDir = require('../env/migrationsDir')

function createMigrationsDirectory() {
return fs.mkdirs(path.join(process.cwd(), process.env.MIGRATION_DIR))
}

module.exports = async () => {
await migrationsDir.shouldNotExist()
return createMigrationsDirectory()
}
29 changes: 29 additions & 0 deletions src/module/migrations/lib/actions/status.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { find } = require('lodash')
const migrationsDir = require('../env/migrationsDir')

module.exports = async (db) => {
await migrationsDir.shouldExist()

let fileValue
const fileNames = await migrationsDir.getFileNames()

if (alias == 'u' && upgradeOneItem && fileNames.includes(upgradeOneItem)) {
fileValue = [upgradeOneItem]
} else if (alias == 'd' && downgradeOneItem && fileNames.includes(downgradeOneItem)) {
fileValue = [downgradeOneItem]
} else {
fileValue = fileNames
}
const collectionName = process.env.MIGRATION_COLLECTION || 'migrations'

const collection = db.collection(collectionName)
const changelog = await collection.find({}).toArray()

const statusTable = fileValue.map((fileName) => {
const itemInLog = find(changelog, { fileName })
const appliedAt = itemInLog ? itemInLog.appliedAt.toJSON() : 'PENDING'
return { fileName, appliedAt }
})

return statusTable
}
Loading

0 comments on commit 5a17bfe

Please sign in to comment.