Skip to content
This repository was archived by the owner on Oct 1, 2021. It is now read-only.

Commit 3b18eba

Browse files
committed
Refactor migrator to pass only repo's path into migration and not datastore instance.
License: MIT Signed-off-by: Adam Uhlir <[email protected]>
1 parent 9eb9418 commit 3b18eba

File tree

7 files changed

+94
-51
lines changed

7 files changed

+94
-51
lines changed

package.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
"bin": {
1313
"jsipfs-migrations": "./src/cli.js"
1414
},
15-
"browser": {},
15+
"browser": {
16+
"./src/option-node.js": "./src/option-browser.js",
17+
"./src/repo/lock.js": "./src/repo/lock-memory.js",
18+
"datastore-fs": "datastore-level"
19+
},
1620
"scripts": {
1721
"test": "aegir test",
1822
"test:node": "aegir test --target node",

src/commands.js

+11-19
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
1+
'use strict'
2+
13
const os = require('os')
24
const path = require('path')
35
const process = require('process')
46

5-
const datastore = require('datastore-fs')
67
const chalk = require('chalk')
7-
const log = require('debug')('js-ipfs-repo-migrations:commands')
88

9-
const repo_version = require('./repo/version')
9+
const repoVersion = require('./repo/version')
1010
const migrator = require('./index')
1111

12-
function getRepoRootStore(dir) {
13-
const repoPath = dir || process.env.IPFS_PATH || path.join(os.homedir(), '.jsipfs')
14-
log(`Operating with repo on path: ${repoPath}`)
15-
16-
// Will throw error if does not exist
17-
return new datastore(repoPath, {extension: '', createIfMissing: false})
18-
}
19-
2012
function asyncClosure(fnc) {
2113
return function asyncWrapper({resolve, ...options}) {
2214
resolve(fnc(options))
@@ -29,24 +21,24 @@ function reportingClosure(action){
2921
}
3022

3123
async function migrate({repoPath, ver, dry}) {
32-
const store = getRepoRootStore(repoPath)
33-
await migrator.migrate(store, ver, reportingClosure(dry ? 'loaded migration' : 'migrated to version'), dry)
24+
repoPath = repoPath || process.env.IPFS_PATH || path.join(os.homedir(), '.jsipfs')
25+
await migrator.migrate(repoPath, ver, reportingClosure(dry ? 'loaded migration' : 'migrated to version'), dry)
3426
}
3527

3628
async function revert({repoPath, ver, dry}) {
37-
const store = getRepoRootStore(repoPath)
38-
await migrator.revert(store, ver, reportingClosure(dry ? 'loaded migration' : 'reverted to version'), dry)
29+
repoPath = repoPath || process.env.IPFS_PATH || path.join(os.homedir(), '.jsipfs')
30+
await migrator.revert(repoPath, ver, reportingClosure(dry ? 'loaded migration' : 'reverted to version'), dry)
3931
}
4032

4133
async function status({repoPath}) {
42-
const store = getRepoRootStore(repoPath)
34+
repoPath = repoPath || process.env.IPFS_PATH || path.join(os.homedir(), '.jsipfs')
4335

44-
const repoVersion = await repo_version.getVersion(store)
36+
const version = await repoVersion.getVersion(repoPath)
4537
const lastMigrationVersion = migrator.getLatestMigrationVersion()
4638
const statusString =
47-
repoVersion < lastMigrationVersion ? chalk.yellow('There are migrations to be applied!') : chalk.green('Nothing to migrate!')
39+
version < lastMigrationVersion ? chalk.yellow('There are migrations to be applied!') : chalk.green('Nothing to migrate!')
4840

49-
return `${statusString}\nCurrent repo version: ${repoVersion}\nLast migration's version: ${lastMigrationVersion}`
41+
return `${statusString}\nCurrent repo version: ${version}\nLast migration's version: ${lastMigrationVersion}`
5042
}
5143

5244
module.exports = {

src/errors.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
'use strict'
22

3-
exports = module.exports
4-
3+
/**
4+
* Exception raised when trying to revert migration that is not possible
5+
* to revert.
6+
*/
57
class NonReversibleMigration extends Error {
68
constructor (message) {
79
super(message)
810
this.name = 'NonReversibleMigration'
911
this.message = message
1012
}
1113
}
12-
1314
exports.NonReversibleMigration = NonReversibleMigration
1415

16+
/**
17+
* Exception raised when structure of a repo is not as expected.
18+
*/
1519
class UnknownRepoStructure extends Error {
1620
constructor (message) {
1721
super(message)
1822
this.name = 'UnknownRepoStructure'
1923
this.message = message
2024
}
2125
}
22-
2326
exports.UnknownRepoStructure = UnknownRepoStructure

src/index.js

+48-19
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,47 @@
11
'use strict'
22

33
const migrations = require('../migrations')
4-
const repo_version = require('./repo/version')
5-
const repo_lock = require('./repo/lock')
4+
const repoVersion = require('./repo/version')
5+
const repoLock = require('./repo/lock')
6+
const isBrowser = require('./option-node')
7+
const errors = require('./errors')
68
const debug = require('debug')
79

810
const log = debug('js-ipfs-repo-migrations:migrator')
911

10-
exports.getLatestMigrationVersion = getLatestMigrationVersion
1112

1213
/**
1314
* Returns the version of latest migration.
1415
*
15-
* @returns int
16+
* @returns {int}
1617
*/
1718
function getLatestMigrationVersion() {
1819
return migrations[migrations.length - 1].version
1920
}
21+
exports.getLatestMigrationVersion = getLatestMigrationVersion
2022

21-
async function migrate(store, toVersion, progressCb, isDryRun) {
23+
/**
24+
* Main function to execute forward migrations.
25+
* It acquire lock on the provided path before doing any migrations.
26+
*
27+
* Signature of the progress callback is: function(migrationObject: object, currentMigrationNumber: int, totalMigrationsCount: int)
28+
*
29+
* @param {string} path - Path to initialized (!) JS-IPFS repo
30+
* @param {int|undefined} toVersion - Version to which the repo should be migrated, if undefined repo will be migrated to the latest version.
31+
* @param {function|undefined} progressCb - Callback which will be called after each executed migration to report progress
32+
* @param {boolean|undefined} isDryRun - Allows to simulate the execution of the migrations without any effect.
33+
* @returns {Promise<void>}
34+
*/
35+
async function migrate(path, toVersion, progressCb, isDryRun) {
2236
if (toVersion && (!Number.isInteger(toVersion) || toVersion <= 0)) {
2337
throw new Error('Version has to be positive integer!')
2438
}
2539
toVersion = toVersion || getLatestMigrationVersion()
2640

27-
const currentVersion = await repo_version.getVersion(store)
41+
const currentVersion = await repoVersion.getVersion(path)
2842

2943
let lock
30-
if (!isDryRun) lock = await repo_lock.lock(currentVersion, store.path)
44+
if (!isDryRun) lock = await repoLock.lock(currentVersion, path)
3145

3246
if (currentVersion === toVersion) {
3347
log('Nothing to migrate, skipping migrations.')
@@ -44,7 +58,7 @@ async function migrate(store, toVersion, progressCb, isDryRun) {
4458
log(`Migrating version ${migration.version}`)
4559
if (!isDryRun) {
4660
try {
47-
await migration.migrate(store)
61+
await migration.migrate(path, isBrowser)
4862
} catch (e) {
4963
e.message = `During migration to version ${migration.version} exception was raised: ${e.message}`
5064
throw e
@@ -55,16 +69,26 @@ async function migrate(store, toVersion, progressCb, isDryRun) {
5569
}
5670
}
5771

58-
if (!isDryRun) await repo_version.setVersion(store, toVersion || getLatestMigrationVersion())
72+
if (!isDryRun) await repoVersion.setVersion(path, toVersion || getLatestMigrationVersion())
5973
log('All migrations successfully migrated ', toVersion !== undefined ? `to version ${toVersion}!` : 'to latest version!')
6074

6175
if (!isDryRun) await lock.close()
62-
await store.close()
6376
}
64-
6577
exports.migrate = migrate
6678

67-
async function revert(store, toVersion, progressCb, isDryRun) {
79+
/**
80+
* Main function to execute backward migration (reversion).
81+
* It acquire lock on the provided path before doing any migrations.
82+
*
83+
* Signature of the progress callback is: function(migrationObject: object, currentMigrationNumber: int, totalMigrationsCount: int)
84+
*
85+
* @param {string} path - Path to initialized (!) JS-IPFS repo
86+
* @param {int} toVersion - Version to which the repo will be reverted.
87+
* @param {function|undefined} progressCb - Callback which will be called after each reverted migration to report progress
88+
* @param {boolean|undefined} isDryRun - Allows to simulate the execution of the reversion without any effect.
89+
* @returns {Promise<void>}
90+
*/
91+
async function revert(path, toVersion, progressCb, isDryRun) {
6892
if (!toVersion) {
6993
throw new Error('When reverting migrations, you have to specify to which version to revert!')
7094
}
@@ -73,19 +97,19 @@ async function revert(store, toVersion, progressCb, isDryRun) {
7397
throw new Error('Version has to be positive integer!')
7498
}
7599

76-
const currentVersion = await repo_version.getVersion(store)
100+
const currentVersion = await repoVersion.getVersion(path)
77101
if (currentVersion === toVersion) {
78102
log('Nothing to revert, skipping reverting.')
79103
return
80104
}
81105

82106
let {reversible, problematicMigration} = verifyReversibility(currentVersion, toVersion)
83107
if (!reversible) {
84-
throw new Error(`Migration version ${problematicMigration} is not possible to revert! Cancelling reversion.`)
108+
throw new errors.NonReversibleMigration(`Migration version ${problematicMigration} is not possible to revert! Cancelling reversion.`)
85109
}
86110

87111
let lock
88-
if (!isDryRun) lock = await repo_lock.lock(currentVersion, store.path)
112+
if (!isDryRun) lock = await repoLock.lock(currentVersion, path)
89113
let counter = 0, totalMigrations = currentVersion - toVersion
90114
const reversedMigrationArray = migrations.reverse()
91115
for (let migration of reversedMigrationArray) {
@@ -98,7 +122,7 @@ async function revert(store, toVersion, progressCb, isDryRun) {
98122
log(`Reverting migration version ${migration.version}`)
99123
if (!isDryRun) {
100124
try {
101-
await migration.revert(store)
125+
await migration.revert(path, isBrowser)
102126
} catch (e) {
103127
e.message = `During reversion to version ${migration.version} exception was raised: ${e.message}`
104128
throw e
@@ -109,15 +133,20 @@ async function revert(store, toVersion, progressCb, isDryRun) {
109133
}
110134
}
111135

112-
if (!isDryRun) await repo_version.setVersion(store, toVersion)
136+
if (!isDryRun) await repoVersion.setVersion(path, toVersion)
113137
log(`All migrations successfully reverted to version ${toVersion}!`)
114138

115139
if (!isDryRun) await lock.close()
116-
await store.close()
117140
}
118-
119141
exports.revert = revert
120142

143+
/**
144+
* Function checks if all migrations in given range supports reversion.
145+
*
146+
* @param {int} fromVersion
147+
* @param {int} toVersion
148+
* @returns {object}
149+
*/
121150
function verifyReversibility(fromVersion, toVersion) {
122151
const reversedMigrationArray = migrations.reverse()
123152
for (let migration of reversedMigrationArray) {

src/option-browser.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
'use strict'
2+
exports = true

src/option-node.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
'use strict'
2+
exports = false

src/repo/version.js

+19-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
'use strict'
2+
13
const errors = require('../errors')
4+
const Datastore = require('datastore-fs')
25

36
const Key = require('interface-datastore').Key
47

@@ -11,27 +14,35 @@ exports.getVersion = getVersion
1114
* This function needs to be cross-repo-version functional to be able to fetch any version number,
1215
* even in case of change of repo's versioning.
1316
*
14-
* @param {FsDatastore|LevelDatastore} store
15-
* @returns Promise<int>
17+
* @param {string} path
18+
* @returns {Promise<int>}
1619
*/
17-
async function getVersion(store) {
20+
async function getVersion(path) {
21+
const store = new Datastore(path, {extension: '', createIfMissing: false})
22+
await store.open()
1823

1924
if (!await store.has(versionKey)) {
2025
throw new errors.UnknownRepoStructure('Repo does not have version file! Is the repo initialized?')
2126
}
2227

23-
return parseInt(await store.get(versionKey))
28+
const version = parseInt(await store.get(versionKey))
29+
await store.close()
30+
31+
return version
2432
}
2533

2634
/**
2735
* Function for setting a version in cross-repo-version manner.
2836
*
29-
* @param {FsDatastore|LevelDatastore} store
37+
* @param {string} path
3038
* @param {int} version
31-
* @returns {Promise<Promise<void>|*|void|IDBRequest<IDBValidKey>|Promise<void>>}
39+
* @returns {Promise<void>}
3240
*/
33-
async function setVersion(store, version) {
34-
return store.put(versionKey, Buffer.from(String(version)))
41+
async function setVersion(path, version) {
42+
const store = new Datastore(path, {extension: '', createIfMissing: false})
43+
await store.open()
44+
await store.put(versionKey, Buffer.from(String(version)))
45+
await store.close()
3546
}
3647

3748
exports.setVersion = setVersion

0 commit comments

Comments
 (0)