@strapi/database 4.2.0-beta.0 → 4.2.0-beta.3
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/lib/__tests__/lifecycles.test.js +55 -0
- package/lib/connection.js +64 -0
- package/lib/dialects/mysql/schema-inspector.js +1 -1
- package/lib/dialects/sqlite/schema-inspector.js +1 -1
- package/lib/entity-manager.js +22 -20
- package/lib/index.d.ts +26 -5
- package/lib/index.js +1 -12
- package/lib/lifecycles/index.d.ts +2 -1
- package/lib/lifecycles/index.js +29 -5
- package/lib/migrations/index.js +19 -13
- package/lib/migrations/storage.js +4 -4
- package/lib/schema/schema.js +1 -1
- package/package.json +4 -4
- package/examples/connections.js +0 -36
- package/examples/docker-compose.yml +0 -29
- package/examples/index.js +0 -73
- package/examples/models.js +0 -341
- package/examples/typings.ts +0 -17
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* eslint-disable node/no-missing-require */
|
|
2
|
+
/* eslint-disable node/no-extraneous-require */
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const knex = require('knex');
|
|
6
|
+
|
|
7
|
+
const SqliteClient = require('knex/lib/dialects/sqlite3/index');
|
|
8
|
+
|
|
9
|
+
const trySqlitePackage = packageName => {
|
|
10
|
+
try {
|
|
11
|
+
require.resolve(packageName);
|
|
12
|
+
return packageName;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
if (error.code === 'MODULE_NOT_FOUND') {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
class LegacySqliteClient extends SqliteClient {
|
|
21
|
+
_driver() {
|
|
22
|
+
return require('sqlite3');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const clientMap = {
|
|
27
|
+
'better-sqlite3': 'better-sqlite3',
|
|
28
|
+
'@vscode/sqlite3': 'sqlite',
|
|
29
|
+
sqlite3: LegacySqliteClient,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const getSqlitePackageName = () => {
|
|
33
|
+
// NOTE: allow forcing the package to use (mostly used for testing purposes)
|
|
34
|
+
if (typeof process.env.SQLITE_PKG !== 'undefined') {
|
|
35
|
+
return process.env.SQLITE_PKG;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// NOTE: this tries to find the best sqlite module possible to use
|
|
39
|
+
// while keeping retro compatibility
|
|
40
|
+
return (
|
|
41
|
+
trySqlitePackage('better-sqlite3') ||
|
|
42
|
+
trySqlitePackage('@vscode/sqlite3') ||
|
|
43
|
+
trySqlitePackage('sqlite3')
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const createConnection = config => {
|
|
48
|
+
const knexConfig = { ...config };
|
|
49
|
+
if (knexConfig.client === 'sqlite') {
|
|
50
|
+
const sqlitePackageName = getSqlitePackageName();
|
|
51
|
+
|
|
52
|
+
knexConfig.client = clientMap[sqlitePackageName];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const knexInstance = knex(knexConfig);
|
|
56
|
+
|
|
57
|
+
return Object.assign(knexInstance, {
|
|
58
|
+
getSchemaName() {
|
|
59
|
+
return this.client.connectionSettings.schema;
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
module.exports = createConnection;
|
|
@@ -48,7 +48,7 @@ const toStrapiType = column => {
|
|
|
48
48
|
switch (rootType) {
|
|
49
49
|
case 'int': {
|
|
50
50
|
if (column.column_key === 'PRI') {
|
|
51
|
-
return { type: 'increments', args: [{ primary: true }], unsigned: false };
|
|
51
|
+
return { type: 'increments', args: [{ primary: true, primaryKey: true }], unsigned: false };
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
return { type: 'integer' };
|
|
@@ -16,7 +16,7 @@ const toStrapiType = column => {
|
|
|
16
16
|
switch (rootType) {
|
|
17
17
|
case 'integer': {
|
|
18
18
|
if (column.pk) {
|
|
19
|
-
return { type: 'increments', args: [{ primary: true }] };
|
|
19
|
+
return { type: 'increments', args: [{ primary: true, primaryKey: true }] };
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
return { type: 'integer' };
|
package/lib/entity-manager.js
CHANGED
|
@@ -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;
|
|
@@ -166,10 +166,12 @@ const createEntityManager = db => {
|
|
|
166
166
|
|
|
167
167
|
const dataToInsert = processData(metadata, data, { withDefaults: true });
|
|
168
168
|
|
|
169
|
-
const
|
|
169
|
+
const res = await this.createQueryBuilder(uid)
|
|
170
170
|
.insert(dataToInsert)
|
|
171
171
|
.execute();
|
|
172
172
|
|
|
173
|
+
const id = res[0].id || res[0];
|
|
174
|
+
|
|
173
175
|
await this.attachRelations(uid, id, data);
|
|
174
176
|
|
|
175
177
|
// TODO: in case there is no select or populate specified return the inserted data ?
|
|
@@ -180,14 +182,14 @@ const createEntityManager = db => {
|
|
|
180
182
|
populate: params.populate,
|
|
181
183
|
});
|
|
182
184
|
|
|
183
|
-
await db.lifecycles.run('afterCreate', uid, { params, result });
|
|
185
|
+
await db.lifecycles.run('afterCreate', uid, { params, result }, states);
|
|
184
186
|
|
|
185
187
|
return result;
|
|
186
188
|
},
|
|
187
189
|
|
|
188
190
|
// TODO: where do we handle relation processing for many queries ?
|
|
189
191
|
async createMany(uid, params = {}) {
|
|
190
|
-
await db.lifecycles.run('beforeCreateMany', uid, { params });
|
|
192
|
+
const states = await db.lifecycles.run('beforeCreateMany', uid, { params });
|
|
191
193
|
|
|
192
194
|
const metadata = db.metadata.get(uid);
|
|
193
195
|
const { data } = params;
|
|
@@ -208,13 +210,13 @@ const createEntityManager = db => {
|
|
|
208
210
|
|
|
209
211
|
const result = { count: data.length };
|
|
210
212
|
|
|
211
|
-
await db.lifecycles.run('afterCreateMany', uid, { params, result });
|
|
213
|
+
await db.lifecycles.run('afterCreateMany', uid, { params, result }, states);
|
|
212
214
|
|
|
213
215
|
return result;
|
|
214
216
|
},
|
|
215
217
|
|
|
216
218
|
async update(uid, params = {}) {
|
|
217
|
-
await db.lifecycles.run('beforeUpdate', uid, { params });
|
|
219
|
+
const states = await db.lifecycles.run('beforeUpdate', uid, { params });
|
|
218
220
|
|
|
219
221
|
const metadata = db.metadata.get(uid);
|
|
220
222
|
const { where, data } = params;
|
|
@@ -257,14 +259,14 @@ const createEntityManager = db => {
|
|
|
257
259
|
populate: params.populate,
|
|
258
260
|
});
|
|
259
261
|
|
|
260
|
-
await db.lifecycles.run('afterUpdate', uid, { params, result });
|
|
262
|
+
await db.lifecycles.run('afterUpdate', uid, { params, result }, states);
|
|
261
263
|
|
|
262
264
|
return result;
|
|
263
265
|
},
|
|
264
266
|
|
|
265
267
|
// TODO: where do we handle relation processing for many queries ?
|
|
266
268
|
async updateMany(uid, params = {}) {
|
|
267
|
-
await db.lifecycles.run('beforeUpdateMany', uid, { params });
|
|
269
|
+
const states = await db.lifecycles.run('beforeUpdateMany', uid, { params });
|
|
268
270
|
|
|
269
271
|
const metadata = db.metadata.get(uid);
|
|
270
272
|
const { where, data } = params;
|
|
@@ -282,13 +284,13 @@ const createEntityManager = db => {
|
|
|
282
284
|
|
|
283
285
|
const result = { count: updatedRows };
|
|
284
286
|
|
|
285
|
-
await db.lifecycles.run('afterUpdateMany', uid, { params, result });
|
|
287
|
+
await db.lifecycles.run('afterUpdateMany', uid, { params, result }, states);
|
|
286
288
|
|
|
287
289
|
return result;
|
|
288
290
|
},
|
|
289
291
|
|
|
290
292
|
async delete(uid, params = {}) {
|
|
291
|
-
await db.lifecycles.run('beforeDelete', uid, { params });
|
|
293
|
+
const states = await db.lifecycles.run('beforeDelete', uid, { params });
|
|
292
294
|
|
|
293
295
|
const { where, select, populate } = params;
|
|
294
296
|
|
|
@@ -316,14 +318,14 @@ const createEntityManager = db => {
|
|
|
316
318
|
|
|
317
319
|
await this.deleteRelations(uid, id);
|
|
318
320
|
|
|
319
|
-
await db.lifecycles.run('afterDelete', uid, { params, result: entity });
|
|
321
|
+
await db.lifecycles.run('afterDelete', uid, { params, result: entity }, states);
|
|
320
322
|
|
|
321
323
|
return entity;
|
|
322
324
|
},
|
|
323
325
|
|
|
324
326
|
// TODO: where do we handle relation processing for many queries ?
|
|
325
327
|
async deleteMany(uid, params = {}) {
|
|
326
|
-
await db.lifecycles.run('beforeDeleteMany', uid, { params });
|
|
328
|
+
const states = await db.lifecycles.run('beforeDeleteMany', uid, { params });
|
|
327
329
|
|
|
328
330
|
const { where } = params;
|
|
329
331
|
|
|
@@ -334,7 +336,7 @@ const createEntityManager = db => {
|
|
|
334
336
|
|
|
335
337
|
const result = { count: deletedRows };
|
|
336
338
|
|
|
337
|
-
await db.lifecycles.run('
|
|
339
|
+
await db.lifecycles.run('afterDeleteMany', uid, { params, result }, states);
|
|
338
340
|
|
|
339
341
|
return result;
|
|
340
342
|
},
|
package/lib/index.d.ts
CHANGED
|
@@ -2,16 +2,37 @@ import { LifecycleProvider } from './lifecycles';
|
|
|
2
2
|
import { MigrationProvider } from './migrations';
|
|
3
3
|
import { SchemaProvideer } from './schema';
|
|
4
4
|
|
|
5
|
-
type
|
|
5
|
+
type LogicOperators<T> = {
|
|
6
6
|
$and?: WhereParams<T>[];
|
|
7
7
|
$or?: WhereParams<T>[];
|
|
8
8
|
$not?: WhereParams<T>;
|
|
9
|
-
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type AttributeOperators<T, K extends keyof T> = {
|
|
12
|
+
$eq?: T[K];
|
|
13
|
+
$ne?: T[K];
|
|
14
|
+
$in?: T[K][];
|
|
15
|
+
$notIn?: T[K][];
|
|
16
|
+
$lt?: T[K];
|
|
17
|
+
$lte?: T[K];
|
|
18
|
+
$gt?: T[K];
|
|
19
|
+
$gte?: T[K];
|
|
20
|
+
$between?: [T[K], T[K]];
|
|
21
|
+
$contains?: T[K];
|
|
22
|
+
$notContains?: T[K];
|
|
23
|
+
$containsi?: T[K];
|
|
24
|
+
$notContainsi?: T[K];
|
|
25
|
+
$startsWith?: T[K];
|
|
26
|
+
$endsWith?: T[K];
|
|
27
|
+
$null?: boolean;
|
|
28
|
+
$notNull?: boolean;
|
|
29
|
+
$not?: WhereParams<T> | AttributeOperators<T, K>;
|
|
30
|
+
}
|
|
10
31
|
|
|
11
|
-
type WhereParams<T> = {
|
|
12
|
-
[K in keyof T]?: T[K]
|
|
32
|
+
export type WhereParams<T> = {
|
|
33
|
+
[K in keyof T]?: T[K] | T[K][] | AttributeOperators<T, K>;
|
|
13
34
|
} &
|
|
14
|
-
|
|
35
|
+
LogicOperators<T>;
|
|
15
36
|
|
|
16
37
|
type Sortables<T> = {
|
|
17
38
|
// check sortable
|
package/lib/index.js
CHANGED
|
@@ -1,28 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const knex = require('knex');
|
|
4
|
-
|
|
5
3
|
const { getDialect } = require('./dialects');
|
|
6
4
|
const createSchemaProvider = require('./schema');
|
|
7
5
|
const createMetadata = require('./metadata');
|
|
8
6
|
const { createEntityManager } = require('./entity-manager');
|
|
9
7
|
const { createMigrationsProvider } = require('./migrations');
|
|
10
8
|
const { createLifecyclesProvider } = require('./lifecycles');
|
|
9
|
+
const createConnection = require('./connection');
|
|
11
10
|
const errors = require('./errors');
|
|
12
11
|
|
|
13
12
|
// TODO: move back into strapi
|
|
14
13
|
const { transformContentTypes } = require('./utils/content-types');
|
|
15
14
|
|
|
16
|
-
const createConnection = config => {
|
|
17
|
-
const knexInstance = knex(config);
|
|
18
|
-
|
|
19
|
-
return Object.assign(knexInstance, {
|
|
20
|
-
getSchemaName() {
|
|
21
|
-
return this.client.connectionSettings.schema;
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
|
|
26
15
|
class Database {
|
|
27
16
|
constructor(config) {
|
|
28
17
|
this.metadata = createMetadata(config.models);
|
|
@@ -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<
|
|
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
|
|
package/lib/lifecycles/index.js
CHANGED
|
@@ -30,21 +30,39 @@ const createLifecyclesProvider = db => {
|
|
|
30
30
|
subscribers = [];
|
|
31
31
|
},
|
|
32
32
|
|
|
33
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
|
|
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
|
|
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
|
};
|
package/lib/migrations/index.js
CHANGED
|
@@ -2,24 +2,35 @@
|
|
|
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
|
-
|
|
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
|
-
|
|
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 => {
|
|
@@ -27,17 +38,12 @@ const createUmzugProvider = db => {
|
|
|
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
|
-
|
|
37
|
-
|
|
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(
|
|
17
|
+
async logMigration({ name }) {
|
|
18
18
|
await db
|
|
19
19
|
.getConnection()
|
|
20
20
|
.insert({
|
|
21
|
-
name
|
|
21
|
+
name,
|
|
22
22
|
time: new Date(),
|
|
23
23
|
})
|
|
24
24
|
.into(tableName);
|
|
25
25
|
},
|
|
26
26
|
|
|
27
|
-
async unlogMigration(
|
|
27
|
+
async unlogMigration({ name }) {
|
|
28
28
|
await db
|
|
29
29
|
.getConnection(tableName)
|
|
30
30
|
.del()
|
|
31
|
-
.where({ name
|
|
31
|
+
.where({ name });
|
|
32
32
|
},
|
|
33
33
|
|
|
34
34
|
async executed() {
|
package/lib/schema/schema.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/database",
|
|
3
|
-
"version": "4.2.0-beta.
|
|
3
|
+
"version": "4.2.0-beta.3",
|
|
4
4
|
"description": "Strapi's database layer",
|
|
5
5
|
"homepage": "https://strapi.io",
|
|
6
6
|
"bugs": {
|
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
"date-fns": "2.22.1",
|
|
35
35
|
"debug": "4.3.1",
|
|
36
36
|
"fs-extra": "10.0.0",
|
|
37
|
-
"knex": "0.
|
|
37
|
+
"knex": "1.0.4",
|
|
38
38
|
"lodash": "4.17.21",
|
|
39
|
-
"umzug": "
|
|
39
|
+
"umzug": "3.1.1"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=12.22.0 <=16.x.x",
|
|
43
43
|
"npm": ">=6.0.0"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "c4addbad6ecbc8ef7633bbba3806f3b0a2ae5f49"
|
|
46
46
|
}
|
package/examples/connections.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const postgres = {
|
|
4
|
-
client: 'postgres',
|
|
5
|
-
connection: {
|
|
6
|
-
database: 'strapi',
|
|
7
|
-
user: 'strapi',
|
|
8
|
-
password: 'strapi',
|
|
9
|
-
},
|
|
10
|
-
// debug: true,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const mysql = {
|
|
14
|
-
client: 'mysql',
|
|
15
|
-
connection: {
|
|
16
|
-
database: 'strapi',
|
|
17
|
-
user: 'strapi',
|
|
18
|
-
password: 'strapi',
|
|
19
|
-
},
|
|
20
|
-
// debug: true,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const sqlite = {
|
|
24
|
-
client: 'sqlite',
|
|
25
|
-
connection: {
|
|
26
|
-
filename: 'data.sqlite',
|
|
27
|
-
},
|
|
28
|
-
useNullAsDefault: true,
|
|
29
|
-
// debug: true,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
module.exports = {
|
|
33
|
-
sqlite,
|
|
34
|
-
postgres,
|
|
35
|
-
mysql,
|
|
36
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
version: '3'
|
|
2
|
-
|
|
3
|
-
services:
|
|
4
|
-
postgres:
|
|
5
|
-
image: postgres
|
|
6
|
-
restart: always
|
|
7
|
-
volumes:
|
|
8
|
-
- ./data/postgresql:/var/lib/postgresql/data
|
|
9
|
-
environment:
|
|
10
|
-
POSTGRES_USER: strapi
|
|
11
|
-
POSTGRES_PASSWORD: strapi
|
|
12
|
-
POSTGRES_DB: strapi
|
|
13
|
-
ports:
|
|
14
|
-
- '5432:5432'
|
|
15
|
-
|
|
16
|
-
mysql:
|
|
17
|
-
image: mysql
|
|
18
|
-
restart: always
|
|
19
|
-
command: --default-authentication-plugin=mysql_native_password
|
|
20
|
-
environment:
|
|
21
|
-
MYSQL_DATABASE: strapi
|
|
22
|
-
MYSQL_USER: strapi
|
|
23
|
-
MYSQL_PASSWORD: strapi
|
|
24
|
-
MYSQL_ROOT_HOST: '%'
|
|
25
|
-
MYSQL_ROOT_PASSWORD: strapi
|
|
26
|
-
volumes:
|
|
27
|
-
- ./data/mysql:/var/lib/mysql
|
|
28
|
-
ports:
|
|
29
|
-
- '3306:3306'
|
package/examples/index.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const util = require('util');
|
|
4
|
-
|
|
5
|
-
const { Database } = require('../lib/index');
|
|
6
|
-
const models = require('./models');
|
|
7
|
-
const connections = require('./connections');
|
|
8
|
-
|
|
9
|
-
async function main(connection) {
|
|
10
|
-
const orm = await Database.init({
|
|
11
|
-
connection,
|
|
12
|
-
models: Database.transformContentTypes(models),
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
// await orm.schema.drop();
|
|
17
|
-
// await orm.schema.create();
|
|
18
|
-
|
|
19
|
-
await orm.schema.reset();
|
|
20
|
-
|
|
21
|
-
let res;
|
|
22
|
-
|
|
23
|
-
const c1 = await orm.query('comment').create({
|
|
24
|
-
data: {
|
|
25
|
-
title: 'coucou',
|
|
26
|
-
},
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const c2 = await orm.query('video-comment').create({
|
|
30
|
-
data: {
|
|
31
|
-
title: 'coucou',
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
res = await orm.query('article').create({
|
|
36
|
-
data: {
|
|
37
|
-
dz: [
|
|
38
|
-
{
|
|
39
|
-
__type: 'comment',
|
|
40
|
-
id: c1.id,
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
__type: 'video-comment',
|
|
44
|
-
id: c2.id,
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
},
|
|
48
|
-
populate: {
|
|
49
|
-
dz: true,
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
log(res);
|
|
54
|
-
|
|
55
|
-
res = await orm.query('article').findMany({
|
|
56
|
-
populate: {
|
|
57
|
-
dz: true,
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
log(res);
|
|
62
|
-
|
|
63
|
-
// await tests(orm);
|
|
64
|
-
} finally {
|
|
65
|
-
orm.destroy();
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function log(res) {
|
|
70
|
-
console.log(util.inspect(res, null, null, true));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
main(connections.sqlite);
|
package/examples/models.js
DELETED
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const category = {
|
|
4
|
-
modelName: 'category',
|
|
5
|
-
uid: 'category',
|
|
6
|
-
collectionName: 'categories',
|
|
7
|
-
attributes: {
|
|
8
|
-
title: {
|
|
9
|
-
type: 'string',
|
|
10
|
-
},
|
|
11
|
-
price: {
|
|
12
|
-
type: 'integer',
|
|
13
|
-
required: true,
|
|
14
|
-
default: 12,
|
|
15
|
-
|
|
16
|
-
column: {
|
|
17
|
-
unique: true,
|
|
18
|
-
nonNullable: true,
|
|
19
|
-
unsigned: true,
|
|
20
|
-
defaultTo: 12,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
articles: {
|
|
24
|
-
type: 'relation',
|
|
25
|
-
relation: 'oneToMany',
|
|
26
|
-
target: 'article',
|
|
27
|
-
mappedBy: 'category',
|
|
28
|
-
},
|
|
29
|
-
compo: {
|
|
30
|
-
type: 'component',
|
|
31
|
-
component: 'compo',
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const article = {
|
|
37
|
-
modelName: 'article',
|
|
38
|
-
uid: 'article',
|
|
39
|
-
collectionName: 'articles',
|
|
40
|
-
attributes: {
|
|
41
|
-
title: {
|
|
42
|
-
type: 'string',
|
|
43
|
-
},
|
|
44
|
-
category: {
|
|
45
|
-
type: 'relation',
|
|
46
|
-
relation: 'manyToOne',
|
|
47
|
-
target: 'category',
|
|
48
|
-
inversedBy: 'articles',
|
|
49
|
-
// useJoinTable: false,
|
|
50
|
-
},
|
|
51
|
-
// tags: {
|
|
52
|
-
// type: 'relation',
|
|
53
|
-
// relation: 'manyToMany',
|
|
54
|
-
// target: 'tag',
|
|
55
|
-
// inversedBy: 'articles',
|
|
56
|
-
// },
|
|
57
|
-
// compo: {
|
|
58
|
-
// type: 'component',
|
|
59
|
-
// component: 'compo',
|
|
60
|
-
// // repeatable: true,
|
|
61
|
-
// },
|
|
62
|
-
// cover: {
|
|
63
|
-
// type: 'media',
|
|
64
|
-
// single: true,
|
|
65
|
-
// },
|
|
66
|
-
// gallery: {
|
|
67
|
-
// type: 'media',
|
|
68
|
-
// multiple: true,
|
|
69
|
-
// },
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const tags = {
|
|
74
|
-
modelName: 'tag',
|
|
75
|
-
uid: 'tag',
|
|
76
|
-
collectionName: 'tags',
|
|
77
|
-
attributes: {
|
|
78
|
-
name: {
|
|
79
|
-
type: 'string',
|
|
80
|
-
},
|
|
81
|
-
articles: {
|
|
82
|
-
type: 'relation',
|
|
83
|
-
relation: 'manyToMany',
|
|
84
|
-
target: 'article',
|
|
85
|
-
mappedBy: 'tag',
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const compo = {
|
|
91
|
-
modelName: 'compo',
|
|
92
|
-
uid: 'compo',
|
|
93
|
-
collectionName: 'compos',
|
|
94
|
-
attributes: {
|
|
95
|
-
key: {
|
|
96
|
-
type: 'string',
|
|
97
|
-
},
|
|
98
|
-
value: {
|
|
99
|
-
type: 'string',
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const user = {
|
|
105
|
-
modelName: 'user',
|
|
106
|
-
uid: 'user',
|
|
107
|
-
collectionName: 'users',
|
|
108
|
-
attributes: {
|
|
109
|
-
address: {
|
|
110
|
-
type: 'relation',
|
|
111
|
-
relation: 'oneToOne',
|
|
112
|
-
target: 'address',
|
|
113
|
-
inversedBy: 'user',
|
|
114
|
-
// useJoinTable: false,
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
const address = {
|
|
120
|
-
modelName: 'address',
|
|
121
|
-
uid: 'address',
|
|
122
|
-
collectionName: 'addresses',
|
|
123
|
-
attributes: {
|
|
124
|
-
name: {
|
|
125
|
-
type: 'string',
|
|
126
|
-
},
|
|
127
|
-
user: {
|
|
128
|
-
type: 'relation',
|
|
129
|
-
relation: 'oneToOne',
|
|
130
|
-
target: 'user',
|
|
131
|
-
mappedBy: 'address',
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const file = {
|
|
137
|
-
modelName: 'file',
|
|
138
|
-
uid: 'file',
|
|
139
|
-
collectionName: 'files',
|
|
140
|
-
attributes: {
|
|
141
|
-
name: {
|
|
142
|
-
type: 'string',
|
|
143
|
-
},
|
|
144
|
-
alternativeText: {
|
|
145
|
-
type: 'string',
|
|
146
|
-
},
|
|
147
|
-
caption: {
|
|
148
|
-
type: 'string',
|
|
149
|
-
},
|
|
150
|
-
width: {
|
|
151
|
-
type: 'integer',
|
|
152
|
-
},
|
|
153
|
-
height: {
|
|
154
|
-
type: 'integer',
|
|
155
|
-
},
|
|
156
|
-
formats: {
|
|
157
|
-
type: 'json',
|
|
158
|
-
},
|
|
159
|
-
hash: {
|
|
160
|
-
type: 'string',
|
|
161
|
-
},
|
|
162
|
-
ext: {
|
|
163
|
-
type: 'string',
|
|
164
|
-
},
|
|
165
|
-
mime: {
|
|
166
|
-
type: 'string',
|
|
167
|
-
},
|
|
168
|
-
size: {
|
|
169
|
-
type: 'decimal',
|
|
170
|
-
},
|
|
171
|
-
url: {
|
|
172
|
-
type: 'string',
|
|
173
|
-
},
|
|
174
|
-
previewUrl: {
|
|
175
|
-
type: 'string',
|
|
176
|
-
},
|
|
177
|
-
provider: {
|
|
178
|
-
type: 'string',
|
|
179
|
-
},
|
|
180
|
-
provider_metadata: {
|
|
181
|
-
type: 'json',
|
|
182
|
-
},
|
|
183
|
-
// related: {
|
|
184
|
-
// type: 'relation',
|
|
185
|
-
// relation: 'oneToMany',
|
|
186
|
-
// target: 'file_morph',
|
|
187
|
-
// mappedBy: 'file',
|
|
188
|
-
// },
|
|
189
|
-
// related: {
|
|
190
|
-
// type: 'relation',
|
|
191
|
-
// realtion: 'morphTo',
|
|
192
|
-
// },
|
|
193
|
-
},
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const fileMorph = {
|
|
197
|
-
modelName: 'file-morph',
|
|
198
|
-
uid: 'file-morph',
|
|
199
|
-
collectionName: 'file_morphs',
|
|
200
|
-
attributes: {
|
|
201
|
-
// file: {
|
|
202
|
-
// type: 'relation',
|
|
203
|
-
// relation: 'manyToOne',
|
|
204
|
-
// target: 'file',
|
|
205
|
-
// inversedBy: 'related',
|
|
206
|
-
// useJoinTable: false,
|
|
207
|
-
// },
|
|
208
|
-
},
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
const blogPost = {
|
|
212
|
-
modelName: 'blogPost',
|
|
213
|
-
uid: 'blogPost',
|
|
214
|
-
collectionName: 'blog_posts',
|
|
215
|
-
attributes: {
|
|
216
|
-
passwordField: {
|
|
217
|
-
type: 'password',
|
|
218
|
-
},
|
|
219
|
-
emailField: {
|
|
220
|
-
type: 'email',
|
|
221
|
-
},
|
|
222
|
-
stringField: {
|
|
223
|
-
type: 'string',
|
|
224
|
-
},
|
|
225
|
-
uidField: {
|
|
226
|
-
type: 'uid',
|
|
227
|
-
},
|
|
228
|
-
richtextField: {
|
|
229
|
-
type: 'richtext',
|
|
230
|
-
},
|
|
231
|
-
textField: {
|
|
232
|
-
type: 'text',
|
|
233
|
-
},
|
|
234
|
-
enumerationField: {
|
|
235
|
-
type: 'enumeration',
|
|
236
|
-
enum: ['A', 'B'],
|
|
237
|
-
},
|
|
238
|
-
jsonField: {
|
|
239
|
-
type: 'json',
|
|
240
|
-
},
|
|
241
|
-
bigintegerField: {
|
|
242
|
-
type: 'biginteger',
|
|
243
|
-
},
|
|
244
|
-
integerField: {
|
|
245
|
-
type: 'integer',
|
|
246
|
-
},
|
|
247
|
-
floatField: {
|
|
248
|
-
type: 'float',
|
|
249
|
-
},
|
|
250
|
-
decimalField: {
|
|
251
|
-
type: 'decimal',
|
|
252
|
-
},
|
|
253
|
-
dateField: {
|
|
254
|
-
type: 'date',
|
|
255
|
-
},
|
|
256
|
-
timeField: {
|
|
257
|
-
type: 'time',
|
|
258
|
-
},
|
|
259
|
-
datetimeField: {
|
|
260
|
-
type: 'datetime',
|
|
261
|
-
},
|
|
262
|
-
timestampField: {
|
|
263
|
-
type: 'timestamp',
|
|
264
|
-
},
|
|
265
|
-
booleanField: {
|
|
266
|
-
type: 'boolean',
|
|
267
|
-
},
|
|
268
|
-
},
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
module.exports = [category, article, tags, compo, user, address, file, fileMorph, blogPost];
|
|
272
|
-
|
|
273
|
-
// const article = {
|
|
274
|
-
// modelName: 'article',
|
|
275
|
-
// uid: 'article',
|
|
276
|
-
// collectionName: 'articles',
|
|
277
|
-
// attributes: {
|
|
278
|
-
// commentable: {
|
|
279
|
-
// type: 'relation',
|
|
280
|
-
// relation: 'morphToOne',
|
|
281
|
-
// },
|
|
282
|
-
// reportables: {
|
|
283
|
-
// type: 'relation',
|
|
284
|
-
// relation: 'morphToMany',
|
|
285
|
-
// },
|
|
286
|
-
// dz: {
|
|
287
|
-
// type: 'dynamiczone',
|
|
288
|
-
// components: ['comment', 'video-comment'],
|
|
289
|
-
// },
|
|
290
|
-
// },
|
|
291
|
-
// };
|
|
292
|
-
|
|
293
|
-
// const comment = {
|
|
294
|
-
// modelName: 'comment',
|
|
295
|
-
// uid: 'comment',
|
|
296
|
-
// collectionName: 'comments',
|
|
297
|
-
// attributes: {
|
|
298
|
-
// article: {
|
|
299
|
-
// type: 'relation',
|
|
300
|
-
// relation: 'morphOne',
|
|
301
|
-
// target: 'article',
|
|
302
|
-
// morphBy: 'commentable',
|
|
303
|
-
// },
|
|
304
|
-
// title: {
|
|
305
|
-
// type: 'string',
|
|
306
|
-
// },
|
|
307
|
-
// },
|
|
308
|
-
// };
|
|
309
|
-
|
|
310
|
-
// const videoComment = {
|
|
311
|
-
// modelName: 'video-comment',
|
|
312
|
-
// uid: 'video-comment',
|
|
313
|
-
// collectionName: 'video_comments',
|
|
314
|
-
// attributes: {
|
|
315
|
-
// articles: {
|
|
316
|
-
// type: 'relation',
|
|
317
|
-
// relation: 'morphMany',
|
|
318
|
-
// target: 'article',
|
|
319
|
-
// morphBy: 'commentable',
|
|
320
|
-
// },
|
|
321
|
-
// title: {
|
|
322
|
-
// type: 'string',
|
|
323
|
-
// },
|
|
324
|
-
// },
|
|
325
|
-
// };
|
|
326
|
-
|
|
327
|
-
// const folder = {
|
|
328
|
-
// modelName: 'folder',
|
|
329
|
-
// uid: 'folder',
|
|
330
|
-
// collectionName: 'folders',
|
|
331
|
-
// attributes: {
|
|
332
|
-
// articles: {
|
|
333
|
-
// type: 'relation',
|
|
334
|
-
// relation: 'morphMany',
|
|
335
|
-
// target: 'article',
|
|
336
|
-
// morphBy: 'reportables',
|
|
337
|
-
// },
|
|
338
|
-
// },
|
|
339
|
-
// };
|
|
340
|
-
|
|
341
|
-
// module.exports = [article, comment, videoComment, folder];
|
package/examples/typings.ts
DELETED