@strapi/database 4.0.0-beta.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.
Files changed (52) hide show
  1. package/LICENSE +22 -0
  2. package/examples/connections.js +36 -0
  3. package/examples/data.sqlite +0 -0
  4. package/examples/docker-compose.yml +29 -0
  5. package/examples/index.js +73 -0
  6. package/examples/models.js +341 -0
  7. package/examples/typings.ts +17 -0
  8. package/lib/dialects/dialect.js +45 -0
  9. package/lib/dialects/index.js +28 -0
  10. package/lib/dialects/mysql/index.js +51 -0
  11. package/lib/dialects/mysql/schema-inspector.js +203 -0
  12. package/lib/dialects/postgresql/index.js +49 -0
  13. package/lib/dialects/postgresql/schema-inspector.js +229 -0
  14. package/lib/dialects/sqlite/index.js +74 -0
  15. package/lib/dialects/sqlite/schema-inspector.js +151 -0
  16. package/lib/entity-manager.js +886 -0
  17. package/lib/entity-repository.js +110 -0
  18. package/lib/errors.js +14 -0
  19. package/lib/fields.d.ts +9 -0
  20. package/lib/fields.js +232 -0
  21. package/lib/index.d.ts +146 -0
  22. package/lib/index.js +60 -0
  23. package/lib/lifecycles/index.d.ts +50 -0
  24. package/lib/lifecycles/index.js +66 -0
  25. package/lib/lifecycles/subscribers/index.d.ts +9 -0
  26. package/lib/lifecycles/subscribers/models-lifecycles.js +19 -0
  27. package/lib/lifecycles/subscribers/timestamps.js +65 -0
  28. package/lib/metadata/index.js +219 -0
  29. package/lib/metadata/relations.js +488 -0
  30. package/lib/migrations/index.d.ts +9 -0
  31. package/lib/migrations/index.js +69 -0
  32. package/lib/migrations/storage.js +49 -0
  33. package/lib/query/helpers/index.js +10 -0
  34. package/lib/query/helpers/join.js +95 -0
  35. package/lib/query/helpers/order-by.js +70 -0
  36. package/lib/query/helpers/populate.js +652 -0
  37. package/lib/query/helpers/search.js +84 -0
  38. package/lib/query/helpers/transform.js +84 -0
  39. package/lib/query/helpers/where.js +322 -0
  40. package/lib/query/index.js +7 -0
  41. package/lib/query/query-builder.js +348 -0
  42. package/lib/schema/__tests__/schema-diff.test.js +181 -0
  43. package/lib/schema/builder.js +352 -0
  44. package/lib/schema/diff.js +376 -0
  45. package/lib/schema/index.d.ts +49 -0
  46. package/lib/schema/index.js +95 -0
  47. package/lib/schema/schema.js +209 -0
  48. package/lib/schema/storage.js +75 -0
  49. package/lib/types/index.d.ts +6 -0
  50. package/lib/types/index.js +34 -0
  51. package/lib/utils/content-types.js +41 -0
  52. package/package.json +39 -0
