@storyous/mongodb 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/__tests__/appData.test.js +36 -0
- package/__tests__/appData.test.ts +39 -0
- package/__tests__/fixtures.test.js +40 -0
- package/__tests__/fixtures.test.ts +44 -0
- package/__tests__/getMongoClient.js +19 -0
- package/__tests__/getMongoClient.ts +24 -0
- package/__tests__/migrations/1642152064249-test.js +7 -0
- package/__tests__/migrations/1642152064249-test.ts +7 -0
- package/__tests__/mongoLocker.test.js +151 -0
- package/__tests__/mongoLocker.test.ts +191 -0
- package/__tests__/runMigration.test.js +22 -0
- package/__tests__/runMigration.test.ts +23 -0
- package/__tests__/withTransaction.test.js +40 -0
- package/__tests__/withTransaction.test.ts +67 -0
- package/lib/appData/index.js +40 -0
- package/lib/appData/index.ts +48 -0
- package/lib/concurrentTask.js +20 -0
- package/lib/concurrentTask.ts +30 -0
- package/lib/fixtures.js +28 -0
- package/lib/fixtures.ts +36 -0
- package/lib/getCollection.js +8 -0
- package/lib/getCollection.ts +4 -0
- package/lib/getMongoLocker.js +53 -0
- package/lib/getMongoLocker.ts +71 -0
- package/lib/index.js +27 -0
- package/lib/index.ts +25 -0
- package/lib/migrations/Migration.js +22 -0
- package/lib/migrations/Migration.ts +29 -0
- package/lib/migrations/MigrationsStore.js +65 -0
- package/lib/migrations/MigrationsStore.ts +73 -0
- package/lib/migrations/runMigrations.js +50 -0
- package/lib/migrations/runMigrations.ts +57 -0
- package/lib/migrations/template.js +7 -0
- package/lib/migrations/template.ts +6 -0
- package/lib/mongoClient.js +18 -0
- package/lib/mongoClient.ts +25 -0
- package/lib/mongoErrorCodes.js +11 -0
- package/lib/mongoErrorCodes.ts +10 -0
- package/lib/mongoLocker.js +17 -0
- package/lib/mongoLocker.ts +15 -0
- package/lib/withTransaction.js +21 -0
- package/lib/withTransaction.ts +24 -0
- package/package.json +40 -0
- package/tsconfig.json +14 -0
- package/types/migrate/index.d.ts +101 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { AppError } from '@storyous/errors';
|
|
2
|
+
import { Collection, ObjectId } from 'mongodb';
|
|
3
|
+
import concurrentTask from './concurrentTask';
|
|
4
|
+
import { Key } from './mongoClient';
|
|
5
|
+
import mongoErrorCodes from './mongoErrorCodes';
|
|
6
|
+
|
|
7
|
+
export type LockCallback<T> = () => Promise<T>;
|
|
8
|
+
|
|
9
|
+
export type LockerOptions = {
|
|
10
|
+
noLaterThan?: number,
|
|
11
|
+
startAttemptsDelay?: number
|
|
12
|
+
expireIn?: number
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const _removeDeprecatedIndex = async (collection: Collection) => {
|
|
16
|
+
const collectionStats = await collection.stats();
|
|
17
|
+
if (collectionStats.indexDetails.acquiredAt_1) {
|
|
18
|
+
await collection.dropIndex('acquiredAt_1');
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default async (collection: Collection) => {
|
|
23
|
+
|
|
24
|
+
await _removeDeprecatedIndex(collection);
|
|
25
|
+
await collection.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
|
|
26
|
+
|
|
27
|
+
return async <T>(
|
|
28
|
+
key: Key,
|
|
29
|
+
callback: LockCallback<T>,
|
|
30
|
+
{ noLaterThan = 1000, startAttemptsDelay = 50, expireIn = 120000 }: LockerOptions = {},
|
|
31
|
+
): Promise<T> => {
|
|
32
|
+
|
|
33
|
+
const lockId = new ObjectId();
|
|
34
|
+
|
|
35
|
+
const releaseLock = () => collection.deleteOne({ _id: key as any, lockId });
|
|
36
|
+
|
|
37
|
+
const acquireLock = async () => {
|
|
38
|
+
try {
|
|
39
|
+
const now = new Date();
|
|
40
|
+
/**
|
|
41
|
+
* 3 possible situations
|
|
42
|
+
* a) There is no lock -> upsert will create the document
|
|
43
|
+
* b) There is an expired lock -> expiresAt: { $lte: now } will match and the document will be replaced
|
|
44
|
+
* c) There is an unexpired lock -> expiresAt: { $lte: now } will not match,
|
|
45
|
+
* database will try to insert the new one, but it will fail on duplicate key (_id) error
|
|
46
|
+
*/
|
|
47
|
+
await collection.replaceOne(
|
|
48
|
+
{ _id: key as any, expiresAt: { $lte: now } },
|
|
49
|
+
// replaceOne will use the _id from filter parameter when inserting new document
|
|
50
|
+
{ lockId, expiresAt: new Date(now.getTime() + expireIn) },
|
|
51
|
+
{ upsert: true },
|
|
52
|
+
);
|
|
53
|
+
} catch (err:any) {
|
|
54
|
+
if (mongoErrorCodes.DUPLICATE_KEY.includes(err.code)) {
|
|
55
|
+
throw AppError.concurrentRequest('The lock is already acquired.');
|
|
56
|
+
}
|
|
57
|
+
await releaseLock(); // the lock could be possible acquired
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
await concurrentTask(acquireLock, { noLaterThan, startAttemptsDelay });
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
return await callback();
|
|
66
|
+
|
|
67
|
+
} finally {
|
|
68
|
+
await releaseLock();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
};
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.withTransaction = exports.runMigrations = exports.mongoLocker = exports.mongoClient = exports.MigrationsStore = exports.Migration = exports.getMongoLocker = exports.getCollection = exports.fixtures = exports.appData = void 0;
|
|
7
|
+
const appData_1 = __importDefault(require("./appData"));
|
|
8
|
+
exports.appData = appData_1.default;
|
|
9
|
+
const fixtures_1 = __importDefault(require("./fixtures"));
|
|
10
|
+
exports.fixtures = fixtures_1.default;
|
|
11
|
+
const getCollection_1 = __importDefault(require("./getCollection"));
|
|
12
|
+
exports.getCollection = getCollection_1.default;
|
|
13
|
+
const getMongoLocker_1 = __importDefault(require("./getMongoLocker"));
|
|
14
|
+
exports.getMongoLocker = getMongoLocker_1.default;
|
|
15
|
+
const Migration_1 = __importDefault(require("./migrations/Migration"));
|
|
16
|
+
exports.Migration = Migration_1.default;
|
|
17
|
+
const MigrationsStore_1 = __importDefault(require("./migrations/MigrationsStore"));
|
|
18
|
+
exports.MigrationsStore = MigrationsStore_1.default;
|
|
19
|
+
const runMigrations_1 = __importDefault(require("./migrations/runMigrations"));
|
|
20
|
+
exports.runMigrations = runMigrations_1.default;
|
|
21
|
+
const mongoClient_1 = __importDefault(require("./mongoClient"));
|
|
22
|
+
exports.mongoClient = mongoClient_1.default;
|
|
23
|
+
const mongoLocker_1 = __importDefault(require("./mongoLocker"));
|
|
24
|
+
exports.mongoLocker = mongoLocker_1.default;
|
|
25
|
+
const withTransaction_1 = __importDefault(require("./withTransaction"));
|
|
26
|
+
exports.withTransaction = withTransaction_1.default;
|
|
27
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSx3REFBZ0M7QUFZNUIsa0JBWkcsaUJBQU8sQ0FZSDtBQVhYLDBEQUFrQztBQVk5QixtQkFaRyxrQkFBUSxDQVlIO0FBWFosb0VBQTRDO0FBWXhDLHdCQVpHLHVCQUFhLENBWUg7QUFYakIsc0VBQStFO0FBWTNFLHlCQVpHLHdCQUFjLENBWUg7QUFYbEIsdUVBQStDO0FBYzNDLG9CQWRHLG1CQUFTLENBY0g7QUFiYixtRkFBMkQ7QUFjdkQsMEJBZEcseUJBQWUsQ0FjSDtBQWJuQiwrRUFBdUQ7QUFnQm5ELHdCQWhCRyx1QkFBYSxDQWdCSDtBQWZqQixnRUFBd0M7QUFhcEMsc0JBYkcscUJBQVcsQ0FhSDtBQVpmLGdFQUF3QztBQWFwQyxzQkFiRyxxQkFBVyxDQWFIO0FBWmYsd0VBQWdEO0FBYzVDLDBCQWRHLHlCQUFlLENBY0gifQ==
|
package/lib/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import appData from './appData';
|
|
2
|
+
import fixtures from './fixtures';
|
|
3
|
+
import getCollection from './getCollection';
|
|
4
|
+
import getMongoLocker, { LockCallback, LockerOptions } from './getMongoLocker';
|
|
5
|
+
import Migration from './migrations/Migration';
|
|
6
|
+
import MigrationsStore from './migrations/MigrationsStore';
|
|
7
|
+
import runMigrations from './migrations/runMigrations';
|
|
8
|
+
import mongoClient from './mongoClient';
|
|
9
|
+
import mongoLocker from './mongoLocker';
|
|
10
|
+
import withTransaction from './withTransaction';
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
appData,
|
|
14
|
+
fixtures,
|
|
15
|
+
getCollection,
|
|
16
|
+
getMongoLocker,
|
|
17
|
+
LockCallback,
|
|
18
|
+
LockerOptions,
|
|
19
|
+
Migration,
|
|
20
|
+
MigrationsStore,
|
|
21
|
+
mongoClient,
|
|
22
|
+
mongoLocker,
|
|
23
|
+
runMigrations,
|
|
24
|
+
withTransaction,
|
|
25
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class Migration {
|
|
4
|
+
constructor(description, up, down = () => null) {
|
|
5
|
+
this.description = description;
|
|
6
|
+
// NOTE 'up' and 'down' has to be as properties because of 'migrate' package
|
|
7
|
+
this.up = (next) => {
|
|
8
|
+
(async () => {
|
|
9
|
+
await up();
|
|
10
|
+
next();
|
|
11
|
+
})().catch(next);
|
|
12
|
+
};
|
|
13
|
+
this.down = (next) => {
|
|
14
|
+
(async () => {
|
|
15
|
+
await down();
|
|
16
|
+
next();
|
|
17
|
+
})().catch(next);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.default = Migration;
|
|
22
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiTWlncmF0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBRUEsTUFBcUIsU0FBUztJQVExQixZQUFhLFdBQW1CLEVBQUUsRUFBWSxFQUFFLElBQUksR0FBRyxHQUFHLEVBQUUsQ0FBQyxJQUFJO1FBQzdELElBQUksQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO1FBRS9CLDRFQUE0RTtRQUM1RSxJQUFJLENBQUMsRUFBRSxHQUFHLENBQUMsSUFBa0IsRUFBRSxFQUFFO1lBQzdCLENBQUMsS0FBSyxJQUFJLEVBQUU7Z0JBQ1IsTUFBTSxFQUFFLEVBQUUsQ0FBQztnQkFDWCxJQUFJLEVBQUUsQ0FBQztZQUNYLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3JCLENBQUMsQ0FBQztRQUVGLElBQUksQ0FBQyxJQUFJLEdBQUcsQ0FBQyxJQUFrQixFQUFFLEVBQUU7WUFDL0IsQ0FBQyxLQUFLLElBQUksRUFBRTtnQkFDUixNQUFNLElBQUksRUFBRSxDQUFDO2dCQUNiLElBQUksRUFBRSxDQUFDO1lBQ1gsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDckIsQ0FBQyxDQUFDO0lBQ04sQ0FBQztDQUNKO0FBMUJELDRCQTBCQyJ9
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
type NextCallback = (err?: Error) => void;
|
|
2
|
+
|
|
3
|
+
export default class Migration {
|
|
4
|
+
|
|
5
|
+
description: string;
|
|
6
|
+
|
|
7
|
+
up: Function;
|
|
8
|
+
|
|
9
|
+
down: Function;
|
|
10
|
+
|
|
11
|
+
constructor (description: string, up: Function, down = () => null) {
|
|
12
|
+
this.description = description;
|
|
13
|
+
|
|
14
|
+
// NOTE 'up' and 'down' has to be as properties because of 'migrate' package
|
|
15
|
+
this.up = (next: NextCallback) => {
|
|
16
|
+
(async () => {
|
|
17
|
+
await up();
|
|
18
|
+
next();
|
|
19
|
+
})().catch(next);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
this.down = (next: NextCallback) => {
|
|
23
|
+
(async () => {
|
|
24
|
+
await down();
|
|
25
|
+
next();
|
|
26
|
+
})().catch(next);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const lodash_1 = require("lodash");
|
|
7
|
+
const appData_1 = __importDefault(require("../appData"));
|
|
8
|
+
const DOCUMENT_ID = 'migrations';
|
|
9
|
+
const safeMigrationMsg = 'safeMigration is true, we do not allow empty migrations to be saved. '
|
|
10
|
+
+ 'If this is new project, feel free to enable it';
|
|
11
|
+
class MigrationsStore {
|
|
12
|
+
// eslint-disable-next-line no-empty-function
|
|
13
|
+
constructor(_logger, path, _safeMigration) {
|
|
14
|
+
this._logger = _logger;
|
|
15
|
+
this._safeMigration = _safeMigration;
|
|
16
|
+
}
|
|
17
|
+
save(set, cb) {
|
|
18
|
+
try {
|
|
19
|
+
this._logger.info('Migration SET', set);
|
|
20
|
+
const objToUpdate = (0, lodash_1.pick)(set, ['lastRun', 'migrations']);
|
|
21
|
+
if (this._safeMigration) {
|
|
22
|
+
this._checkValidMigrations(objToUpdate);
|
|
23
|
+
const numOfValidMigrations = objToUpdate.migrations
|
|
24
|
+
.reduce((prev, cur) => (cur.timestamp ? prev + 1 : prev), 0);
|
|
25
|
+
// If all migrations are renewed=without timestamp (beside the first one that is trying to be applied)
|
|
26
|
+
// There can be something wrong
|
|
27
|
+
if (objToUpdate.migrations.length >= 3 && numOfValidMigrations <= 1) {
|
|
28
|
+
this._logger.error(safeMigrationMsg);
|
|
29
|
+
throw new Error(safeMigrationMsg);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
appData_1.default.updateDocument(DOCUMENT_ID, { $set: objToUpdate }, true)
|
|
33
|
+
.then(() => { cb(null); })
|
|
34
|
+
.catch(cb);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
cb(err);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
load(cb) {
|
|
41
|
+
appData_1.default.getDocument(DOCUMENT_ID)
|
|
42
|
+
.then((state) => {
|
|
43
|
+
if (this._safeMigration) {
|
|
44
|
+
this._checkValidMigrations(state);
|
|
45
|
+
}
|
|
46
|
+
cb(null, state || {
|
|
47
|
+
lastRun: null,
|
|
48
|
+
migrations: [],
|
|
49
|
+
});
|
|
50
|
+
})
|
|
51
|
+
.catch((err) => {
|
|
52
|
+
cb(err, { migrations: [] });
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
_checkValidMigrations(objWithMigrations) {
|
|
56
|
+
if (!objWithMigrations
|
|
57
|
+
|| (0, lodash_1.isEmpty)(objWithMigrations.migrations)
|
|
58
|
+
|| !(0, lodash_1.isArray)(objWithMigrations.migrations)) {
|
|
59
|
+
this._logger.error(safeMigrationMsg);
|
|
60
|
+
throw new Error(safeMigrationMsg);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.default = MigrationsStore;
|
|
65
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uc1N0b3JlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiTWlncmF0aW9uc1N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsbUNBQWdEO0FBR2hELHlEQUFpQztBQUVqQyxNQUFNLFdBQVcsR0FBRyxZQUFZLENBQUM7QUFFakMsTUFBTSxnQkFBZ0IsR0FBRyx1RUFBdUU7TUFDMUYsZ0RBQWdELENBQUM7QUFFdkQsTUFBcUIsZUFBZTtJQUNoQyw2Q0FBNkM7SUFDN0MsWUFBcUIsT0FBZSxFQUFFLElBQVksRUFBVSxjQUF1QjtRQUE5RCxZQUFPLEdBQVAsT0FBTyxDQUFRO1FBQXdCLG1CQUFjLEdBQWQsY0FBYyxDQUFTO0lBQ25GLENBQUM7SUFFTSxJQUFJLENBQUUsR0FBVyxFQUFFLEVBQWlCO1FBQ3ZDLElBQUk7WUFDQSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDeEMsTUFBTSxXQUFXLEdBQVEsSUFBQSxhQUFJLEVBQUMsR0FBRyxFQUFFLENBQUMsU0FBUyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUM7WUFDOUQsSUFBSSxJQUFJLENBQUMsY0FBYyxFQUFFO2dCQUNyQixJQUFJLENBQUMscUJBQXFCLENBQUMsV0FBVyxDQUFDLENBQUM7Z0JBRXhDLE1BQU0sb0JBQW9CLEdBQUcsV0FBVyxDQUFDLFVBQVU7cUJBQzlDLE1BQU0sQ0FBQyxDQUFDLElBQVksRUFBRSxHQUF3QixFQUFFLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO2dCQUU5RixzR0FBc0c7Z0JBQ3RHLCtCQUErQjtnQkFDL0IsSUFBSSxXQUFXLENBQUMsVUFBVSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksb0JBQW9CLElBQUksQ0FBQyxFQUFFO29CQUNqRSxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO29CQUVyQyxNQUFNLElBQUksS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUM7aUJBQ3JDO2FBQ0o7WUFFRCxpQkFBTyxDQUFDLGNBQWMsQ0FBQyxXQUFXLEVBQUUsRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLEVBQUUsSUFBSSxDQUFDO2lCQUMzRCxJQUFJLENBQ0QsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUN0QjtpQkFDQSxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7U0FDbEI7UUFBQyxPQUFPLEdBQUcsRUFBRTtZQUNWLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQztTQUNYO0lBQ0wsQ0FBQztJQUVNLElBQUksQ0FBRSxFQUFnQjtRQUN6QixpQkFBTyxDQUFDLFdBQVcsQ0FBQyxXQUFXLENBQUM7YUFDM0IsSUFBSSxDQUFDLENBQUMsS0FBVSxFQUFFLEVBQUU7WUFDakIsSUFBSSxJQUFJLENBQUMsY0FBYyxFQUFFO2dCQUNyQixJQUFJLENBQUMscUJBQXFCLENBQUMsS0FBSyxDQUFDLENBQUM7YUFDckM7WUFFRCxFQUFFLENBQ0UsSUFBSSxFQUFFLEtBQUssSUFBSTtnQkFDWCxPQUFPLEVBQUUsSUFBSTtnQkFDYixVQUFVLEVBQUUsRUFBRTthQUNqQixDQUNKLENBQUM7UUFDTixDQUFDLENBQUM7YUFDRCxLQUFLLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRTtZQUNYLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRSxVQUFVLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNoQyxDQUFDLENBQUMsQ0FBQztJQUNYLENBQUM7SUFFTyxxQkFBcUIsQ0FBRSxpQkFBOEM7UUFDekUsSUFDSSxDQUFDLGlCQUFpQjtlQUNmLElBQUEsZ0JBQU8sRUFBQyxpQkFBaUIsQ0FBQyxVQUFVLENBQUM7ZUFDckMsQ0FBQyxJQUFBLGdCQUFPLEVBQUMsaUJBQWlCLENBQUMsVUFBVSxDQUFDLEVBQUU7WUFDM0MsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztZQUNyQyxNQUFNLElBQUksS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUM7U0FDckM7SUFDTCxDQUFDO0NBQ0o7QUE5REQsa0NBOERDIn0=
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { isArray, isEmpty, pick } from 'lodash';
|
|
2
|
+
import { CallbackError, DoneCallback } from 'migrate';
|
|
3
|
+
import { Logger } from 'winston';
|
|
4
|
+
import appData from '../appData';
|
|
5
|
+
|
|
6
|
+
const DOCUMENT_ID = 'migrations';
|
|
7
|
+
|
|
8
|
+
const safeMigrationMsg = 'safeMigration is true, we do not allow empty migrations to be saved. '
|
|
9
|
+
+ 'If this is new project, feel free to enable it';
|
|
10
|
+
|
|
11
|
+
export default class MigrationsStore {
|
|
12
|
+
// eslint-disable-next-line no-empty-function
|
|
13
|
+
constructor (private _logger: Logger, path: string, private _safeMigration: boolean) {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public save (set: Object, cb: CallbackError) {
|
|
17
|
+
try {
|
|
18
|
+
this._logger.info('Migration SET', set);
|
|
19
|
+
const objToUpdate: any = pick(set, ['lastRun', 'migrations']);
|
|
20
|
+
if (this._safeMigration) {
|
|
21
|
+
this._checkValidMigrations(objToUpdate);
|
|
22
|
+
|
|
23
|
+
const numOfValidMigrations = objToUpdate.migrations
|
|
24
|
+
.reduce((prev: number, cur: { timestamp: any; }) => (cur.timestamp ? prev + 1 : prev), 0);
|
|
25
|
+
|
|
26
|
+
// If all migrations are renewed=without timestamp (beside the first one that is trying to be applied)
|
|
27
|
+
// There can be something wrong
|
|
28
|
+
if (objToUpdate.migrations.length >= 3 && numOfValidMigrations <= 1) {
|
|
29
|
+
this._logger.error(safeMigrationMsg);
|
|
30
|
+
|
|
31
|
+
throw new Error(safeMigrationMsg);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
appData.updateDocument(DOCUMENT_ID, { $set: objToUpdate }, true)
|
|
36
|
+
.then(
|
|
37
|
+
() => { cb(null); },
|
|
38
|
+
)
|
|
39
|
+
.catch(cb);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
cb(err);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public load (cb: DoneCallback) {
|
|
46
|
+
appData.getDocument(DOCUMENT_ID)
|
|
47
|
+
.then((state: any) => {
|
|
48
|
+
if (this._safeMigration) {
|
|
49
|
+
this._checkValidMigrations(state);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
cb(
|
|
53
|
+
null, state || {
|
|
54
|
+
lastRun: null,
|
|
55
|
+
migrations: [],
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
})
|
|
59
|
+
.catch((err) => {
|
|
60
|
+
cb(err, { migrations: [] });
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private _checkValidMigrations (objWithMigrations?: { migrations: any } | null) {
|
|
65
|
+
if (
|
|
66
|
+
!objWithMigrations
|
|
67
|
+
|| isEmpty(objWithMigrations.migrations)
|
|
68
|
+
|| !isArray(objWithMigrations.migrations)) {
|
|
69
|
+
this._logger.error(safeMigrationMsg);
|
|
70
|
+
throw new Error(safeMigrationMsg);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const migrate_1 = require("migrate");
|
|
7
|
+
const MigrationsStore_1 = __importDefault(require("./MigrationsStore"));
|
|
8
|
+
/**
|
|
9
|
+
* In case of fail - logs the result and returns rejected promise
|
|
10
|
+
* @param {Logger} logger Logger
|
|
11
|
+
* @param {string} directory Path to directory where migrations are
|
|
12
|
+
* @param {boolean} continueOnError If app crashes on error or continue
|
|
13
|
+
* @param {boolean} safeMigration Do not allow to rewrite all migrations with empty array
|
|
14
|
+
*/
|
|
15
|
+
async function runMigrations(logger, directory, { continueOnError = false, safeMigration = true, }) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
(0, migrate_1.load)({
|
|
18
|
+
stateStore: new MigrationsStore_1.default(logger, directory, safeMigration),
|
|
19
|
+
migrationsDirectory: directory,
|
|
20
|
+
filterFunction: (file) => file.endsWith('.js'),
|
|
21
|
+
}, (err, set) => {
|
|
22
|
+
if (err) {
|
|
23
|
+
logger.error('Migrations did not run due to error', err);
|
|
24
|
+
reject(err);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
set.on('warning', (msg) => {
|
|
28
|
+
logger.warn('warning', msg);
|
|
29
|
+
});
|
|
30
|
+
set.on('migration', (migration, direction) => {
|
|
31
|
+
logger.info(`Running ${direction} migration`, { title: migration.title });
|
|
32
|
+
});
|
|
33
|
+
set.up((error) => {
|
|
34
|
+
if (error) {
|
|
35
|
+
logger.error('A migration ended with error', error);
|
|
36
|
+
if (!continueOnError) {
|
|
37
|
+
reject(error);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
logger.info('Migrations successfully ran');
|
|
43
|
+
}
|
|
44
|
+
resolve();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
exports.default = runMigrations;
|
|
50
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVuTWlncmF0aW9ucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInJ1bk1pZ3JhdGlvbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxxQ0FBNkM7QUFFN0Msd0VBQWdEO0FBRWhEOzs7Ozs7R0FNRztBQUNZLEtBQUssVUFBVSxhQUFhLENBQ3ZDLE1BQWMsRUFDZCxTQUFpQixFQUNqQixFQUNJLGVBQWUsR0FBRyxLQUFLLEVBQ3ZCLGFBQWEsR0FBRyxJQUFJLEdBQ3ZCO0lBRUQsT0FBTyxJQUFJLE9BQU8sQ0FBTyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtRQUN6QyxJQUFBLGNBQUksRUFDQTtZQUNJLFVBQVUsRUFBRSxJQUFJLHlCQUFlLENBQUMsTUFBTSxFQUFFLFNBQVMsRUFBRSxhQUFhLENBQUM7WUFDakUsbUJBQW1CLEVBQUUsU0FBUztZQUM5QixjQUFjLEVBQUUsQ0FBQyxJQUFZLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDO1NBQ3pELEVBQ0QsQ0FBQyxHQUFpQixFQUFFLEdBQWlCLEVBQUUsRUFBRTtZQUNyQyxJQUFJLEdBQUcsRUFBRTtnQkFDTCxNQUFNLENBQUMsS0FBSyxDQUFDLHFDQUFxQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO2dCQUN6RCxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ1osT0FBTzthQUNWO1lBRUQsR0FBRyxDQUFDLEVBQUUsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxHQUFXLEVBQUUsRUFBRTtnQkFDOUIsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDaEMsQ0FBQyxDQUFDLENBQUM7WUFFSCxHQUFHLENBQUMsRUFBRSxDQUFDLFdBQVcsRUFBRSxDQUFDLFNBQWMsRUFBRSxTQUFpQixFQUFFLEVBQUU7Z0JBQ3RELE1BQU0sQ0FBQyxJQUFJLENBQUMsV0FBVyxTQUFTLFlBQVksRUFBRSxFQUFFLEtBQUssRUFBRSxTQUFTLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUM5RSxDQUFDLENBQUMsQ0FBQztZQUVILEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxLQUFhLEVBQUUsRUFBRTtnQkFDckIsSUFBSSxLQUFLLEVBQUU7b0JBQ1AsTUFBTSxDQUFDLEtBQUssQ0FBQyw4QkFBOEIsRUFBRSxLQUFLLENBQUMsQ0FBQztvQkFDcEQsSUFBSSxDQUFDLGVBQWUsRUFBRTt3QkFDbEIsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO3dCQUNkLE9BQU87cUJBQ1Y7aUJBQ0o7cUJBQU07b0JBQ0gsTUFBTSxDQUFDLElBQUksQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO2lCQUM5QztnQkFFRCxPQUFPLEVBQUUsQ0FBQztZQUNkLENBQUMsQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxDQUFDLENBQUM7SUFDWCxDQUFDLENBQUMsQ0FBQztBQUNQLENBQUM7QUE3Q0QsZ0NBNkNDIn0=
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { load, MigrationSet } from 'migrate';
|
|
2
|
+
import { Logger } from 'winston';
|
|
3
|
+
import MigrationsStore from './MigrationsStore';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* In case of fail - logs the result and returns rejected promise
|
|
7
|
+
* @param {Logger} logger Logger
|
|
8
|
+
* @param {string} directory Path to directory where migrations are
|
|
9
|
+
* @param {boolean} continueOnError If app crashes on error or continue
|
|
10
|
+
* @param {boolean} safeMigration Do not allow to rewrite all migrations with empty array
|
|
11
|
+
*/
|
|
12
|
+
export default async function runMigrations (
|
|
13
|
+
logger: Logger,
|
|
14
|
+
directory: string,
|
|
15
|
+
{
|
|
16
|
+
continueOnError = false,
|
|
17
|
+
safeMigration = true,
|
|
18
|
+
},
|
|
19
|
+
) {
|
|
20
|
+
return new Promise<void>((resolve, reject) => {
|
|
21
|
+
load(
|
|
22
|
+
{
|
|
23
|
+
stateStore: new MigrationsStore(logger, directory, safeMigration),
|
|
24
|
+
migrationsDirectory: directory,
|
|
25
|
+
filterFunction: (file: string) => file.endsWith('.js'),
|
|
26
|
+
},
|
|
27
|
+
(err: Error | null, set: MigrationSet) => {
|
|
28
|
+
if (err) {
|
|
29
|
+
logger.error('Migrations did not run due to error', err);
|
|
30
|
+
reject(err);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
set.on('warning', (msg: string) => {
|
|
35
|
+
logger.warn('warning', msg);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
set.on('migration', (migration: any, direction: string) => {
|
|
39
|
+
logger.info(`Running ${direction} migration`, { title: migration.title });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
set.up((error?: Error) => {
|
|
43
|
+
if (error) {
|
|
44
|
+
logger.error('A migration ended with error', error);
|
|
45
|
+
if (!continueOnError) {
|
|
46
|
+
reject(error);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
logger.info('Migrations successfully ran');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
resolve();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const mongodb_1 = require("@storyous/mongodb");
|
|
3
|
+
module.exports = new mongodb_1.Migration('[ADD DESCRIPTION HERE]', async () => {
|
|
4
|
+
// add logic here
|
|
5
|
+
await (0, mongodb_1.getCollection)('exampleCollection');
|
|
6
|
+
});
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVtcGxhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ0ZW1wbGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsK0NBQTZEO0FBRTdELGlCQUFTLElBQUksbUJBQVMsQ0FBQyx3QkFBd0IsRUFBRSxLQUFLLElBQUksRUFBRTtJQUN4RCxpQkFBaUI7SUFDakIsTUFBTSxJQUFBLHVCQUFhLEVBQUMsbUJBQW1CLENBQUMsQ0FBQztBQUM3QyxDQUFDLENBQUMsQ0FBQyJ9
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const lodash_1 = require("lodash");
|
|
4
|
+
const mongodb_1 = require("mongodb");
|
|
5
|
+
const mongoClient = {
|
|
6
|
+
_client: null,
|
|
7
|
+
init(mongodbUrl, mongoOptions) {
|
|
8
|
+
this._client = new mongodb_1.MongoClient(mongodbUrl, (0, lodash_1.defaultsDeep)(mongoOptions, { directConnection: true }));
|
|
9
|
+
},
|
|
10
|
+
getClient() {
|
|
11
|
+
if (this._client) {
|
|
12
|
+
return this._client;
|
|
13
|
+
}
|
|
14
|
+
throw new Error('MongoDb must be initialized before use.');
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
exports.default = mongoClient;
|
|
18
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9uZ29DbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJtb25nb0NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLG1DQUFzQztBQUN0QyxxQ0FBb0U7QUFJcEUsTUFBTSxXQUFXLEdBQUc7SUFDaEIsT0FBTyxFQUFxQixJQUFJO0lBRWhDLElBQUksQ0FBRSxVQUFrQixFQUFFLFlBQWlDO1FBQ3ZELElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxxQkFBVyxDQUMxQixVQUFVLEVBQ1YsSUFBQSxxQkFBWSxFQUFDLFlBQVksRUFBRSxFQUFFLGdCQUFnQixFQUFFLElBQUksRUFBRSxDQUFDLENBQ3pELENBQUM7SUFDTixDQUFDO0lBRUQsU0FBUztRQUNMLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRTtZQUNkLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQztTQUN2QjtRQUVELE1BQU0sSUFBSSxLQUFLLENBQUMseUNBQXlDLENBQUMsQ0FBQztJQUMvRCxDQUFDO0NBQ0osQ0FBQztBQUVGLGtCQUFlLFdBQVcsQ0FBQyJ9
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { defaultsDeep } from 'lodash';
|
|
2
|
+
import { MongoClient, MongoClientOptions, ObjectId } from 'mongodb';
|
|
3
|
+
|
|
4
|
+
export type Key = string | number | ObjectId;
|
|
5
|
+
|
|
6
|
+
const mongoClient = {
|
|
7
|
+
_client: <MongoClient|null> null,
|
|
8
|
+
|
|
9
|
+
init (mongodbUrl: string, mongoOptions?: MongoClientOptions) {
|
|
10
|
+
this._client = new MongoClient(
|
|
11
|
+
mongodbUrl,
|
|
12
|
+
defaultsDeep(mongoOptions, { directConnection: true }),
|
|
13
|
+
);
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
getClient () {
|
|
17
|
+
if (this._client) {
|
|
18
|
+
return this._client;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw new Error('MongoDb must be initialized before use.');
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default mongoClient;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const DUPLICATE_KEY_INSERT = 11000;
|
|
4
|
+
const DUPLICATE_KEY_UPDATE = 11001;
|
|
5
|
+
const mongoErrorCodes = {
|
|
6
|
+
DUPLICATE_KEY_INSERT,
|
|
7
|
+
DUPLICATE_KEY_UPDATE,
|
|
8
|
+
DUPLICATE_KEY: [DUPLICATE_KEY_INSERT, DUPLICATE_KEY_UPDATE],
|
|
9
|
+
};
|
|
10
|
+
exports.default = mongoErrorCodes;
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9uZ29FcnJvckNvZGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsibW9uZ29FcnJvckNvZGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsTUFBTSxvQkFBb0IsR0FBRyxLQUFLLENBQUM7QUFDbkMsTUFBTSxvQkFBb0IsR0FBRyxLQUFLLENBQUM7QUFFbkMsTUFBTSxlQUFlLEdBQUc7SUFDcEIsb0JBQW9CO0lBQ3BCLG9CQUFvQjtJQUNwQixhQUFhLEVBQUUsQ0FBQyxvQkFBb0IsRUFBRSxvQkFBb0IsQ0FBQztDQUM5RCxDQUFDO0FBRUYsa0JBQWUsZUFBZSxDQUFDIn0=
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const getCollection_1 = __importDefault(require("./getCollection"));
|
|
7
|
+
const getMongoLocker_1 = __importDefault(require("./getMongoLocker"));
|
|
8
|
+
let lockerPromise;
|
|
9
|
+
async function default_1(key, callback, options = {}) {
|
|
10
|
+
if (!lockerPromise) {
|
|
11
|
+
lockerPromise = (0, getMongoLocker_1.default)((0, getCollection_1.default)('appLocks'));
|
|
12
|
+
}
|
|
13
|
+
const locker = await lockerPromise;
|
|
14
|
+
return locker(key, callback, options);
|
|
15
|
+
}
|
|
16
|
+
exports.default = default_1;
|
|
17
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9uZ29Mb2NrZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJtb25nb0xvY2tlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLG9FQUE0QztBQUM1QyxzRUFBK0U7QUFHL0UsSUFBSSxhQUFnRCxDQUFDO0FBRXRDLEtBQUssb0JBQWUsR0FBUSxFQUFFLFFBQXlCLEVBQUUsVUFBeUIsRUFBRTtJQUMvRixJQUFJLENBQUMsYUFBYSxFQUFFO1FBQ2hCLGFBQWEsR0FBRyxJQUFBLHdCQUFjLEVBQUMsSUFBQSx1QkFBYSxFQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUM7S0FDN0Q7SUFFRCxNQUFNLE1BQU0sR0FBRyxNQUFNLGFBQWEsQ0FBQztJQUVuQyxPQUFPLE1BQU0sQ0FBQyxHQUFHLEVBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQzFDLENBQUM7QUFSRCw0QkFRQyJ9
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import getCollection from './getCollection';
|
|
2
|
+
import getMongoLocker, { LockCallback, LockerOptions } from './getMongoLocker';
|
|
3
|
+
import { Key } from './mongoClient';
|
|
4
|
+
|
|
5
|
+
let lockerPromise: ReturnType<typeof getMongoLocker>;
|
|
6
|
+
|
|
7
|
+
export default async function <T> (key: Key, callback: LockCallback<T>, options: LockerOptions = {}): Promise<T> {
|
|
8
|
+
if (!lockerPromise) {
|
|
9
|
+
lockerPromise = getMongoLocker(getCollection('appLocks'));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const locker = await lockerPromise;
|
|
13
|
+
|
|
14
|
+
return locker(key, callback, options);
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const mongoClient_1 = __importDefault(require("./mongoClient"));
|
|
7
|
+
exports.default = async (callback, options) => {
|
|
8
|
+
const session = mongoClient_1.default.getClient().startSession();
|
|
9
|
+
try {
|
|
10
|
+
let result;
|
|
11
|
+
await session.withTransaction(async () => {
|
|
12
|
+
result = await callback(session);
|
|
13
|
+
}, options);
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
await session.endSession();
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aFRyYW5zYWN0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsid2l0aFRyYW5zYWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQ0EsZ0VBQXdDO0FBRXhDLGtCQUFlLEtBQUssRUFDaEIsUUFBZ0QsRUFDaEQsT0FBNEIsRUFDbEIsRUFBRTtJQUVaLE1BQU0sT0FBTyxHQUFHLHFCQUFXLENBQUMsU0FBUyxFQUFFLENBQUMsWUFBWSxFQUFFLENBQUM7SUFFdkQsSUFBSTtRQUNBLElBQUksTUFBUyxDQUFDO1FBRWQsTUFBTSxPQUFPLENBQUMsZUFBZSxDQUFDLEtBQUssSUFBSSxFQUFFO1lBQ3JDLE1BQU0sR0FBRyxNQUFNLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNyQyxDQUFDLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFWixhQUFhO1FBQ2IsT0FBTyxNQUFNLENBQUM7S0FFakI7WUFBUztRQUNOLE1BQU0sT0FBTyxDQUFDLFVBQVUsRUFBRSxDQUFDO0tBQzlCO0FBQ0wsQ0FBQyxDQUFDIn0=
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ClientSession, TransactionOptions } from 'mongodb';
|
|
2
|
+
import mongoClient from './mongoClient';
|
|
3
|
+
|
|
4
|
+
export default async <T> (
|
|
5
|
+
callback: (session: ClientSession) => Promise<T>,
|
|
6
|
+
options?: TransactionOptions,
|
|
7
|
+
): Promise<T> => {
|
|
8
|
+
|
|
9
|
+
const session = mongoClient.getClient().startSession();
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
let result: T;
|
|
13
|
+
|
|
14
|
+
await session.withTransaction(async () => {
|
|
15
|
+
result = await callback(session);
|
|
16
|
+
}, options);
|
|
17
|
+
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
return result;
|
|
20
|
+
|
|
21
|
+
} finally {
|
|
22
|
+
await session.endSession();
|
|
23
|
+
}
|
|
24
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@storyous/mongodb",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"homepage": "https://github.com/Storyous/mono-utils#readme",
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"main": "lib/index.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"start": "tsc",
|
|
10
|
+
"build": "tsc && npm publish"
|
|
11
|
+
},
|
|
12
|
+
"mocha": {
|
|
13
|
+
"recursive": true,
|
|
14
|
+
"timeout": 15000,
|
|
15
|
+
"extension": [
|
|
16
|
+
"ts"
|
|
17
|
+
],
|
|
18
|
+
"exit": true,
|
|
19
|
+
"require": "ts-node/register"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/Storyous/mono-utils.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/Storyous/mono-utils/issues"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"registry": "https://registry.npmjs.org/"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@storyous/errors": "^1.0.0",
|
|
34
|
+
"lodash": "^4.17.21",
|
|
35
|
+
"migrate": "^1.8.0",
|
|
36
|
+
"mongodb": "^4.5.0",
|
|
37
|
+
"winston": "^3.7.2"
|
|
38
|
+
},
|
|
39
|
+
"gitHead": "2ba0209d33ac7cd2fd1136a9123134f656750102"
|
|
40
|
+
}
|