rake-db 2.2.3 → 2.2.4

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.
@@ -3,6 +3,8 @@ import { createSchemaMigrations, migrationConfigDefaults } from '../common';
3
3
  import { getMigrationFiles } from '../common';
4
4
  import { Adapter, noop, TransactionAdapter } from 'pqb';
5
5
  import { Migration } from '../migration/migration';
6
+ import { change } from '../migration/change';
7
+ import { asMock } from '../test-utils';
6
8
 
7
9
  jest.mock('../common', () => ({
8
10
  ...jest.requireActual('../common'),
@@ -23,6 +25,7 @@ Adapter.prototype.arrays = getMigratedVersionsArrayMock;
23
25
 
24
26
  const queryMock = jest.fn();
25
27
  Adapter.prototype.query = queryMock;
28
+ queryMock.mockImplementation(() => undefined);
26
29
 
27
30
  Adapter.prototype.transaction = (cb) => {
28
31
  return cb({} as unknown as TransactionAdapter);
@@ -43,17 +46,32 @@ const config = {
43
46
  },
44
47
  };
45
48
 
49
+ const createTableCallback = () => {
50
+ change(async (db) => {
51
+ await db.createTable('table', (t) => ({
52
+ id: t.serial().primaryKey(),
53
+ }));
54
+ });
55
+ };
56
+
57
+ let migrationFiles: { path: string; version: string }[] = [];
58
+ asMock(getMigrationFiles).mockImplementation(() => migrationFiles);
59
+
60
+ let migratedVersions: string[] = [];
61
+ getMigratedVersionsArrayMock.mockImplementation(() => ({
62
+ rows: migratedVersions.map((version) => [version]),
63
+ }));
64
+
46
65
  describe('migrateOrRollback', () => {
47
66
  beforeEach(() => {
48
67
  jest.clearAllMocks();
68
+ requireTsMock.mockImplementation(() => undefined);
49
69
  });
50
70
 
51
71
  describe('migrate', () => {
52
72
  it('should work properly', async () => {
53
- (getMigrationFiles as jest.Mock).mockReturnValueOnce(files);
54
- getMigratedVersionsArrayMock.mockResolvedValueOnce({ rows: [['1']] });
55
- queryMock.mockReturnValueOnce(undefined);
56
- requireTsMock.mockResolvedValue(undefined);
73
+ migrationFiles = files;
74
+ migratedVersions = ['1'];
57
75
 
58
76
  await migrate(options, config, []);
59
77
 
@@ -74,7 +92,7 @@ describe('migrateOrRollback', () => {
74
92
  });
75
93
 
76
94
  it('should create migrations table if it not exist', async () => {
77
- (getMigrationFiles as jest.Mock).mockReturnValueOnce([]);
95
+ migrationFiles = [];
78
96
  getMigratedVersionsArrayMock.mockRejectedValueOnce({ code: '42P01' });
79
97
  (createSchemaMigrations as jest.Mock).mockResolvedValueOnce(undefined);
80
98
 
@@ -86,16 +104,72 @@ describe('migrateOrRollback', () => {
86
104
  expect(transactionQueryMock).not.toBeCalled();
87
105
  expect(config.logger.log).not.toBeCalled();
88
106
  });
107
+
108
+ it('should call appCodeUpdater only on the first run', async () => {
109
+ migrationFiles = [files[0]];
110
+ migratedVersions = [];
111
+ requireTsMock.mockImplementationOnce(createTableCallback);
112
+ const appCodeUpdater = jest.fn();
113
+
114
+ await migrate(
115
+ [options, options],
116
+ { ...config, appCodeUpdater, useCodeUpdater: true },
117
+ [],
118
+ );
119
+
120
+ expect(appCodeUpdater).toBeCalledTimes(1);
121
+ });
122
+
123
+ it('should not call appCodeUpdater when useCodeUpdater is set to false in config', async () => {
124
+ migrationFiles = [files[0]];
125
+ migratedVersions = [];
126
+ requireTsMock.mockImplementation(createTableCallback);
127
+ const appCodeUpdater = jest.fn();
128
+
129
+ await migrate(
130
+ options,
131
+ { ...config, appCodeUpdater, useCodeUpdater: false },
132
+ [],
133
+ );
134
+
135
+ expect(appCodeUpdater).not.toBeCalled();
136
+ });
137
+
138
+ it('should not call appCodeUpdater when having argument --code false', async () => {
139
+ migrationFiles = [files[0]];
140
+ migratedVersions = [];
141
+ requireTsMock.mockImplementation(createTableCallback);
142
+ const appCodeUpdater = jest.fn();
143
+
144
+ await migrate(
145
+ options,
146
+ { ...config, appCodeUpdater, useCodeUpdater: true },
147
+ ['--code', 'false'],
148
+ );
149
+
150
+ expect(appCodeUpdater).not.toBeCalled();
151
+ });
152
+
153
+ it('should call appCodeUpdater when having argument --code', async () => {
154
+ migrationFiles = [files[0]];
155
+ migratedVersions = [];
156
+ requireTsMock.mockImplementation(createTableCallback);
157
+ const appCodeUpdater = jest.fn();
158
+
159
+ await migrate(
160
+ options,
161
+ { ...config, appCodeUpdater, useCodeUpdater: false },
162
+ ['--code'],
163
+ );
164
+
165
+ expect(appCodeUpdater).toBeCalled();
166
+ });
89
167
  });
90
168
 
91
169
  describe('rollback', () => {
92
170
  it('should work properly', async () => {
93
- (getMigrationFiles as jest.Mock).mockReturnValueOnce(files.reverse());
94
- getMigratedVersionsArrayMock.mockResolvedValueOnce({
95
- rows: [['1'], ['2']],
96
- });
97
- queryMock.mockReturnValueOnce(undefined);
98
- requireTsMock.mockResolvedValue(undefined);
171
+ migrationFiles = files.reverse();
172
+ migratedVersions = ['1', '2'];
99
173
 
100
174
  await rollback(options, config, []);
101
175
 
@@ -114,7 +188,7 @@ describe('migrateOrRollback', () => {
114
188
  });
115
189
 
116
190
  it('should create migrations table if it not exist', async () => {
117
- (getMigrationFiles as jest.Mock).mockReturnValueOnce([]);
191
+ migrationFiles = [];
118
192
  getMigratedVersionsArrayMock.mockRejectedValueOnce({ code: '42P01' });
119
193
  (createSchemaMigrations as jest.Mock).mockResolvedValueOnce(undefined);
120
194
 
@@ -22,10 +22,25 @@ const migrateOrRollback = async (
22
22
  args: string[],
23
23
  up: boolean,
24
24
  ) => {
25
+ config = { ...config };
25
26
  const files = await getMigrationFiles(config, up);
26
27
 
27
- const argCount = args[0] === 'all' ? Infinity : parseInt(args[0]);
28
- let count = isNaN(argCount) ? (up ? Infinity : 1) : argCount;
28
+ let count = up ? Infinity : 1;
29
+ let argI = 0;
30
+ const num = args[0] === 'all' ? Infinity : parseInt(args[0]);
31
+ if (!isNaN(num)) {
32
+ argI++;
33
+ count = num;
34
+ }
35
+
36
+ const arg = args[argI];
37
+ if (arg === '--code') {
38
+ config.useCodeUpdater = args[argI + 1] !== 'false';
39
+ }
40
+
41
+ if (!config.useCodeUpdater) delete config.appCodeUpdater;
42
+
43
+ const appCodeUpdaterCache = {};
29
44
 
30
45
  for (const opts of toArray(options)) {
31
46
  const db = new Adapter(opts);
@@ -41,12 +56,14 @@ const migrateOrRollback = async (
41
56
 
42
57
  if (count-- <= 0) break;
43
58
 
44
- await processMigration(db, up, file, config);
59
+ await processMigration(db, up, file, config, opts, appCodeUpdaterCache);
45
60
  config.logger?.log(`${file.path} ${up ? 'migrated' : 'rolled back'}`);
46
61
  }
47
62
  } finally {
48
63
  await db.close();
49
64
  }
65
+ // use appCodeUpdater only for the first provided options
66
+ delete config.appCodeUpdater;
50
67
  }
51
68
  };
52
69
 
@@ -57,9 +74,11 @@ const processMigration = async (
57
74
  up: boolean,
58
75
  file: MigrationFile,
59
76
  config: RakeDbConfig,
77
+ options: AdapterOptions,
78
+ appCodeUpdaterCache: object,
60
79
  ) => {
61
80
  await db.transaction(async (tx) => {
62
- const db = new Migration(tx, up, config);
81
+ const db = new Migration(tx, up, config, options, appCodeUpdaterCache);
63
82
  setCurrentMigration(db);
64
83
  setCurrentMigrationUp(up);
65
84
 
package/src/common.ts CHANGED
@@ -15,9 +15,14 @@ export type RakeDbConfig = {
15
15
  requireTs(path: string): void;
16
16
  noPrimaryKey?: NoPrimaryKeyOption;
17
17
  appCodeUpdater?: AppCodeUpdater;
18
+ useCodeUpdater?: boolean;
18
19
  } & QueryLogOptions;
19
20
 
20
- export type AppCodeUpdater = (ast: RakeDbAst) => Promise<void>;
21
+ export type AppCodeUpdater = (params: {
22
+ ast: RakeDbAst;
23
+ options: AdapterOptions;
24
+ cache: object;
25
+ }) => Promise<void>;
21
26
 
22
27
  export const migrationConfigDefaults = {
23
28
  migrationsPath: path.resolve(process.cwd(), 'src', 'migrations'),
@@ -25,6 +30,7 @@ export const migrationConfigDefaults = {
25
30
  requireTs: require,
26
31
  log: true,
27
32
  logger: console,
33
+ useCodeUpdaterByDefault: true,
28
34
  };
29
35
 
30
36
  export const getMigrationConfigWithDefaults = (
@@ -20,6 +20,7 @@ import {
20
20
  DropMode,
21
21
  Migration,
22
22
  MigrationColumnTypes,
23
+ runCodeUpdater,
23
24
  } from './migration';
24
25
  import { RakeDbAst } from '../ast';
25
26
  import { quoteTable } from '../common';
@@ -218,7 +219,7 @@ export const changeTable = async (
218
219
  await migration.query(query);
219
220
  }
220
221
 
221
- await migration.options.appCodeUpdater?.(ast);
222
+ await runCodeUpdater(migration, ast);
222
223
  };
223
224
 
224
225
  const makeAst = (
@@ -13,6 +13,7 @@ import {
13
13
  ColumnComment,
14
14
  ColumnsShapeCallback,
15
15
  Migration,
16
+ runCodeUpdater,
16
17
  TableOptions,
17
18
  } from './migration';
18
19
  import {
@@ -56,7 +57,7 @@ export const createTable = async (
56
57
  await migration.query(query);
57
58
  }
58
59
 
59
- await migration.options.appCodeUpdater?.(ast);
60
+ await runCodeUpdater(migration, ast);
60
61
  };
61
62
 
62
63
  const makeAst = (
@@ -17,6 +17,7 @@ import {
17
17
  TypeParsers,
18
18
  raw,
19
19
  TextColumn,
20
+ AdapterOptions,
20
21
  } from 'pqb';
21
22
  import { createTable } from './createTable';
22
23
  import { changeTable, TableChangeData, TableChanger } from './changeTable';
@@ -67,6 +68,8 @@ export class Migration extends TransactionAdapter {
67
68
  tx: TransactionAdapter,
68
69
  public up: boolean,
69
70
  public options: RakeDbConfig,
71
+ public adapterOptions: AdapterOptions,
72
+ public appCodeUpdaterCache: object,
70
73
  ) {
71
74
  super(tx, tx.client, tx.types);
72
75
  this.log = logParamToLogObject(options.logger || console, options.log);
@@ -186,7 +189,7 @@ export class Migration extends TransactionAdapter {
186
189
  `ALTER TABLE ${quoteTable(ast.from)} RENAME TO "${ast.to}"`,
187
190
  );
188
191
 
189
- await this.options.appCodeUpdater?.(ast);
192
+ await runCodeUpdater(this, ast);
190
193
  }
191
194
 
192
195
  addColumn(
@@ -417,7 +420,7 @@ const createSchema = async (
417
420
  `${ast.action === 'create' ? 'CREATE' : 'DROP'} SCHEMA "${name}"`,
418
421
  );
419
422
 
420
- await migration.options.appCodeUpdater?.(ast);
423
+ await runCodeUpdater(migration, ast);
421
424
  };
422
425
 
423
426
  const createExtension = async (
@@ -450,7 +453,7 @@ const createExtension = async (
450
453
 
451
454
  await migration.query(query);
452
455
 
453
- await migration.options.appCodeUpdater?.(ast);
456
+ await runCodeUpdater(migration, ast);
454
457
  };
455
458
 
456
459
  const queryExists = (
@@ -459,3 +462,11 @@ const queryExists = (
459
462
  ) => {
460
463
  return db.query(sql).then(({ rowCount }) => rowCount > 0);
461
464
  };
465
+
466
+ export const runCodeUpdater = (migration: Migration, ast: RakeDbAst) => {
467
+ return migration.options.appCodeUpdater?.({
468
+ ast,
469
+ options: migration.adapterOptions,
470
+ cache: migration.appCodeUpdaterCache,
471
+ });
472
+ };
package/src/rakeDb.ts CHANGED
@@ -39,17 +39,30 @@ Commands:
39
39
  drop drop databases
40
40
  reset drop, create and migrate databases
41
41
  g, generate generate migration file, see below
42
- migrate migrate all pending migrations
42
+ migrate migrate pending migrations
43
43
  rollback rollback the last migrated
44
44
  no or unknown command prints this message
45
45
 
46
+ Migrate arguments:
47
+ no arguments run all pending migrations
48
+ number run specific number of pending migrations
49
+
50
+ Rollback arguments:
51
+ no arguments rollback one last applied migration
52
+ number rollback specific number of applied migrations
53
+ all rollback all applied migrations
54
+
55
+ Migrate and rollback common arguments:
56
+ --code run code updater, overrides \`useCodeUpdater\` option
57
+ --code false do not run code updater
58
+
46
59
  Generate arguments:
47
60
  - (required) first argument is migration name
48
- * create* template for create table
49
- * change* template for change table
50
- * add*To* template for add columns
51
- * remove*From* template for remove columns
52
- * drop* template for drop table
61
+ * create* template for create table
62
+ * change* template for change table
63
+ * add*To* template for add columns
64
+ * remove*From* template for remove columns
65
+ * drop* template for drop table
53
66
 
54
67
  - other arguments considered as columns with types and optional methods:
55
68
  rake-db g createTable id:serial.primaryKey name:text.nullable
package/src/test-utils.ts CHANGED
@@ -1,18 +1,26 @@
1
1
  import { Migration } from './migration/migration';
2
2
  import { MaybeArray, toArray, TransactionAdapter } from 'pqb';
3
3
 
4
+ export const asMock = (fn: unknown) => fn as jest.Mock;
5
+
4
6
  let db: Migration | undefined;
5
7
 
6
8
  export const getDb = () => {
7
9
  if (db) return db;
8
10
 
9
- db = new Migration({} as unknown as TransactionAdapter, true, {
10
- log: false,
11
- migrationsPath: 'migrations-path',
12
- migrationsTable: 'schemaMigrations',
13
- requireTs: require,
14
- appCodeUpdater: appCodeUpdaterMock,
15
- });
11
+ db = new Migration(
12
+ {} as unknown as TransactionAdapter,
13
+ true,
14
+ {
15
+ log: false,
16
+ migrationsPath: 'migrations-path',
17
+ migrationsTable: 'schemaMigrations',
18
+ requireTs: require,
19
+ appCodeUpdater: appCodeUpdaterMock,
20
+ },
21
+ {},
22
+ {},
23
+ );
16
24
  db.query = queryMock;
17
25
  return db;
18
26
  };