@@ -0,0 +1,352 @@
1
+ 'use strict';
2
+
3
+ const { isNil, prop, omit, castArray } = require('lodash/fp');
4
+ const debug = require('debug')('strapi::database');
5
+
6
+ module.exports = db => {
7
+ const helpers = createHelpers(db);
8
+
9
+ return {
10
+ /**
11
+ * Returns a knex schema builder instance
12
+ * @param {string} table - table name
13
+ */
14
+ getSchemaBuilder(table, trx = db.connection) {
15
+ return table.schema ? trx.schema.withSchema(table.schema) : trx.schema;
16
+ },
17
+
18
+ /**
19
+ * Creates schema in DB
20
+ * @param {Schema} schema - database schema
21
+ */
22
+ async createSchema(schema) {
23
+ // TODO: ensure database exists;
24
+
25
+ await db.connection.transaction(async trx => {
26
+ // create tables without FKs first do avoid ordering issues
27
+ await this.createTables(schema.tables, trx);
28
+ });
29
+ },
30
+
31
+ /**
32
+ * Creates a list of tables in a schema
33
+ * @param {KnexInstance} trx
34
+ * @param {Table[]} tables
35
+ */
36
+ async createTables(tables, trx) {
37
+ for (const table of tables) {
38
+ debug(`Creating table: ${table.name}`);
39
+ const schemaBuilder = this.getSchemaBuilder(table, trx);
40
+ await helpers.createTable(schemaBuilder, table);
41
+ }
42
+
43
+ // create FKs once all the tables exist
44
+ for (const table of tables) {
45
+ debug(`Creating table foreign keys: ${table.name}`);
46
+ const schemaBuilder = this.getSchemaBuilder(table, trx);
47
+ await helpers.createTableForeignKeys(schemaBuilder, table);
48
+ }
49
+ },
50
+ /**
51
+ * Drops schema from DB
52
+ * @param {Schema} schema - database schema
53
+ * @param {object} opts
54
+ * @param {boolean} opts.dropDatabase - weather to drop the entire database or simply drop the tables
55
+ */
56
+ async dropSchema(schema, { dropDatabase = false } = {}) {
57
+ if (dropDatabase) {
58
+ // TODO: drop database & return as it will drop everything
59
+ return;
60
+ }
61
+
62
+ await db.connection.transaction(async trx => {
63
+ for (const table of schema.tables.reverse()) {
64
+ const schemaBuilder = this.getSchemaBuilder(table, trx);
65
+ await helpers.dropTable(schemaBuilder, table);
66
+ }
67
+ });
68
+ },
69
+
70
+ /**
71
+ * Applies a schema diff update in the DB
72
+ * @param {*} schemaDiff
73
+ */
74
+ // TODO: implement force option to disable removal in DB
75
+ async updateSchema(schemaDiff) {
76
+ await db.dialect.startSchemaUpdate();
77
+ await db.connection.transaction(async trx => {
78
+ await this.createTables(schemaDiff.tables.added, trx);
79
+
80
+ // drop all delete table foreign keys then delete the tables
81
+ for (const table of schemaDiff.tables.removed) {
82
+ debug(`Removing table foreign keys: ${table.name}`);
83
+
84
+ const schemaBuilder = this.getSchemaBuilder(table, trx);
85
+ await helpers.dropTableForeignKeys(schemaBuilder, table);
86
+ }
87
+
88
+ for (const table of schemaDiff.tables.removed) {
89
+ debug(`Removing table: ${table.name}`);
90
+
91
+ const schemaBuilder = this.getSchemaBuilder(table, trx);
92
+ await helpers.dropTable(schemaBuilder, table);
93
+ }
94
+
95
+ for (const table of schemaDiff.tables.updated) {
96
+ debug(`Updating table: ${table.name}`);
97
+ // alter table
98
+ const schemaBuilder = this.getSchemaBuilder(table, trx);
99
+
100
+ await helpers.alterTable(schemaBuilder, table);
101
+ }
102
+ });
103
+
104
+ await db.dialect.endSchemaUpdate();
105
+ },
106
+ };
107
+ };
108
+
109
+ const createHelpers = db => {
110
+ /**
111
+ * Creates a foreign key on a table
112
+ * @param {Knex.TableBuilder} tableBuilder
113
+ * @param {ForeignKey} foreignKey
114
+ */
115
+ const createForeignKey = (tableBuilder, foreignKey) => {
116
+ const { name, columns, referencedColumns, referencedTable, onDelete, onUpdate } = foreignKey;
117
+
118
+ const constraint = tableBuilder
119
+ .foreign(columns, name)
120
+ .references(referencedColumns)
121
+ .inTable(referencedTable);
122
+
123
+ if (onDelete) {
124
+ constraint.onDelete(onDelete);
125
+ }
126
+
127
+ if (onUpdate) {
128
+ constraint.onUpdate(onUpdate);
129
+ }
130
+ };
131
+
132
+ /**
133
+ * Drops a foreign key from a table
134
+ * @param {Knex.TableBuilder} tableBuilder
135
+ * @param {ForeignKey} foreignKey
136
+ */
137
+ const dropForeignKey = (tableBuilder, foreignKey) => {
138
+ const { name, columns } = foreignKey;
139
+
140
+ tableBuilder.dropForeign(columns, name);
141
+ };
142
+
143
+ /**
144
+ * Creates an index on a table
145
+ * @param {Knex.TableBuilder} tableBuilder
146
+ * @param {Index} index
147
+ */
148
+ const createIndex = (tableBuilder, index) => {
149
+ const { type, columns, name } = index;
150
+
151
+ switch (type) {
152
+ case 'primary': {
153
+ return tableBuilder.primary(columns, name);
154
+ }
155
+ case 'unique': {
156
+ return tableBuilder.unique(columns, name);
157
+ }
158
+ default: {
159
+ return tableBuilder.index(columns, name, type);
160
+ }
161
+ }
162
+ };
163
+
164
+ /**
165
+ * Drops an index from table
166
+ * @param {Knex.TableBuilder} tableBuilder
167
+ * @param {Index} index
168
+ */
169
+ const dropIndex = (tableBuilder, index) => {
170
+ const { type, columns, name } = index;
171
+
172
+ switch (type) {
173
+ case 'primary': {
174
+ return tableBuilder.dropPrimary(name);
175
+ }
176
+ case 'unique': {
177
+ return tableBuilder.dropUnique(columns, name);
178
+ }
179
+ default: {
180
+ return tableBuilder.dropIndex(columns, name);
181
+ }
182
+ }
183
+ };
184
+
185
+ /**
186
+ * Creates a column in a table
187
+ * @param {Knex.TableBuilder} tableBuilder
188
+ * @param {Column} column
189
+ */
190
+ const createColumn = (tableBuilder, column) => {
191
+ const { type, name, args = [], defaultTo, unsigned, notNullable } = column;
192
+
193
+ const col = tableBuilder[type](name, ...args);
194
+
195
+ if (unsigned === true) {
196
+ col.unsigned();
197
+ }
198
+
199
+ if (!isNil(defaultTo)) {
200
+ const [value, opts] = castArray(defaultTo);
201
+
202
+ if (prop('isRaw', opts)) {
203
+ col.defaultTo(db.connection.raw(value), omit('isRaw', opts));
204
+ } else {
205
+ col.defaultTo(value, opts);
206
+ }
207
+ }
208
+
209
+ if (notNullable === true) {
210
+ col.notNullable();
211
+ } else {
212
+ col.nullable();
213
+ }
214
+
215
+ return col;
216
+ };
217
+
218
+ /**
219
+ * Drops a column from a table
220
+ * @param {Knex.TableBuilder} tableBuilder
221
+ * @param {Column} column
222
+ */
223
+ const dropColumn = (tableBuilder, column) => {
224
+ tableBuilder.dropColumn(column.name);
225
+ };
226
+
227
+ /**
228
+ * Creates a table in a database
229
+ * @param {SchemaBuilder} schemaBuilder
230
+ * @param {Table} table
231
+ */
232
+ const createTable = async (schemaBuilder, table) => {
233
+ await schemaBuilder.createTable(table.name, tableBuilder => {
234
+ // columns
235
+ (table.columns || []).forEach(column => createColumn(tableBuilder, column));
236
+
237
+ // indexes
238
+ (table.indexes || []).forEach(index => createIndex(tableBuilder, index));
239
+
240
+ // foreign keys
241
+
242
+ if (!db.dialect.canAlterConstraints()) {
243
+ (table.foreignKeys || []).forEach(foreignKey => createForeignKey(tableBuilder, foreignKey));
244
+ }
245
+ });
246
+ };
247
+
248
+ const alterTable = async (schemaBuilder, table) => {
249
+ await schemaBuilder.alterTable(table.name, tableBuilder => {
250
+ // Delete indexes / fks / columns
251
+
252
+ for (const removedIndex of table.indexes.removed) {
253
+ debug(`Dropping index ${removedIndex.name}`);
254
+ dropIndex(tableBuilder, removedIndex);
255
+ }
256
+
257
+ for (const updateddIndex of table.indexes.updated) {
258
+ debug(`Dropping updated index ${updateddIndex.name}`);
259
+ dropIndex(tableBuilder, updateddIndex.object);
260
+ }
261
+
262
+ for (const removedForeignKey of table.foreignKeys.removed) {
263
+ debug(`Dropping foreign key ${removedForeignKey.name}`);
264
+ dropForeignKey(tableBuilder, removedForeignKey);
265
+ }
266
+
267
+ for (const updatedForeignKey of table.foreignKeys.updated) {
268
+ debug(`Dropping updated foreign key ${updatedForeignKey.name}`);
269
+ dropForeignKey(tableBuilder, updatedForeignKey.object);
270
+ }
271
+
272
+ for (const removedColumn of table.columns.removed) {
273
+ debug(`Dropping column ${removedColumn.name}`);
274
+ dropColumn(tableBuilder, removedColumn);
275
+ }
276
+
277
+ // Update existing columns / foreign keys / indexes
278
+
279
+ for (const updatedColumn of table.columns.updated) {
280
+ debug(`Updating column ${updatedColumn.name}`);
281
+
282
+ const { object } = updatedColumn;
283
+
284
+ createColumn(tableBuilder, object).alter();
285
+ }
286
+
287
+ for (const updatedForeignKey of table.foreignKeys.updated) {
288
+ debug(`Recreating updated foreign key ${updatedForeignKey.name}`);
289
+ createForeignKey(tableBuilder, updatedForeignKey.object);
290
+ }
291
+
292
+ for (const updatedIndex of table.indexes.updated) {
293
+ debug(`Recreating updated index ${updatedIndex.name}`);
294
+ createIndex(tableBuilder, updatedIndex.object);
295
+ }
296
+
297
+ for (const addedColumn of table.columns.added) {
298
+ debug(`Creating column ${addedColumn.name}`);
299
+ createColumn(tableBuilder, addedColumn);
300
+ }
301
+
302
+ for (const addedForeignKey of table.foreignKeys.added) {
303
+ debug(`Creating foreign keys ${addedForeignKey.name}`);
304
+ createForeignKey(tableBuilder, addedForeignKey);
305
+ }
306
+
307
+ for (const addedIndex of table.indexes.added) {
308
+ debug(`Creating index ${addedIndex.name}`);
309
+ createIndex(tableBuilder, addedIndex);
310
+ }
311
+ });
312
+ };
313
+
314
+ /**
315
+ * Drops a table from a database
316
+ * @param {Knex.SchemaBuilder} schemaBuilder
317
+ * @param {Table} table
318
+ */
319
+ const dropTable = (schemaBuilder, table) => schemaBuilder.dropTableIfExists(table.name);
320
+
321
+ /**
322
+ * Creates a table foreign keys constraints
323
+ * @param {SchemaBuilder} schemaBuilder
324
+ * @param {Table} table
325
+ */
326
+ const createTableForeignKeys = async (schemaBuilder, table) => {
327
+ // foreign keys
328
+ await schemaBuilder.table(table.name, tableBuilder => {
329
+ (table.foreignKeys || []).forEach(foreignKey => createForeignKey(tableBuilder, foreignKey));
330
+ });
331
+ };
332
+
333
+ /**
334
+ * Drops a table foreign keys constraints
335
+ * @param {SchemaBuilder} schemaBuilder
336
+ * @param {Table} table
337
+ */
338
+ const dropTableForeignKeys = async (schemaBuilder, table) => {
339
+ // foreign keys
340
+ await schemaBuilder.table(table.name, tableBuilder => {
341
+ (table.foreignKeys || []).forEach(foreignKey => dropForeignKey(tableBuilder, foreignKey));
342
+ });
343
+ };
344
+
345
+ return {
346
+ createTable,
347
+ alterTable,
348
+ dropTable,
349
+ createTableForeignKeys,
350
+ dropTableForeignKeys,
351
+ };
352
+ };