@webiny/data-migration 0.0.0-unstable.13771d80a8

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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/MigrationRunner.d.ts +18 -0
  3. package/MigrationRunner.js +276 -0
  4. package/MigrationRunner.js.map +1 -0
  5. package/README.md +6 -0
  6. package/cli/getDuration.d.ts +5 -0
  7. package/cli/getDuration.js +21 -0
  8. package/cli/getDuration.js.map +1 -0
  9. package/cli/getMigrationStatus.d.ts +9 -0
  10. package/cli/getMigrationStatus.js +23 -0
  11. package/cli/getMigrationStatus.js.map +1 -0
  12. package/cli/index.d.ts +3 -0
  13. package/cli/index.js +38 -0
  14. package/cli/index.js.map +1 -0
  15. package/cli/printReport.d.ts +9 -0
  16. package/cli/printReport.js +57 -0
  17. package/cli/printReport.js.map +1 -0
  18. package/cli/runMigration.d.ts +13 -0
  19. package/cli/runMigration.js +85 -0
  20. package/cli/runMigration.js.map +1 -0
  21. package/createId.d.ts +1 -0
  22. package/createId.js +14 -0
  23. package/createId.js.map +1 -0
  24. package/createPinoLogger.d.ts +7 -0
  25. package/createPinoLogger.js +22 -0
  26. package/createPinoLogger.js.map +1 -0
  27. package/createTable.d.ts +7 -0
  28. package/createTable.js +29 -0
  29. package/createTable.js.map +1 -0
  30. package/executeWithRetry.d.ts +2 -0
  31. package/executeWithRetry.js +19 -0
  32. package/executeWithRetry.js.map +1 -0
  33. package/handlers/createDdbEsProjectMigration.d.ts +16 -0
  34. package/handlers/createDdbEsProjectMigration.js +79 -0
  35. package/handlers/createDdbEsProjectMigration.js.map +1 -0
  36. package/handlers/createDdbProjectMigration.d.ts +13 -0
  37. package/handlers/createDdbProjectMigration.js +75 -0
  38. package/handlers/createDdbProjectMigration.js.map +1 -0
  39. package/handlers/createPatternMatcher.d.ts +2 -0
  40. package/handlers/createPatternMatcher.js +17 -0
  41. package/handlers/createPatternMatcher.js.map +1 -0
  42. package/handlers/devVersionErrorResponse.d.ts +5 -0
  43. package/handlers/devVersionErrorResponse.js +14 -0
  44. package/handlers/devVersionErrorResponse.js.map +1 -0
  45. package/index.d.ts +7 -0
  46. package/index.js +71 -0
  47. package/index.js.map +1 -0
  48. package/package.json +60 -0
  49. package/repository/createStandardEntity.d.ts +5 -0
  50. package/repository/createStandardEntity.js +37 -0
  51. package/repository/createStandardEntity.js.map +1 -0
  52. package/repository/migrations.repository.d.ts +17 -0
  53. package/repository/migrations.repository.js +108 -0
  54. package/repository/migrations.repository.js.map +1 -0
  55. package/symbols.d.ts +7 -0
  56. package/symbols.js +20 -0
  57. package/symbols.js.map +1 -0
  58. package/types.d.ts +80 -0
  59. package/types.js +12 -0
  60. package/types.js.map +1 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Webiny
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,18 @@
1
+ import { Logger } from "pino";
2
+ import { MigrationRepository, DataMigration, ExecutionTimeLimiter, MigrationStatus } from "./types";
3
+ export declare type IsMigrationApplicable = (migration: DataMigration) => boolean;
4
+ export declare class MigrationRunner {
5
+ private readonly logger;
6
+ private readonly migrations;
7
+ private readonly repository;
8
+ private readonly timeLimiter;
9
+ constructor(repository: MigrationRepository, timeLimiter: ExecutionTimeLimiter, migrations: DataMigration[], logger: Logger | undefined);
10
+ execute(projectVersion: string, isApplicable?: IsMigrationApplicable): Promise<void>;
11
+ getStatus(): Promise<MigrationStatus>;
12
+ private validateIds;
13
+ private createCheckpoint;
14
+ private getOrCreateRun;
15
+ private getOrCreateRunItem;
16
+ private setRunItem;
17
+ private setRunItemAndSave;
18
+ }
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.MigrationRunner = void 0;
8
+ var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
+ var _ioc = require("@webiny/ioc");
11
+ var _semver = require("semver");
12
+ var _symbols = require("./symbols");
13
+ var _createPinoLogger = require("./createPinoLogger");
14
+ var _executeWithRetry = require("./executeWithRetry");
15
+ var _createId = require("./createId");
16
+ const getCurrentISOTime = () => {
17
+ return new Date().toISOString();
18
+ };
19
+ const getRunItemDuration = runItem => {
20
+ if (!runItem.startedOn || !runItem.finishedOn) {
21
+ return "N/A";
22
+ }
23
+ return new Date(runItem.finishedOn).getTime() - new Date(runItem.startedOn).getTime();
24
+ };
25
+ class MigrationNotFinished extends Error {}
26
+ class MigrationInProgress extends Error {}
27
+ class MigrationRunner {
28
+ constructor(repository, timeLimiter, migrations, logger) {
29
+ (0, _defineProperty2.default)(this, "logger", void 0);
30
+ (0, _defineProperty2.default)(this, "migrations", void 0);
31
+ (0, _defineProperty2.default)(this, "repository", void 0);
32
+ (0, _defineProperty2.default)(this, "timeLimiter", void 0);
33
+ this.repository = repository;
34
+ this.timeLimiter = timeLimiter;
35
+ this.migrations = migrations || [];
36
+ if (!logger) {
37
+ logger = (0, _createPinoLogger.createPinoLogger)();
38
+ }
39
+ this.logger = logger;
40
+ }
41
+ async execute(projectVersion, isApplicable) {
42
+ const lastRun = await this.getOrCreateRun();
43
+ try {
44
+ this.validateIds(this.migrations);
45
+ } catch (err) {
46
+ lastRun.status = "error";
47
+ lastRun.error = {
48
+ message: err.message
49
+ };
50
+ await this.repository.saveRun(lastRun);
51
+ return;
52
+ }
53
+ const [latestMigration] = await this.repository.listMigrations({
54
+ limit: 1
55
+ });
56
+ this.logger.info(`Project version is %s.`, projectVersion);
57
+
58
+ // Get current version, and coerce it to a valid SemVer.
59
+ // With this, we can run migrations targeted for stable versions, released under a preid tag (e.g., `beta`).
60
+ const currentVersion = (0, _semver.coerce)(projectVersion) + "";
61
+ const startingId = latestMigration ? latestMigration.id : `${currentVersion}-000`;
62
+ const lastId = `${currentVersion}-999`;
63
+
64
+ // Create initial migration record.
65
+ if (!latestMigration) {
66
+ this.logger.info(`No migrations were ever executed. Creating initial migration record %s.`, startingId);
67
+ await this.repository.logMigration({
68
+ id: startingId,
69
+ description: "starting point for applicable migrations detection",
70
+ startedOn: getCurrentISOTime(),
71
+ finishedOn: getCurrentISOTime(),
72
+ reason: "initial migration"
73
+ });
74
+ } else {
75
+ this.logger.info(`Latest migration ID is %s.`, latestMigration.id);
76
+ }
77
+ if (isApplicable) {
78
+ this.logger.info(`Using custom "isApplicable" function.`);
79
+ } else {
80
+ this.logger.info(`Using migrations in the range of %s to %s.`, startingId, lastId);
81
+ }
82
+ const defaultIsApplicable = mig => {
83
+ return mig.getId() > startingId && mig.getId() <= lastId;
84
+ };
85
+ const isMigrationApplicable = isApplicable || defaultIsApplicable;
86
+ const executableMigrations = this.migrations.filter(mig => {
87
+ if (!isMigrationApplicable(mig)) {
88
+ this.setRunItem(lastRun, {
89
+ id: mig.getId(),
90
+ status: "not-applicable"
91
+ });
92
+ return false;
93
+ }
94
+ return true;
95
+ }).sort((a, b) => a.getId() > b.getId() ? 1 : -1);
96
+ this.logger.info(`Found %s applicable out of %s available migration(s).`, executableMigrations.length, this.migrations.length);
97
+
98
+ // Are we're within the last 2 minutes of the execution time limit?
99
+ const shouldCreateCheckpoint = () => {
100
+ return this.timeLimiter() < 120000;
101
+ };
102
+ for (const migration of executableMigrations) {
103
+ const runItem = this.getOrCreateRunItem(lastRun, migration);
104
+ const checkpoint = await this.repository.getCheckpoint(migration.getId());
105
+ const logger = (0, _createPinoLogger.getChildLogger)(this.logger, migration);
106
+ if (checkpoint) {
107
+ this.logger.info(checkpoint, `Found checkpoint ${migration.getId()}.`);
108
+ }
109
+ const context = {
110
+ projectVersion,
111
+ logger,
112
+ checkpoint,
113
+ runningOutOfTime: shouldCreateCheckpoint,
114
+ createCheckpoint: async data => {
115
+ await this.createCheckpoint(migration, data);
116
+ },
117
+ createCheckpointAndExit: async data => {
118
+ await this.createCheckpoint(migration, data);
119
+ // We throw an error to break out of the migration execution completely.
120
+ throw new MigrationNotFinished();
121
+ }
122
+ };
123
+ const shouldExecute = checkpoint ? true : await migration.shouldExecute(context);
124
+ if (!shouldExecute) {
125
+ this.logger.info(`Skipping migration %s.`, migration.getId());
126
+ runItem.status = "skipped";
127
+ await this.setRunItemAndSave(lastRun, runItem);
128
+ await this.repository.logMigration({
129
+ id: migration.getId(),
130
+ description: migration.getDescription(),
131
+ reason: "skipped"
132
+ });
133
+ continue;
134
+ }
135
+ try {
136
+ lastRun.status = "running";
137
+ runItem.status = "running";
138
+ if (!runItem.startedOn) {
139
+ runItem.startedOn = getCurrentISOTime();
140
+ }
141
+ await this.setRunItemAndSave(lastRun, runItem);
142
+ this.logger.info(`Executing migration %s: %s`, migration.getId(), migration.getDescription());
143
+ await migration.execute(context);
144
+ runItem.status = "done";
145
+ } catch (err) {
146
+ // If `MigrationNotFinished` was thrown, we will need to resume the migration.
147
+ if (err instanceof MigrationNotFinished) {
148
+ lastRun.status = "pending";
149
+ runItem.status = "pending";
150
+ return;
151
+ }
152
+ runItem.status = "error";
153
+ lastRun.status = "error";
154
+ lastRun.error = {
155
+ name: err.name || "Migration error",
156
+ message: err.message,
157
+ stack: err.stack,
158
+ data: err.data,
159
+ code: err.code
160
+ };
161
+ this.logger.error(err, err.message);
162
+ return;
163
+ } finally {
164
+ // We sum duration from the previous run with the current run.
165
+ runItem.finishedOn = getCurrentISOTime();
166
+
167
+ // Update run stats.
168
+ await this.setRunItemAndSave(lastRun, runItem);
169
+ this.logger.info(`Finished executing migration %s in %sms.`, migration.getId(), getRunItemDuration(runItem));
170
+ }
171
+ await this.repository.logMigration({
172
+ id: migration.getId(),
173
+ description: migration.getDescription(),
174
+ startedOn: runItem.startedOn,
175
+ finishedOn: runItem.finishedOn,
176
+ reason: "executed"
177
+ });
178
+ this.logger.info(`Deleting checkpoint ${migration.getId()}.`);
179
+ await this.repository.deleteCheckpoint(migration.getId());
180
+ }
181
+ lastRun.status = "done";
182
+ lastRun.finishedOn = getCurrentISOTime();
183
+ await this.repository.saveRun(lastRun);
184
+ this.logger.info(`Finished processing applicable migrations.`);
185
+ }
186
+ async getStatus() {
187
+ const lastRun = await this.repository.getLastRun();
188
+ if (!lastRun) {
189
+ throw new Error(`No migrations were ever executed!`);
190
+ }
191
+
192
+ // Since we don't store migration descriptions to DB, we need to fetch them from actual migration objects.
193
+ const withDescriptions = lastRun.migrations.map(mig => {
194
+ const dataMigration = this.migrations.find(dm => dm.getId() === mig.id);
195
+ return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, mig), {}, {
196
+ description: dataMigration ? dataMigration.getDescription() : "N/A"
197
+ });
198
+ });
199
+ return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, lastRun), {}, {
200
+ migrations: withDescriptions
201
+ });
202
+ }
203
+ validateIds(migrations) {
204
+ const ids = new Set();
205
+ for (const mig of migrations) {
206
+ const id = mig.getId();
207
+ if (id.endsWith("-000")) {
208
+ const error = new Error(`Migration ID must not end with "000": ${id}`);
209
+ this.logger.error(error);
210
+ throw error;
211
+ }
212
+ if (ids.has(id)) {
213
+ const error = new Error(`Duplicate migration ID found: ${id}`);
214
+ this.logger.error(error);
215
+ throw error;
216
+ }
217
+ ids.add(id);
218
+ }
219
+ }
220
+ async createCheckpoint(migration, checkpoint) {
221
+ this.logger.info(checkpoint, `Saving checkpoint ${migration.getId()}`);
222
+ const execute = () => this.repository.createCheckpoint(migration.getId(), checkpoint);
223
+ await (0, _executeWithRetry.executeWithRetry)(execute);
224
+ }
225
+ async getOrCreateRun() {
226
+ const resolvedStatus = ["done", "error"];
227
+ const unresolvedStatus = ["init", "running"];
228
+ let lastRun = await this.repository.getLastRun();
229
+ if (lastRun && unresolvedStatus.includes(lastRun.status)) {
230
+ throw new MigrationInProgress(`Migration is already in progress (ID: ${lastRun.id})!`);
231
+ }
232
+ if (!lastRun || resolvedStatus.includes(lastRun.status)) {
233
+ lastRun = {
234
+ id: (0, _createId.createId)(),
235
+ status: "init",
236
+ startedOn: getCurrentISOTime(),
237
+ finishedOn: "",
238
+ migrations: []
239
+ };
240
+ await this.repository.saveRun(lastRun);
241
+ }
242
+ return lastRun;
243
+ }
244
+ getOrCreateRunItem(run, migration) {
245
+ const existingItem = run.migrations.find(item => item.id === migration.getId());
246
+ if (existingItem) {
247
+ return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, existingItem), {}, {
248
+ status: "running"
249
+ });
250
+ }
251
+ return {
252
+ id: migration.getId(),
253
+ status: "running"
254
+ };
255
+ }
256
+ setRunItem(run, item) {
257
+ const index = run.migrations.findIndex(runItem => runItem.id === item.id);
258
+ if (index < 0) {
259
+ run.migrations.push(item);
260
+ } else {
261
+ run.migrations = [...run.migrations.slice(0, index), item, ...run.migrations.slice(index + 1)];
262
+ }
263
+ run.migrations = run.migrations.sort((a, b) => a.id > b.id ? 1 : -1);
264
+ }
265
+ async setRunItemAndSave(run, item) {
266
+ this.setRunItem(run, item);
267
+ await this.repository.saveRun(run);
268
+ }
269
+ }
270
+ exports.MigrationRunner = MigrationRunner;
271
+ (0, _ioc.makeInjectable)(MigrationRunner, [(0, _ioc.inject)(_symbols.MigrationRepositorySymbol), (0, _ioc.inject)(_symbols.ExecutionTimeLimiterSymbol), (0, _ioc.inject)(_symbols.MigrationSymbol, {
272
+ multi: true,
273
+ optional: true
274
+ }), (0, _ioc.inject)(_symbols.LoggerSymbol, {
275
+ optional: true
276
+ })]);
@@ -0,0 +1 @@
1
+ {"version":3,"names":["getCurrentISOTime","Date","toISOString","getRunItemDuration","runItem","startedOn","finishedOn","getTime","MigrationNotFinished","Error","MigrationInProgress","MigrationRunner","constructor","repository","timeLimiter","migrations","logger","createPinoLogger","execute","projectVersion","isApplicable","lastRun","getOrCreateRun","validateIds","err","status","error","message","saveRun","latestMigration","listMigrations","limit","info","currentVersion","coerce","startingId","id","lastId","logMigration","description","reason","defaultIsApplicable","mig","getId","isMigrationApplicable","executableMigrations","filter","setRunItem","sort","a","b","length","shouldCreateCheckpoint","migration","getOrCreateRunItem","checkpoint","getCheckpoint","getChildLogger","context","runningOutOfTime","createCheckpoint","data","createCheckpointAndExit","shouldExecute","setRunItemAndSave","getDescription","name","stack","code","deleteCheckpoint","getStatus","getLastRun","withDescriptions","map","dataMigration","find","dm","ids","Set","endsWith","has","add","executeWithRetry","resolvedStatus","unresolvedStatus","includes","createId","run","existingItem","item","index","findIndex","push","slice","makeInjectable","inject","MigrationRepositorySymbol","ExecutionTimeLimiterSymbol","MigrationSymbol","multi","optional","LoggerSymbol"],"sources":["MigrationRunner.ts"],"sourcesContent":["import { Logger } from \"pino\";\nimport { inject, makeInjectable } from \"@webiny/ioc\";\nimport { coerce } from \"semver\";\nimport {\n MigrationRepositorySymbol,\n LoggerSymbol,\n MigrationSymbol,\n ExecutionTimeLimiterSymbol\n} from \"./symbols\";\nimport { createPinoLogger, getChildLogger } from \"./createPinoLogger\";\nimport {\n MigrationRepository,\n DataMigration,\n DataMigrationContext,\n ExecutionTimeLimiter,\n MigrationRun,\n MigrationStatus,\n MigrationRunItem\n} from \"~/types\";\nimport { executeWithRetry } from \"./executeWithRetry\";\nimport { createId } from \"~/createId\";\n\nexport type IsMigrationApplicable = (migration: DataMigration) => boolean;\n\nconst getCurrentISOTime = () => {\n return new Date().toISOString();\n};\n\nconst getRunItemDuration = (runItem: MigrationRunItem) => {\n if (!runItem.startedOn || !runItem.finishedOn) {\n return \"N/A\";\n }\n\n return new Date(runItem.finishedOn).getTime() - new Date(runItem.startedOn).getTime();\n};\n\nclass MigrationNotFinished extends Error {}\nclass MigrationInProgress extends Error {}\n\nexport class MigrationRunner {\n private readonly logger: Logger;\n private readonly migrations: DataMigration[];\n private readonly repository: MigrationRepository;\n private readonly timeLimiter: ExecutionTimeLimiter;\n\n constructor(\n repository: MigrationRepository,\n timeLimiter: ExecutionTimeLimiter,\n migrations: DataMigration[],\n logger: Logger | undefined\n ) {\n this.repository = repository;\n this.timeLimiter = timeLimiter;\n this.migrations = migrations || [];\n\n if (!logger) {\n logger = createPinoLogger();\n }\n this.logger = logger;\n }\n\n async execute(projectVersion: string, isApplicable?: IsMigrationApplicable) {\n const lastRun = await this.getOrCreateRun();\n\n try {\n this.validateIds(this.migrations);\n } catch (err) {\n lastRun.status = \"error\";\n lastRun.error = {\n message: err.message\n };\n await this.repository.saveRun(lastRun);\n return;\n }\n\n const [latestMigration] = await this.repository.listMigrations({ limit: 1 });\n\n this.logger.info(`Project version is %s.`, projectVersion);\n\n // Get current version, and coerce it to a valid SemVer.\n // With this, we can run migrations targeted for stable versions, released under a preid tag (e.g., `beta`).\n const currentVersion = coerce(projectVersion) + \"\";\n const startingId = latestMigration ? latestMigration.id : `${currentVersion}-000`;\n const lastId = `${currentVersion}-999`;\n\n // Create initial migration record.\n if (!latestMigration) {\n this.logger.info(\n `No migrations were ever executed. Creating initial migration record %s.`,\n startingId\n );\n await this.repository.logMigration({\n id: startingId,\n description: \"starting point for applicable migrations detection\",\n startedOn: getCurrentISOTime(),\n finishedOn: getCurrentISOTime(),\n reason: \"initial migration\"\n });\n } else {\n this.logger.info(`Latest migration ID is %s.`, latestMigration.id);\n }\n\n if (isApplicable) {\n this.logger.info(`Using custom \"isApplicable\" function.`);\n } else {\n this.logger.info(`Using migrations in the range of %s to %s.`, startingId, lastId);\n }\n\n const defaultIsApplicable: IsMigrationApplicable = mig => {\n return mig.getId() > startingId && mig.getId() <= lastId;\n };\n\n const isMigrationApplicable = isApplicable || defaultIsApplicable;\n\n const executableMigrations = this.migrations\n .filter(mig => {\n if (!isMigrationApplicable(mig)) {\n this.setRunItem(lastRun, {\n id: mig.getId(),\n status: \"not-applicable\"\n });\n\n return false;\n }\n return true;\n })\n .sort((a, b) => (a.getId() > b.getId() ? 1 : -1));\n\n this.logger.info(\n `Found %s applicable out of %s available migration(s).`,\n executableMigrations.length,\n this.migrations.length\n );\n\n // Are we're within the last 2 minutes of the execution time limit?\n const shouldCreateCheckpoint = () => {\n return this.timeLimiter() < 120000;\n };\n\n for (const migration of executableMigrations) {\n const runItem = this.getOrCreateRunItem(lastRun, migration);\n const checkpoint = await this.repository.getCheckpoint(migration.getId());\n const logger = getChildLogger(this.logger, migration);\n\n if (checkpoint) {\n this.logger.info(checkpoint, `Found checkpoint ${migration.getId()}.`);\n }\n\n const context: DataMigrationContext = {\n projectVersion,\n logger,\n checkpoint,\n runningOutOfTime: shouldCreateCheckpoint,\n createCheckpoint: async (data: unknown) => {\n await this.createCheckpoint(migration, data);\n },\n createCheckpointAndExit: async (data: unknown) => {\n await this.createCheckpoint(migration, data);\n // We throw an error to break out of the migration execution completely.\n throw new MigrationNotFinished();\n }\n };\n const shouldExecute = checkpoint ? true : await migration.shouldExecute(context);\n\n if (!shouldExecute) {\n this.logger.info(`Skipping migration %s.`, migration.getId());\n runItem.status = \"skipped\";\n\n await this.setRunItemAndSave(lastRun, runItem);\n\n await this.repository.logMigration({\n id: migration.getId(),\n description: migration.getDescription(),\n reason: \"skipped\"\n });\n\n continue;\n }\n\n try {\n lastRun.status = \"running\";\n runItem.status = \"running\";\n if (!runItem.startedOn) {\n runItem.startedOn = getCurrentISOTime();\n }\n await this.setRunItemAndSave(lastRun, runItem);\n this.logger.info(\n `Executing migration %s: %s`,\n migration.getId(),\n migration.getDescription()\n );\n await migration.execute(context);\n runItem.status = \"done\";\n } catch (err) {\n // If `MigrationNotFinished` was thrown, we will need to resume the migration.\n if (err instanceof MigrationNotFinished) {\n lastRun.status = \"pending\";\n runItem.status = \"pending\";\n return;\n }\n\n runItem.status = \"error\";\n lastRun.status = \"error\";\n lastRun.error = {\n name: err.name || \"Migration error\",\n message: err.message,\n stack: err.stack,\n data: err.data,\n code: err.code\n };\n this.logger.error(err, err.message);\n return;\n } finally {\n // We sum duration from the previous run with the current run.\n runItem.finishedOn = getCurrentISOTime();\n\n // Update run stats.\n await this.setRunItemAndSave(lastRun, runItem);\n\n this.logger.info(\n `Finished executing migration %s in %sms.`,\n migration.getId(),\n getRunItemDuration(runItem)\n );\n }\n\n await this.repository.logMigration({\n id: migration.getId(),\n description: migration.getDescription(),\n startedOn: runItem.startedOn,\n finishedOn: runItem.finishedOn,\n reason: \"executed\"\n });\n\n this.logger.info(`Deleting checkpoint ${migration.getId()}.`);\n await this.repository.deleteCheckpoint(migration.getId());\n }\n\n lastRun.status = \"done\";\n lastRun.finishedOn = getCurrentISOTime();\n await this.repository.saveRun(lastRun);\n\n this.logger.info(`Finished processing applicable migrations.`);\n }\n\n async getStatus(): Promise<MigrationStatus> {\n const lastRun = await this.repository.getLastRun();\n if (!lastRun) {\n throw new Error(`No migrations were ever executed!`);\n }\n\n // Since we don't store migration descriptions to DB, we need to fetch them from actual migration objects.\n const withDescriptions = lastRun.migrations.map(mig => {\n const dataMigration = this.migrations.find(dm => dm.getId() === mig.id);\n return {\n ...mig,\n description: dataMigration ? dataMigration.getDescription() : \"N/A\"\n };\n });\n\n return { ...lastRun, migrations: withDescriptions };\n }\n\n private validateIds(migrations: DataMigration[]) {\n const ids = new Set();\n for (const mig of migrations) {\n const id = mig.getId();\n if (id.endsWith(\"-000\")) {\n const error = new Error(`Migration ID must not end with \"000\": ${id}`);\n this.logger.error(error);\n throw error;\n }\n\n if (ids.has(id)) {\n const error = new Error(`Duplicate migration ID found: ${id}`);\n this.logger.error(error);\n throw error;\n }\n ids.add(id);\n }\n }\n\n private async createCheckpoint(migration: DataMigration, checkpoint: unknown) {\n this.logger.info(checkpoint, `Saving checkpoint ${migration.getId()}`);\n const execute = () => this.repository.createCheckpoint(migration.getId(), checkpoint);\n await executeWithRetry(execute);\n }\n\n private async getOrCreateRun() {\n const resolvedStatus: Array<MigrationRun[\"status\"]> = [\"done\", \"error\"];\n const unresolvedStatus: Array<MigrationRun[\"status\"]> = [\"init\", \"running\"];\n\n let lastRun = await this.repository.getLastRun();\n\n if (lastRun && unresolvedStatus.includes(lastRun.status)) {\n throw new MigrationInProgress(`Migration is already in progress (ID: ${lastRun.id})!`);\n }\n\n if (!lastRun || resolvedStatus.includes(lastRun.status)) {\n lastRun = {\n id: createId(),\n status: \"init\",\n startedOn: getCurrentISOTime(),\n finishedOn: \"\",\n migrations: []\n };\n\n await this.repository.saveRun(lastRun);\n }\n\n return lastRun;\n }\n\n private getOrCreateRunItem(run: MigrationRun, migration: DataMigration): MigrationRunItem {\n const existingItem = run.migrations.find(item => item.id === migration.getId());\n if (existingItem) {\n return {\n ...existingItem,\n status: \"running\"\n };\n }\n\n return {\n id: migration.getId(),\n status: \"running\"\n };\n }\n\n private setRunItem(run: MigrationRun, item: MigrationRunItem) {\n const index = run.migrations.findIndex(runItem => runItem.id === item.id);\n if (index < 0) {\n run.migrations.push(item);\n } else {\n run.migrations = [\n ...run.migrations.slice(0, index),\n item,\n ...run.migrations.slice(index + 1)\n ];\n }\n\n run.migrations = run.migrations.sort((a, b) => (a.id > b.id ? 1 : -1));\n }\n\n private async setRunItemAndSave(run: MigrationRun, item: MigrationRunItem) {\n this.setRunItem(run, item);\n await this.repository.saveRun(run);\n }\n}\n\nmakeInjectable(MigrationRunner, [\n inject(MigrationRepositorySymbol),\n inject(ExecutionTimeLimiterSymbol),\n inject(MigrationSymbol, { multi: true, optional: true }),\n inject(LoggerSymbol, { optional: true })\n]);\n"],"mappings":"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"}
package/README.md ADDED
@@ -0,0 +1,6 @@
1
+ # @webiny/data-migration
2
+
3
+ [![](https://img.shields.io/npm/dw/@webiny/data-migration.svg)](https://www.npmjs.com/package/@webiny/data-migration)
4
+ [![](https://img.shields.io/npm/v/@webiny/data-migration.svg)](https://www.npmjs.com/package/@webiny/data-migration)
5
+ [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
6
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Get duration since the given ISO timestamp.
3
+ * @param string since
4
+ */
5
+ export declare const getDuration: (since: string) => string;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getDuration = void 0;
7
+ /**
8
+ * Get duration since the given ISO timestamp.
9
+ * @param string since
10
+ */
11
+ const getDuration = since => {
12
+ const ms = new Date().getTime() - new Date(since).getTime();
13
+ let seconds = Math.floor(ms / 1000);
14
+ let minutes = undefined;
15
+ if (seconds > 60) {
16
+ minutes = Math.floor(seconds / 60);
17
+ seconds = Math.floor(seconds % 60);
18
+ }
19
+ return minutes ? `${minutes}m ${seconds}s` : `${seconds}s`;
20
+ };
21
+ exports.getDuration = getDuration;
@@ -0,0 +1 @@
1
+ {"version":3,"names":["getDuration","since","ms","Date","getTime","seconds","Math","floor","minutes","undefined"],"sources":["getDuration.ts"],"sourcesContent":["/**\n * Get duration since the given ISO timestamp.\n * @param string since\n */\nexport const getDuration = (since: string) => {\n const ms = new Date().getTime() - new Date(since).getTime();\n let seconds = Math.floor(ms / 1000);\n let minutes = undefined;\n if (seconds > 60) {\n minutes = Math.floor(seconds / 60);\n seconds = Math.floor(seconds % 60);\n }\n\n return minutes ? `${minutes}m ${seconds}s` : `${seconds}s`;\n};\n"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACO,MAAMA,WAAW,GAAIC,KAAa,IAAK;EAC1C,MAAMC,EAAE,GAAG,IAAIC,IAAI,EAAE,CAACC,OAAO,EAAE,GAAG,IAAID,IAAI,CAACF,KAAK,CAAC,CAACG,OAAO,EAAE;EAC3D,IAAIC,OAAO,GAAGC,IAAI,CAACC,KAAK,CAACL,EAAE,GAAG,IAAI,CAAC;EACnC,IAAIM,OAAO,GAAGC,SAAS;EACvB,IAAIJ,OAAO,GAAG,EAAE,EAAE;IACdG,OAAO,GAAGF,IAAI,CAACC,KAAK,CAACF,OAAO,GAAG,EAAE,CAAC;IAClCA,OAAO,GAAGC,IAAI,CAACC,KAAK,CAACF,OAAO,GAAG,EAAE,CAAC;EACtC;EAEA,OAAOG,OAAO,GAAI,GAAEA,OAAQ,KAAIH,OAAQ,GAAE,GAAI,GAAEA,OAAQ,GAAE;AAC9D,CAAC;AAAC"}
@@ -0,0 +1,9 @@
1
+ import LambdaClient from "aws-sdk/clients/lambda";
2
+ import { MigrationEventHandlerResponse } from "../types";
3
+ interface GetMigrationStatusParams {
4
+ lambdaClient: LambdaClient;
5
+ functionName: string;
6
+ payload?: Record<string, any>;
7
+ }
8
+ export declare const getMigrationStatus: ({ payload, functionName, lambdaClient }: GetMigrationStatusParams) => Promise<MigrationEventHandlerResponse>;
9
+ export {};
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.getMigrationStatus = void 0;
8
+ var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
9
+ const getMigrationStatus = async ({
10
+ payload,
11
+ functionName,
12
+ lambdaClient
13
+ }) => {
14
+ const response = await lambdaClient.invoke({
15
+ FunctionName: functionName,
16
+ InvocationType: "RequestResponse",
17
+ Payload: JSON.stringify((0, _objectSpread2.default)((0, _objectSpread2.default)({}, payload), {}, {
18
+ command: "status"
19
+ }))
20
+ }).promise();
21
+ return JSON.parse(response.Payload);
22
+ };
23
+ exports.getMigrationStatus = getMigrationStatus;
@@ -0,0 +1 @@
1
+ {"version":3,"names":["getMigrationStatus","payload","functionName","lambdaClient","response","invoke","FunctionName","InvocationType","Payload","JSON","stringify","command","promise","parse"],"sources":["getMigrationStatus.ts"],"sourcesContent":["import LambdaClient from \"aws-sdk/clients/lambda\";\nimport { MigrationEventHandlerResponse } from \"~/types\";\n\ninterface GetMigrationStatusParams {\n lambdaClient: LambdaClient;\n functionName: string;\n payload?: Record<string, any>;\n}\n\nexport const getMigrationStatus = async ({\n payload,\n functionName,\n lambdaClient\n}: GetMigrationStatusParams) => {\n const response = await lambdaClient\n .invoke({\n FunctionName: functionName,\n InvocationType: \"RequestResponse\",\n Payload: JSON.stringify({ ...payload, command: \"status\" })\n })\n .promise();\n\n return JSON.parse(response.Payload as string) as MigrationEventHandlerResponse;\n};\n"],"mappings":";;;;;;;;AASO,MAAMA,kBAAkB,GAAG,OAAO;EACrCC,OAAO;EACPC,YAAY;EACZC;AACsB,CAAC,KAAK;EAC5B,MAAMC,QAAQ,GAAG,MAAMD,YAAY,CAC9BE,MAAM,CAAC;IACJC,YAAY,EAAEJ,YAAY;IAC1BK,cAAc,EAAE,iBAAiB;IACjCC,OAAO,EAAEC,IAAI,CAACC,SAAS,6DAAMT,OAAO;MAAEU,OAAO,EAAE;IAAQ;EAC3D,CAAC,CAAC,CACDC,OAAO,EAAE;EAEd,OAAOH,IAAI,CAACI,KAAK,CAACT,QAAQ,CAACI,OAAO,CAAW;AACjD,CAAC;AAAC"}
package/cli/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./printReport";
2
+ export * from "./runMigration";
3
+ export * from "./getDuration";
package/cli/index.js ADDED
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _printReport = require("./printReport");
7
+ Object.keys(_printReport).forEach(function (key) {
8
+ if (key === "default" || key === "__esModule") return;
9
+ if (key in exports && exports[key] === _printReport[key]) return;
10
+ Object.defineProperty(exports, key, {
11
+ enumerable: true,
12
+ get: function () {
13
+ return _printReport[key];
14
+ }
15
+ });
16
+ });
17
+ var _runMigration = require("./runMigration");
18
+ Object.keys(_runMigration).forEach(function (key) {
19
+ if (key === "default" || key === "__esModule") return;
20
+ if (key in exports && exports[key] === _runMigration[key]) return;
21
+ Object.defineProperty(exports, key, {
22
+ enumerable: true,
23
+ get: function () {
24
+ return _runMigration[key];
25
+ }
26
+ });
27
+ });
28
+ var _getDuration = require("./getDuration");
29
+ Object.keys(_getDuration).forEach(function (key) {
30
+ if (key === "default" || key === "__esModule") return;
31
+ if (key in exports && exports[key] === _getDuration[key]) return;
32
+ Object.defineProperty(exports, key, {
33
+ enumerable: true,
34
+ get: function () {
35
+ return _getDuration[key];
36
+ }
37
+ });
38
+ });
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["export * from \"./printReport\";\nexport * from \"./runMigration\";\nexport * from \"./getDuration\";\n"],"mappings":";;;;;AAAA;AAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA;AACA;AAAA;EAAA;EAAA;EAAA;IAAA;IAAA;MAAA;IAAA;EAAA;AAAA"}
@@ -0,0 +1,9 @@
1
+ import { CliContext } from "@webiny/cli/types";
2
+ import { MigrationEventHandlerResponse } from "../types";
3
+ interface ReportParams {
4
+ response: MigrationEventHandlerResponse;
5
+ migrationLambdaArn: string;
6
+ context: CliContext;
7
+ }
8
+ export declare const printReport: ({ response, migrationLambdaArn, context }: ReportParams) => void;
9
+ export {};
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.printReport = void 0;
8
+ var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
9
+ var _centerAlign = _interopRequireDefault(require("center-align"));
10
+ const _excluded = ["migrations"];
11
+ const isError = response => {
12
+ if (!response) {
13
+ return false;
14
+ }
15
+ return "error" in response;
16
+ };
17
+ const makeEven = str => {
18
+ if (str.length % 2 > 0) {
19
+ return str + " ";
20
+ }
21
+ return str;
22
+ };
23
+ const printReport = ({
24
+ response,
25
+ migrationLambdaArn,
26
+ context
27
+ }) => {
28
+ if (!response) {
29
+ return;
30
+ }
31
+ if (isError(response)) {
32
+ context.error(response.error.message);
33
+ return;
34
+ }
35
+ const functionName = migrationLambdaArn.split(":").pop();
36
+ context.success(`Data migration Lambda %s executed successfully!`, functionName);
37
+ const _response$data = response.data,
38
+ {
39
+ migrations
40
+ } = _response$data,
41
+ run = (0, _objectWithoutProperties2.default)(_response$data, _excluded);
42
+ if (!migrations.length) {
43
+ context.info(`No applicable migrations were found!`);
44
+ return;
45
+ }
46
+ const maxLength = Math.max(...migrations.map(mig => mig.status.length)) + 2;
47
+ context.info(`Migration run: %s`, run.id);
48
+ context.info(`Status: %s`, run.status);
49
+ context.info(`Started on: %s`, run.startedOn);
50
+ if (run.status === "done") {
51
+ context.info(`Finished on: %s`, run.finishedOn);
52
+ }
53
+ for (const migration of migrations) {
54
+ context.info(...[`[%s] %s: ${migration.description}`, (0, _centerAlign.default)(makeEven(migration.status), maxLength), migration.id]);
55
+ }
56
+ };
57
+ exports.printReport = printReport;
@@ -0,0 +1 @@
1
+ {"version":3,"names":["isError","response","makeEven","str","length","printReport","migrationLambdaArn","context","error","message","functionName","split","pop","success","data","migrations","run","info","maxLength","Math","max","map","mig","status","id","startedOn","finishedOn","migration","description","center"],"sources":["printReport.ts"],"sourcesContent":["import { CliContext } from \"@webiny/cli/types\";\nimport { MigrationEventHandlerResponse, MigrationInvocationErrorResponse } from \"~/types\";\nimport center from \"center-align\";\n\ninterface ReportParams {\n response: MigrationEventHandlerResponse;\n migrationLambdaArn: string;\n context: CliContext;\n}\n\nconst isError = (\n response: MigrationEventHandlerResponse\n): response is MigrationInvocationErrorResponse => {\n if (!response) {\n return false;\n }\n\n return \"error\" in response;\n};\n\nconst makeEven = (str: string) => {\n if (str.length % 2 > 0) {\n return str + \" \";\n }\n return str;\n};\n\nexport const printReport = ({ response, migrationLambdaArn, context }: ReportParams) => {\n if (!response) {\n return;\n }\n\n if (isError(response)) {\n context.error(response.error.message);\n return;\n }\n\n const functionName = migrationLambdaArn.split(\":\").pop();\n context.success(`Data migration Lambda %s executed successfully!`, functionName);\n\n const { migrations, ...run } = response.data;\n if (!migrations.length) {\n context.info(`No applicable migrations were found!`);\n return;\n }\n\n const maxLength = Math.max(...migrations.map(mig => mig.status.length)) + 2;\n context.info(`Migration run: %s`, run.id);\n context.info(`Status: %s`, run.status);\n context.info(`Started on: %s`, run.startedOn);\n if (run.status === \"done\") {\n context.info(`Finished on: %s`, run.finishedOn);\n }\n for (const migration of migrations) {\n context.info(\n ...[\n `[%s] %s: ${migration.description}`,\n center(makeEven(migration.status), maxLength),\n migration.id\n ]\n );\n }\n};\n"],"mappings":";;;;;;;;AAEA;AAAkC;AAQlC,MAAMA,OAAO,GACTC,QAAuC,IACQ;EAC/C,IAAI,CAACA,QAAQ,EAAE;IACX,OAAO,KAAK;EAChB;EAEA,OAAO,OAAO,IAAIA,QAAQ;AAC9B,CAAC;AAED,MAAMC,QAAQ,GAAIC,GAAW,IAAK;EAC9B,IAAIA,GAAG,CAACC,MAAM,GAAG,CAAC,GAAG,CAAC,EAAE;IACpB,OAAOD,GAAG,GAAG,GAAG;EACpB;EACA,OAAOA,GAAG;AACd,CAAC;AAEM,MAAME,WAAW,GAAG,CAAC;EAAEJ,QAAQ;EAAEK,kBAAkB;EAAEC;AAAsB,CAAC,KAAK;EACpF,IAAI,CAACN,QAAQ,EAAE;IACX;EACJ;EAEA,IAAID,OAAO,CAACC,QAAQ,CAAC,EAAE;IACnBM,OAAO,CAACC,KAAK,CAACP,QAAQ,CAACO,KAAK,CAACC,OAAO,CAAC;IACrC;EACJ;EAEA,MAAMC,YAAY,GAAGJ,kBAAkB,CAACK,KAAK,CAAC,GAAG,CAAC,CAACC,GAAG,EAAE;EACxDL,OAAO,CAACM,OAAO,CAAE,iDAAgD,EAAEH,YAAY,CAAC;EAEhF,uBAA+BT,QAAQ,CAACa,IAAI;IAAtC;MAAEC;IAAmB,CAAC;IAALC,GAAG;EAC1B,IAAI,CAACD,UAAU,CAACX,MAAM,EAAE;IACpBG,OAAO,CAACU,IAAI,CAAE,sCAAqC,CAAC;IACpD;EACJ;EAEA,MAAMC,SAAS,GAAGC,IAAI,CAACC,GAAG,CAAC,GAAGL,UAAU,CAACM,GAAG,CAACC,GAAG,IAAIA,GAAG,CAACC,MAAM,CAACnB,MAAM,CAAC,CAAC,GAAG,CAAC;EAC3EG,OAAO,CAACU,IAAI,CAAE,mBAAkB,EAAED,GAAG,CAACQ,EAAE,CAAC;EACzCjB,OAAO,CAACU,IAAI,CAAE,YAAW,EAAED,GAAG,CAACO,MAAM,CAAC;EACtChB,OAAO,CAACU,IAAI,CAAE,gBAAe,EAAED,GAAG,CAACS,SAAS,CAAC;EAC7C,IAAIT,GAAG,CAACO,MAAM,KAAK,MAAM,EAAE;IACvBhB,OAAO,CAACU,IAAI,CAAE,iBAAgB,EAAED,GAAG,CAACU,UAAU,CAAC;EACnD;EACA,KAAK,MAAMC,SAAS,IAAIZ,UAAU,EAAE;IAChCR,OAAO,CAACU,IAAI,CACR,GAAG,CACE,YAAWU,SAAS,CAACC,WAAY,EAAC,EACnC,IAAAC,oBAAM,EAAC3B,QAAQ,CAACyB,SAAS,CAACJ,MAAM,CAAC,EAAEL,SAAS,CAAC,EAC7CS,SAAS,CAACH,EAAE,CACf,CACJ;EACL;AACJ,CAAC;AAAC"}
@@ -0,0 +1,13 @@
1
+ import LambdaClient from "aws-sdk/clients/lambda";
2
+ import { MigrationInvocationErrorResponse, MigrationRun, MigrationStatusResponse } from "../types";
3
+ interface RunMigrationParams {
4
+ lambdaClient: LambdaClient;
5
+ functionName: string;
6
+ payload?: Record<string, any>;
7
+ statusCallback?: (status: MigrationRun) => void;
8
+ }
9
+ /**
10
+ * Run the migration Lambda, and re-run when resuming is requested.
11
+ */
12
+ export declare const runMigration: ({ payload, functionName, lambdaClient, statusCallback }: RunMigrationParams) => Promise<MigrationStatusResponse | MigrationInvocationErrorResponse>;
13
+ export {};