@strapi/database 4.0.0-next.8 → 4.0.2

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 (46) hide show
  1. package/jest.config.js +10 -0
  2. package/lib/dialects/dialect.js +45 -0
  3. package/lib/dialects/index.js +6 -112
  4. package/lib/dialects/mysql/index.js +51 -0
  5. package/lib/dialects/mysql/schema-inspector.js +199 -0
  6. package/lib/dialects/postgresql/index.js +49 -0
  7. package/lib/dialects/postgresql/schema-inspector.js +232 -0
  8. package/lib/dialects/sqlite/index.js +73 -0
  9. package/lib/dialects/sqlite/schema-inspector.js +151 -0
  10. package/lib/entity-manager.js +18 -14
  11. package/lib/entity-repository.js +2 -3
  12. package/lib/errors.js +44 -2
  13. package/lib/fields.d.ts +2 -3
  14. package/lib/fields.js +7 -16
  15. package/lib/index.d.ts +67 -22
  16. package/lib/index.js +44 -27
  17. package/lib/lifecycles/index.d.ts +50 -0
  18. package/lib/{lifecycles.js → lifecycles/index.js} +25 -14
  19. package/lib/lifecycles/subscribers/index.d.ts +9 -0
  20. package/lib/lifecycles/subscribers/models-lifecycles.js +19 -0
  21. package/lib/lifecycles/subscribers/timestamps.js +65 -0
  22. package/lib/metadata/index.js +84 -95
  23. package/lib/metadata/relations.js +16 -0
  24. package/lib/migrations/index.d.ts +9 -0
  25. package/lib/migrations/index.js +69 -0
  26. package/lib/migrations/storage.js +51 -0
  27. package/lib/query/helpers/join.js +3 -5
  28. package/lib/query/helpers/order-by.js +21 -11
  29. package/lib/query/helpers/populate.js +35 -10
  30. package/lib/query/helpers/search.js +26 -12
  31. package/lib/query/helpers/transform.js +42 -14
  32. package/lib/query/helpers/where.js +92 -57
  33. package/lib/query/query-builder.js +116 -34
  34. package/lib/schema/__tests__/schema-diff.test.js +14 -1
  35. package/lib/schema/builder.js +315 -284
  36. package/lib/schema/diff.js +374 -0
  37. package/lib/schema/index.d.ts +49 -0
  38. package/lib/schema/index.js +47 -50
  39. package/lib/schema/schema.js +21 -26
  40. package/lib/schema/storage.js +79 -0
  41. package/lib/utils/content-types.js +0 -1
  42. package/package.json +26 -21
  43. package/examples/data.sqlite +0 -0
  44. package/lib/configuration.js +0 -49
  45. package/lib/schema/schema-diff.js +0 -337
  46. package/lib/schema/schema-storage.js +0 -44
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+
5
+ const TABLE_NAME = 'strapi_database_schema';
6
+
7
+ module.exports = db => {
8
+ const hasSchemaTable = () => db.getSchemaConnection().hasTable(TABLE_NAME);
9
+
10
+ const createSchemaTable = () => {
11
+ return db.getSchemaConnection().createTable(TABLE_NAME, t => {
12
+ t.increments('id');
13
+ t.json('schema');
14
+ t.datetime('time', { useTz: false });
15
+ t.string('hash');
16
+ });
17
+ };
18
+
19
+ const checkTableExists = async () => {
20
+ if (!(await hasSchemaTable())) {
21
+ await createSchemaTable();
22
+ }
23
+ };
24
+
25
+ return {
26
+ async read() {
27
+ await checkTableExists();
28
+
29
+ const res = await db
30
+ .getConnection()
31
+ .select('*')
32
+ .from(TABLE_NAME)
33
+ .orderBy('time', 'DESC')
34
+ .first();
35
+
36
+ if (!res) {
37
+ return null;
38
+ }
39
+
40
+ const parsedSchema = typeof res.schema === 'object' ? res.schema : JSON.parse(res.schema);
41
+
42
+ return {
43
+ ...res,
44
+ schema: parsedSchema,
45
+ };
46
+ },
47
+
48
+ hashSchema(schema) {
49
+ return crypto
50
+ .createHash('md5')
51
+ .update(JSON.stringify(schema))
52
+ .digest('hex');
53
+ },
54
+
55
+ async add(schema) {
56
+ await checkTableExists();
57
+
58
+ // NOTE: we can remove this to add history
59
+ await db.getConnection(TABLE_NAME).delete();
60
+
61
+ const time = new Date();
62
+
63
+ await db
64
+ .getConnection()
65
+ .insert({
66
+ schema: JSON.stringify(schema),
67
+ hash: this.hashSchema(schema),
68
+ time,
69
+ })
70
+ .into(TABLE_NAME);
71
+ },
72
+
73
+ async clear() {
74
+ await checkTableExists();
75
+
76
+ await db.getConnection(TABLE_NAME).truncate();
77
+ },
78
+ };
79
+ };
@@ -3,7 +3,6 @@
3
3
  const transformAttribute = attribute => {
4
4
  switch (attribute.type) {
5
5
  case 'media': {
6
- // TODO: handle a filter on field
7
6
  return {
8
7
  type: 'relation',
9
8
  relation: attribute.multiple === true ? 'morphMany' : 'morphOne',
package/package.json CHANGED
@@ -1,41 +1,46 @@
1
1
  {
2
2
  "name": "@strapi/database",
3
- "version": "4.0.0-next.8",
3
+ "version": "4.0.2",
4
4
  "description": "Strapi's database layer",
5
5
  "homepage": "https://strapi.io",
6
- "main": "./lib/index.js",
7
- "scripts": {
8
- "test": "echo \"no tests yet\""
6
+ "bugs": {
7
+ "url": "https://github.com/strapi/strapi/issues"
9
8
  },
10
- "directories": {
11
- "lib": "./lib"
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git://github.com/strapi/strapi.git"
12
12
  },
13
+ "license": "SEE LICENSE IN LICENSE",
13
14
  "author": {
14
- "name": "Strapi team",
15
+ "name": "Strapi Solutions SAS",
15
16
  "email": "hi@strapi.io",
16
17
  "url": "https://strapi.io"
17
18
  },
18
- "repository": {
19
- "type": "git",
20
- "url": "git://github.com/strapi/strapi.git"
21
- },
22
- "bugs": {
23
- "url": "https://github.com/strapi/strapi/issues"
19
+ "maintainers": [
20
+ {
21
+ "name": "Strapi Solutions SAS",
22
+ "email": "hi@strapi.io",
23
+ "url": "https://strapi.io"
24
+ }
25
+ ],
26
+ "main": "./lib/index.js",
27
+ "directories": {
28
+ "lib": "./lib"
24
29
  },
25
- "engines": {
26
- "node": ">=12.x.x <=16.x.x",
27
- "npm": ">=6.0.0"
30
+ "scripts": {
31
+ "test:unit": "jest --verbose"
28
32
  },
29
- "license": "SEE LICENSE IN LICENSE",
30
33
  "dependencies": {
31
34
  "date-fns": "2.22.1",
32
35
  "debug": "4.3.1",
33
36
  "fs-extra": "10.0.0",
34
37
  "knex": "0.95.6",
35
- "lodash": "4.17.21"
38
+ "lodash": "4.17.21",
39
+ "umzug": "2.3.0"
36
40
  },
37
- "devDependencies": {
38
- "sqlite3": "5.0.2"
41
+ "engines": {
42
+ "node": ">=12.x.x <=16.x.x",
43
+ "npm": ">=6.0.0"
39
44
  },
40
- "gitHead": "e3452f6662a45a4ba96e96861e076e313b297666"
45
+ "gitHead": "fd656a47698e0a33aae42abd4330410c8cba1d08"
41
46
  }
Binary file
@@ -1,49 +0,0 @@
1
- 'use strict';
2
- const { prop, defaultsDeep } = require('lodash/fp');
3
-
4
- /*
5
- config/database.js
6
-
7
- {
8
- connector: '',
9
- connection: {},
10
- migration: {},
11
- seed: {},
12
- schema: {
13
- autoSync: true,
14
- forceSync: true
15
- }
16
- }
17
-
18
- */
19
-
20
- class Configuration {
21
- constructor(config) {
22
- this.config = config;
23
- }
24
-
25
- static from(config) {
26
- if (config instanceof Configuration) {
27
- return config;
28
- }
29
-
30
- return new Configuration(defaultsDeep(config, Configuration.defaults));
31
- }
32
-
33
- get(path) {
34
- return prop(path, this.config);
35
- }
36
- }
37
-
38
- Configuration.defaults = {
39
- connector: '@strapi/connector-sql',
40
- migration: {
41
- //
42
- },
43
- seed: {
44
- //
45
- },
46
- models: [],
47
- };
48
-
49
- module.exports = Configuration;
@@ -1,337 +0,0 @@
1
- 'use strict';
2
-
3
- const _ = require('lodash/fp');
4
-
5
- const statuses = {
6
- CHANGED: 'CHANGED',
7
- UNCHANGED: 'UNCHANGED',
8
- };
9
-
10
- // NOTE:We could move the schema to use maps of tables & columns instead of arrays to make it easier to diff
11
- // => this will make the creation a bit more complicated (ordering, Object.values(tables | columns)) -> not a big pbl
12
-
13
- const helpers = {
14
- hasTable(schema, tableName) {
15
- return schema.tables.findIndex(table => table.name === tableName) !== -1;
16
- },
17
- findTable(schema, tableName) {
18
- return schema.tables.find(table => table.name === tableName);
19
- },
20
-
21
- hasColumn(table, columnName) {
22
- return table.columns.findIndex(column => column.name === columnName) !== -1;
23
- },
24
- findColumn(table, columnName) {
25
- return table.columns.find(column => column.name === columnName);
26
- },
27
-
28
- hasIndex(table, columnName) {
29
- return table.indexes.findIndex(column => column.name === columnName) !== -1;
30
- },
31
- findIndex(table, columnName) {
32
- return table.indexes.find(column => column.name === columnName);
33
- },
34
-
35
- hasForeignKey(table, columnName) {
36
- return table.foreignKeys.findIndex(column => column.name === columnName) !== -1;
37
- },
38
- findForeignKey(table, columnName) {
39
- return table.foreignKeys.find(column => column.name === columnName);
40
- },
41
- };
42
-
43
- const diffProperties = (srcObject, destObject) => {
44
- const addedProperties = [];
45
- const updatedProperties = [];
46
- const unchangedProperties = [];
47
- const removedProperties = [];
48
-
49
- for (const key in destObject) {
50
- const value = destObject[key];
51
-
52
- if (_.has(key, srcObject)) {
53
- const srcValue = srcObject[key];
54
-
55
- if (!_.isEqual(srcValue, value)) {
56
- updatedProperties.push({ key, oldValue: srcValue, value });
57
- } else {
58
- unchangedProperties.push({ key, value });
59
- }
60
- } else {
61
- addedProperties.push({ key, value });
62
- }
63
- }
64
-
65
- for (const key in srcObject) {
66
- const value = srcObject[key];
67
-
68
- if (!_.has(key, destObject)) {
69
- removedProperties.push({ key, oldValue: value });
70
- }
71
- }
72
-
73
- const hasChanged = [addedProperties, updatedProperties, removedProperties].some(
74
- arr => arr.length > 0
75
- );
76
-
77
- return {
78
- status: hasChanged ? statuses.CHANGED : statuses.UNCHANGED,
79
- diff: {
80
- name: destObject.name,
81
- object: destObject,
82
- // NOTE: maybe put into properties: {}
83
- added: addedProperties,
84
- updated: updatedProperties,
85
- unchanged: unchangedProperties,
86
- removed: removedProperties,
87
- },
88
- };
89
- };
90
-
91
- const diffTableColumns = (srcTable, destTable) => {
92
- /*
93
- for each column in dest table
94
- if exists in src table
95
- diff the two columns
96
- check properties
97
- if diff has changes push to changed
98
- else push to unchanged
99
- else push to added
100
-
101
- for each column in src table
102
- if not exists in dest table push to removed
103
-
104
- */
105
-
106
- const addedColumns = [];
107
- const updatedColumns = [];
108
- const unchangedColumns = [];
109
- const removedColumns = [];
110
-
111
- for (const destColumn of destTable.columns) {
112
- if (helpers.hasColumn(srcTable, destColumn.name)) {
113
- const srcColumn = helpers.findColumn(srcTable, destColumn.name);
114
- const { status, diff } = diffProperties(srcColumn, destColumn);
115
-
116
- if (status === statuses.CHANGED) {
117
- updatedColumns.push(diff);
118
- } else {
119
- unchangedColumns.push(srcColumn);
120
- }
121
- } else {
122
- addedColumns.push(destColumn);
123
- }
124
- }
125
-
126
- for (const srcColumn of srcTable.columns) {
127
- if (!helpers.hasColumn(destTable, srcColumn.name)) {
128
- removedColumns.push(srcColumn);
129
- }
130
- }
131
-
132
- const hasChanged = [addedColumns, updatedColumns, removedColumns].some(arr => arr.length > 0);
133
-
134
- return {
135
- status: hasChanged ? statuses.CHANGED : statuses.UNCHANGED,
136
- diff: {
137
- added: addedColumns,
138
- updated: updatedColumns,
139
- unchanged: unchangedColumns,
140
- removed: removedColumns,
141
- },
142
- };
143
- };
144
-
145
- const diffTableIndexes = (srcTable, destTable) => {
146
- /*
147
- for each indexes in dest table
148
- if exists in src table
149
- diff the two indexes
150
- check properties
151
- if diff has changes push to changed
152
- else push to unchanged
153
- else push to added
154
-
155
- for each indexes in src table
156
- if not exists in dest table push to removed
157
- */
158
-
159
- const addedIndexes = [];
160
- const updatedIndexes = [];
161
- const unchangedIndexes = [];
162
- const removedIndexes = [];
163
-
164
- for (const destIndex of destTable.indexes) {
165
- if (helpers.hasIndex(srcTable, destIndex.name)) {
166
- const srcIndex = helpers.findIndex(srcTable, destIndex.name);
167
- const { status, diff } = diffProperties(srcIndex, destIndex);
168
-
169
- if (status === statuses.CHANGED) {
170
- updatedIndexes.push(diff);
171
- } else {
172
- unchangedIndexes.push(srcIndex);
173
- }
174
- } else {
175
- addedIndexes.push(destIndex);
176
- }
177
- }
178
-
179
- for (const srcIndex of srcTable.indexes) {
180
- if (!helpers.hasIndex(destTable, srcIndex.name)) {
181
- removedIndexes.push(srcIndex);
182
- }
183
- }
184
-
185
- const hasChanged = [addedIndexes, updatedIndexes, removedIndexes].some(arr => arr.length > 0);
186
-
187
- return {
188
- status: hasChanged ? statuses.CHANGED : statuses.UNCHANGED,
189
- diff: {
190
- added: addedIndexes,
191
- updated: updatedIndexes,
192
- unchanged: unchangedIndexes,
193
- removed: removedIndexes,
194
- },
195
- };
196
- };
197
-
198
- const diffTableForeignKeys = (srcTable, destTable) => {
199
- /*
200
- for each foreginKeys in dest table
201
- if exists in src table
202
- diff the two foreginKeys
203
- check properties
204
- if diff has changes push to changed
205
- else push to unchanged
206
- else push to added
207
-
208
- for each foreginKeys in src table
209
- if not exists in dest table push to removed
210
- */
211
-
212
- const addedForeignKeys = [];
213
- const updatedForeignKeys = [];
214
- const unchangedForeignKeys = [];
215
- const removedForeignKeys = [];
216
-
217
- for (const destForeignKey of destTable.foreignKeys) {
218
- if (helpers.hasForeignKey(srcTable, destForeignKey.name)) {
219
- const srcForeignKey = helpers.findForeignKey(srcTable, destForeignKey.name);
220
- const { status, diff } = diffProperties(srcForeignKey, destForeignKey);
221
-
222
- if (status === statuses.CHANGED) {
223
- updatedForeignKeys.push(diff);
224
- } else {
225
- unchangedForeignKeys.push(srcForeignKey);
226
- }
227
- } else {
228
- addedForeignKeys.push(destForeignKey);
229
- }
230
- }
231
-
232
- for (const srcForeignKey of srcTable.foreignKeys) {
233
- if (!helpers.hasForeignKey(destTable, srcForeignKey.name)) {
234
- removedForeignKeys.push(srcForeignKey);
235
- }
236
- }
237
-
238
- const hasChanged = [addedForeignKeys, updatedForeignKeys, removedForeignKeys].some(
239
- arr => arr.length > 0
240
- );
241
-
242
- return {
243
- status: hasChanged ? statuses.CHANGED : statuses.UNCHANGED,
244
- diff: {
245
- added: addedForeignKeys,
246
- updated: updatedForeignKeys,
247
- unchanged: unchangedForeignKeys,
248
- removed: removedForeignKeys,
249
- },
250
- };
251
- };
252
-
253
- const hasChangedStatus = diff => diff.status === statuses.CHANGED;
254
-
255
- const diffTables = (srcTable, destTable) => {
256
- const columnsDiff = diffTableColumns(srcTable, destTable);
257
- const indexesDiff = diffTableIndexes(srcTable, destTable);
258
- const foreignKeysDiff = diffTableForeignKeys(srcTable, destTable);
259
-
260
- const hasChanged = [columnsDiff, indexesDiff, foreignKeysDiff].some(hasChangedStatus);
261
-
262
- return {
263
- status: hasChanged ? statuses.CHANGED : statuses.UNCHANGED,
264
- diff: {
265
- name: srcTable.name,
266
- indexes: indexesDiff.diff,
267
- foreignKeys: foreignKeysDiff.diff,
268
- columns: columnsDiff.diff,
269
- },
270
- };
271
- };
272
-
273
- const diffSchemas = (srcSchema, destSchema) => {
274
- // NOTE: We could eventually try to detect renames in tables & columns or hint them when making changes
275
- // TODO: let's try to see how we can avoid restarts completely by applying these dynamically in dev mode
276
-
277
- /*
278
- for each table in dest
279
- if exists in src then
280
-
281
- diff two tables
282
-
283
- if diff has changes push to changed
284
- else push to unchanged
285
- else push to added
286
-
287
- for each table in src check if it still exists
288
- if table not in dest push to removed
289
-
290
- */
291
-
292
- const addedTables = [];
293
- const updatedTables = [];
294
- const unchangedTables = [];
295
- const removedTables = [];
296
-
297
- for (const destTable of destSchema.tables) {
298
- if (helpers.hasTable(srcSchema, destTable.name)) {
299
- // either changed or unchanged
300
- const srcTable = helpers.findTable(srcSchema, destTable.name);
301
-
302
- const { status, diff } = diffTables(srcTable, destTable);
303
-
304
- if (status === statuses.CHANGED) {
305
- updatedTables.push(diff);
306
- } else {
307
- unchangedTables.push(srcTable);
308
- }
309
- } else {
310
- addedTables.push(destTable);
311
- }
312
- }
313
-
314
- for (const srcTable of srcSchema.tables) {
315
- if (!helpers.hasTable(destSchema, srcTable.name)) {
316
- removedTables.push(srcTable);
317
- }
318
- }
319
-
320
- const hasChanged = [addedTables, updatedTables, removedTables].some(arr => arr.length > 0);
321
-
322
- return {
323
- status: hasChanged ? statuses.CHANGED : statuses.UNCHANGED,
324
- diff: {
325
- tables: {
326
- added: addedTables,
327
- updated: updatedTables,
328
- unchanged: unchangedTables,
329
- removed: removedTables,
330
- },
331
- },
332
- };
333
- };
334
-
335
- module.exports = {
336
- diffSchemas,
337
- };
@@ -1,44 +0,0 @@
1
- 'use strict';
2
-
3
- module.exports = db => ({
4
- async read() {
5
- /*
6
- NOTE:
7
- 1- read the tables structure etc
8
- 2- select from a specific table
9
- */
10
-
11
- if (await db.connection.schema.hasTable('strapi_database_schema')) {
12
- // NOTE: We could store and history of database schemas to fix / rollback etc ?
13
- const res = await db.connection
14
- .select('*')
15
- .from('strapi_database_schema')
16
- .orderBy('id', 'DESC')
17
- .first();
18
-
19
- if (!res) {
20
- return null;
21
- }
22
-
23
- return typeof res.schema === 'object' ? res.schema : JSON.parse(res.schema);
24
- }
25
-
26
- return null;
27
- },
28
-
29
- async update(schema) {
30
- await db.connection('strapi_database_schema').update({
31
- schema: JSON.stringify(schema),
32
- });
33
- },
34
-
35
- async create(schema) {
36
- await db.connection('strapi_database_schema').insert({
37
- schema: JSON.stringify(schema),
38
- });
39
- },
40
-
41
- async clear() {
42
- await db.connection('strapi_database_schema').del();
43
- },
44
- });