@strapi/database 4.2.0-beta.2 → 4.2.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.
@@ -0,0 +1,55 @@
1
+ 'use strict';
2
+
3
+ const { createLifecyclesProvider } = require('../lifecycles');
4
+
5
+ describe('LifecycleProvider', () => {
6
+ describe('run', () => {
7
+ /** @type {import("../lifecycles").LifecycleProvider} */
8
+ let provider;
9
+ let dbMetadataGetStub = jest.fn(uid => ({ uid, name: 'TestModel' }));
10
+
11
+ beforeEach(() => {
12
+ const db = {
13
+ metadata: {
14
+ get: dbMetadataGetStub,
15
+ },
16
+ };
17
+ provider = createLifecyclesProvider(db);
18
+ provider.clear();
19
+ });
20
+
21
+ it('store state', async () => {
22
+ const expectedState = new Date().toISOString();
23
+
24
+ const subscriber = {
25
+ async beforeEvent(event) {
26
+ event.state = expectedState;
27
+ },
28
+ };
29
+ provider.subscribe(subscriber);
30
+
31
+ const stateBefore = await provider.run('beforeEvent', 'test-model', { id: 'instance-id' });
32
+
33
+ expect(stateBefore.get(subscriber)).toEqual(expectedState);
34
+ });
35
+
36
+ it('use shared state', async () => {
37
+ const expectedState = { value: new Date().toISOString() };
38
+ let receivedState;
39
+
40
+ provider.subscribe({
41
+ async beforeEvent(event) {
42
+ event.state.value = expectedState.value;
43
+ },
44
+ async afterEvent(event) {
45
+ receivedState = event.state;
46
+ },
47
+ });
48
+
49
+ const stateBefore = await provider.run('beforeEvent', 'test-model', { id: 'instance-id' });
50
+ await provider.run('afterEvent', 'test-model', { id: 'instance-id' }, stateBefore);
51
+
52
+ expect(receivedState).toEqual(expectedState);
53
+ });
54
+ });
55
+ });
@@ -113,33 +113,33 @@ const createEntityManager = db => {
113
113
 
114
114
  return {
115
115
  async findOne(uid, params) {
116
- await db.lifecycles.run('beforeFindOne', uid, { params });
116
+ const states = await db.lifecycles.run('beforeFindOne', uid, { params });
117
117
 
118
118
  const result = await this.createQueryBuilder(uid)
119
119
  .init(params)
120
120
  .first()
121
121
  .execute();
122
122
 
123
- await db.lifecycles.run('afterFindOne', uid, { params, result });
123
+ await db.lifecycles.run('afterFindOne', uid, { params, result }, states);
124
124
 
125
125
  return result;
126
126
  },
127
127
 
128
128
  // should we name it findOne because people are used to it ?
129
129
  async findMany(uid, params) {
130
- await db.lifecycles.run('beforeFindMany', uid, { params });
130
+ const states = await db.lifecycles.run('beforeFindMany', uid, { params });
131
131
 
132
132
  const result = await this.createQueryBuilder(uid)
133
133
  .init(params)
134
134
  .execute();
135
135
 
136
- await db.lifecycles.run('afterFindMany', uid, { params, result });
136
+ await db.lifecycles.run('afterFindMany', uid, { params, result }, states);
137
137
 
138
138
  return result;
139
139
  },
140
140
 
141
- async count(uid, params = {}) {
142
- await db.lifecycles.run('beforeCount', uid, { params });
141
+ async count(uid, params) {
142
+ const states = await db.lifecycles.run('beforeCount', uid, { params });
143
143
 
144
144
  const res = await this.createQueryBuilder(uid)
145
145
  .init(_.pick(['_q', 'where', 'filters'], params))
@@ -149,13 +149,13 @@ const createEntityManager = db => {
149
149
 
150
150
  const result = Number(res.count);
151
151
 
152
- await db.lifecycles.run('afterCount', uid, { params, result });
152
+ await db.lifecycles.run('afterCount', uid, { params, result }, states);
153
153
 
154
154
  return result;
155
155
  },
156
156
 
157
157
  async create(uid, params = {}) {
158
- await db.lifecycles.run('beforeCreate', uid, { params });
158
+ const states = await db.lifecycles.run('beforeCreate', uid, { params });
159
159
 
160
160
  const metadata = db.metadata.get(uid);
161
161
  const { data } = params;
@@ -182,14 +182,14 @@ const createEntityManager = db => {
182
182
  populate: params.populate,
183
183
  });
184
184
 
185
- await db.lifecycles.run('afterCreate', uid, { params, result });
185
+ await db.lifecycles.run('afterCreate', uid, { params, result }, states);
186
186
 
187
187
  return result;
188
188
  },
189
189
 
190
190
  // TODO: where do we handle relation processing for many queries ?
191
191
  async createMany(uid, params = {}) {
192
- await db.lifecycles.run('beforeCreateMany', uid, { params });
192
+ const states = await db.lifecycles.run('beforeCreateMany', uid, { params });
193
193
 
194
194
  const metadata = db.metadata.get(uid);
195
195
  const { data } = params;
@@ -210,13 +210,13 @@ const createEntityManager = db => {
210
210
 
211
211
  const result = { count: data.length };
212
212
 
213
- await db.lifecycles.run('afterCreateMany', uid, { params, result });
213
+ await db.lifecycles.run('afterCreateMany', uid, { params, result }, states);
214
214
 
215
215
  return result;
216
216
  },
217
217
 
218
218
  async update(uid, params = {}) {
219
- await db.lifecycles.run('beforeUpdate', uid, { params });
219
+ const states = await db.lifecycles.run('beforeUpdate', uid, { params });
220
220
 
221
221
  const metadata = db.metadata.get(uid);
222
222
  const { where, data } = params;
@@ -259,14 +259,14 @@ const createEntityManager = db => {
259
259
  populate: params.populate,
260
260
  });
261
261
 
262
- await db.lifecycles.run('afterUpdate', uid, { params, result });
262
+ await db.lifecycles.run('afterUpdate', uid, { params, result }, states);
263
263
 
264
264
  return result;
265
265
  },
266
266
 
267
267
  // TODO: where do we handle relation processing for many queries ?
268
268
  async updateMany(uid, params = {}) {
269
- await db.lifecycles.run('beforeUpdateMany', uid, { params });
269
+ const states = await db.lifecycles.run('beforeUpdateMany', uid, { params });
270
270
 
271
271
  const metadata = db.metadata.get(uid);
272
272
  const { where, data } = params;
@@ -284,13 +284,13 @@ const createEntityManager = db => {
284
284
 
285
285
  const result = { count: updatedRows };
286
286
 
287
- await db.lifecycles.run('afterUpdateMany', uid, { params, result });
287
+ await db.lifecycles.run('afterUpdateMany', uid, { params, result }, states);
288
288
 
289
289
  return result;
290
290
  },
291
291
 
292
292
  async delete(uid, params = {}) {
293
- await db.lifecycles.run('beforeDelete', uid, { params });
293
+ const states = await db.lifecycles.run('beforeDelete', uid, { params });
294
294
 
295
295
  const { where, select, populate } = params;
296
296
 
@@ -318,14 +318,14 @@ const createEntityManager = db => {
318
318
 
319
319
  await this.deleteRelations(uid, id);
320
320
 
321
- await db.lifecycles.run('afterDelete', uid, { params, result: entity });
321
+ await db.lifecycles.run('afterDelete', uid, { params, result: entity }, states);
322
322
 
323
323
  return entity;
324
324
  },
325
325
 
326
326
  // TODO: where do we handle relation processing for many queries ?
327
327
  async deleteMany(uid, params = {}) {
328
- await db.lifecycles.run('beforeDeleteMany', uid, { params });
328
+ const states = await db.lifecycles.run('beforeDeleteMany', uid, { params });
329
329
 
330
330
  const { where } = params;
331
331
 
@@ -336,7 +336,7 @@ const createEntityManager = db => {
336
336
 
337
337
  const result = { count: deletedRows };
338
338
 
339
- await db.lifecycles.run('afterDelete', uid, { params, result });
339
+ await db.lifecycles.run('afterDeleteMany', uid, { params, result }, states);
340
340
 
341
341
  return result;
342
342
  },
@@ -43,7 +43,8 @@ export interface Event {
43
43
  export interface LifecycleProvider {
44
44
  subscribe(subscriber: Subscriber): () => void;
45
45
  clear(): void;
46
- run(action: Action, uid: string, properties: any): Promise<void>;
46
+ run(action: Action, uid: string, properties: any): Promise<Map<any, any>>;
47
+ run(action: Action, uid: string, properties: any, states: Map<any, any>): Promise<Map<any, any>>;
47
48
  createEvent(action: Action, uid: string, properties: any): Event;
48
49
  }
49
50
 
@@ -30,21 +30,39 @@ const createLifecyclesProvider = db => {
30
30
  subscribers = [];
31
31
  },
32
32
 
33
- createEvent(action, uid, properties) {
33
+ /**
34
+ * @param {string} action
35
+ * @param {string} uid
36
+ * @param {{ params?: any, result?: any }} properties
37
+ * @param {Map<any, any>} state
38
+ */
39
+ createEvent(action, uid, properties, state) {
34
40
  const model = db.metadata.get(uid);
35
41
 
36
42
  return {
37
43
  action,
38
44
  model,
45
+ state,
39
46
  ...properties,
40
47
  };
41
48
  },
42
49
 
43
- async run(action, uid, properties) {
44
- for (const subscriber of subscribers) {
50
+ /**
51
+ * @param {string} action
52
+ * @param {string} uid
53
+ * @param {{ params?: any, result?: any }} properties
54
+ * @param {Map<any, any>} states
55
+ */
56
+ async run(action, uid, properties, states = new Map()) {
57
+ for (let i = 0; i < subscribers.length; i++) {
58
+ const subscriber = subscribers[i];
45
59
  if (typeof subscriber === 'function') {
46
- const event = this.createEvent(action, uid, properties);
60
+ const state = states.get(subscriber) || {};
61
+ const event = this.createEvent(action, uid, properties, state);
47
62
  await subscriber(event);
63
+ if (event.state) {
64
+ states.set(subscriber, event.state || state);
65
+ }
48
66
  continue;
49
67
  }
50
68
 
@@ -52,11 +70,17 @@ const createLifecyclesProvider = db => {
52
70
  const hasModel = !subscriber.models || subscriber.models.includes(uid);
53
71
 
54
72
  if (hasAction && hasModel) {
55
- const event = this.createEvent(action, uid, properties);
73
+ const state = states.get(subscriber) || {};
74
+ const event = this.createEvent(action, uid, properties, state);
56
75
 
57
76
  await subscriber[action](event);
77
+ if (event.state) {
78
+ states.set(subscriber, event.state);
79
+ }
58
80
  }
59
81
  }
82
+
83
+ return states;
60
84
  },
61
85
  };
62
86
  };
@@ -2,42 +2,48 @@
2
2
 
3
3
  const path = require('path');
4
4
  const fse = require('fs-extra');
5
- const Umzug = require('umzug');
5
+ const { Umzug } = require('umzug');
6
6
 
7
7
  const createStorage = require('./storage');
8
8
 
9
+ const wrapTransaction = db => fn => () =>
10
+ db.getConnection().transaction(trx => Promise.resolve(fn(trx)));
11
+
9
12
  // TODO: check multiple commands in one sql statement
10
- const migrationResolver = path => {
13
+ const migrationResolver = ({ name, path, context }) => {
14
+ const { db } = context;
15
+
11
16
  // if sql file run with knex raw
12
17
  if (path.match(/\.sql$/)) {
13
18
  const sql = fse.readFileSync(path, 'utf8');
14
19
 
15
20
  return {
16
- up: knex => knex.raw(sql),
21
+ name,
22
+ up: wrapTransaction(db)(knex => knex.raw(sql)),
17
23
  down() {},
18
24
  };
19
25
  }
20
26
 
21
27
  // NOTE: we can add some ts register if we want to handle ts migration files at some point
22
- return require(path);
28
+ const migration = require(path);
29
+ return {
30
+ name,
31
+ up: wrapTransaction(db)(migration.up),
32
+ down: wrapTransaction(db)(migration.down),
33
+ };
23
34
  };
24
35
 
25
36
  const createUmzugProvider = db => {
26
- const migrationDir = path.join(strapi.dirs.app.root, 'database/migrations');
37
+ const migrationDir = path.join(strapi.dirs.root, 'database/migrations');
27
38
 
28
39
  fse.ensureDirSync(migrationDir);
29
40
 
30
- const wrapFn = fn => db => db.getConnection().transaction(trx => Promise.resolve(fn(trx)));
31
- const storage = createStorage({ db, tableName: 'strapi_migrations' });
32
-
33
41
  return new Umzug({
34
- storage,
42
+ storage: createStorage({ db, tableName: 'strapi_migrations' }),
43
+ context: { db },
35
44
  migrations: {
36
- path: migrationDir,
37
- pattern: /\.(js|sql)$/,
38
- params: [db],
39
- wrap: wrapFn,
40
- customResolver: migrationResolver,
45
+ glob: ['*.{js,sql}', { cwd: migrationDir }],
46
+ resolve: migrationResolver,
41
47
  },
42
48
  });
43
49
  };
@@ -14,21 +14,21 @@ const createStorage = (opts = {}) => {
14
14
  };
15
15
 
16
16
  return {
17
- async logMigration(migrationName) {
17
+ async logMigration({ name }) {
18
18
  await db
19
19
  .getConnection()
20
20
  .insert({
21
- name: migrationName,
21
+ name,
22
22
  time: new Date(),
23
23
  })
24
24
  .into(tableName);
25
25
  },
26
26
 
27
- async unlogMigration(migrationName) {
27
+ async unlogMigration({ name }) {
28
28
  await db
29
29
  .getConnection(tableName)
30
30
  .del()
31
- .where({ name: migrationName });
31
+ .where({ name });
32
32
  },
33
33
 
34
34
  async executed() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/database",
3
- "version": "4.2.0-beta.2",
3
+ "version": "4.2.0",
4
4
  "description": "Strapi's database layer",
5
5
  "homepage": "https://strapi.io",
6
6
  "bugs": {
@@ -36,11 +36,11 @@
36
36
  "fs-extra": "10.0.0",
37
37
  "knex": "1.0.4",
38
38
  "lodash": "4.17.21",
39
- "umzug": "2.3.0"
39
+ "umzug": "3.1.1"
40
40
  },
41
41
  "engines": {
42
- "node": ">=12.22.0 <=16.x.x",
42
+ "node": ">=14.19.1 <=16.x.x",
43
43
  "npm": ">=6.0.0"
44
44
  },
45
- "gitHead": "bff73257e7695d6f361c91dda8cc810a2bb70b6e"
45
+ "gitHead": "12c8ee3b2d95fe417de4d939db0311a0513bd8da"
46
46
  }