@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,19 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @typedef {import(".").Subscriber } Subscriber
5
+ */
6
+
7
+ /**
8
+ * For each model try to run it's lifecycles function if any is defined
9
+ * @type {Subscriber}
10
+ */
11
+ const modelsLifecyclesSubscriber = async event => {
12
+ const { model } = event;
13
+
14
+ if (event.action in model.lifecycles) {
15
+ await model.lifecycles[event.action](event);
16
+ }
17
+ };
18
+
19
+ module.exports = modelsLifecyclesSubscriber;
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+
5
+ /**
6
+ * @typedef {import(".").Subscriber } Subscriber
7
+ * @typedef { import("../").Event } Event
8
+ */
9
+
10
+ // NOTE: we could add onCreate & onUpdate on field level to do this instead
11
+
12
+ /**
13
+ * @type {Subscriber}
14
+ */
15
+ const timestampsLifecyclesSubscriber = {
16
+ /**
17
+ * Init createdAt & updatedAt before create
18
+ * @param {Event} event
19
+ */
20
+ beforeCreate(event) {
21
+ const { data } = event.params;
22
+
23
+ const now = new Date();
24
+ _.defaults(data, { createdAt: now, updatedAt: now });
25
+ },
26
+
27
+ /**
28
+ * Init createdAt & updatedAt before create
29
+ * @param {Event} event
30
+ */
31
+ beforeCreateMany(event) {
32
+ const { data } = event.params;
33
+
34
+ const now = new Date();
35
+ if (_.isArray(data)) {
36
+ data.forEach(data => _.defaults(data, { createdAt: now, updatedAt: now }));
37
+ }
38
+ },
39
+
40
+ /**
41
+ * Update updatedAt before update
42
+ * @param {Event} event
43
+ */
44
+ beforeUpdate(event) {
45
+ const { data } = event.params;
46
+
47
+ const now = new Date();
48
+ _.assign(data, { updatedAt: now });
49
+ },
50
+
51
+ /**
52
+ * Update updatedAt before update
53
+ * @param {Event} event
54
+ */
55
+ beforeUpdateMany(event) {
56
+ const { data } = event.params;
57
+
58
+ const now = new Date();
59
+ if (_.isArray(data)) {
60
+ data.forEach(data => _.assign(data, { updatedAt: now }));
61
+ }
62
+ },
63
+ };
64
+
65
+ module.exports = timestampsLifecyclesSubscriber;
@@ -0,0 +1,219 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash/fp');
4
+
5
+ const types = require('../types');
6
+ const { createRelation } = require('./relations');
7
+
8
+ class Metadata extends Map {
9
+ add(meta) {
10
+ return this.set(meta.uid, meta);
11
+ }
12
+ }
13
+
14
+ // TODO: check if there isn't an attribute with an id already
15
+ /**
16
+ * Create Metadata from models configurations
17
+ * @param {object[]} models
18
+ * @returns {Metadata}
19
+ */
20
+ const createMetadata = (models = []) => {
21
+ const metadata = new Metadata();
22
+
23
+ // init pass
24
+ for (const model of _.cloneDeep(models)) {
25
+ metadata.add({
26
+ singularName: model.singularName,
27
+ uid: model.uid,
28
+ tableName: model.tableName,
29
+ attributes: {
30
+ id: {
31
+ type: 'increments',
32
+ },
33
+ ...model.attributes,
34
+ },
35
+ lifecycles: model.lifecycles || {},
36
+ });
37
+ }
38
+
39
+ // build compos / relations
40
+ for (const meta of metadata.values()) {
41
+ if (hasComponentsOrDz(meta)) {
42
+ const compoLinkModelMeta = createCompoLinkModelMeta(meta);
43
+ meta.componentLink = compoLinkModelMeta;
44
+ metadata.add(compoLinkModelMeta);
45
+ }
46
+
47
+ for (const [attributeName, attribute] of Object.entries(meta.attributes)) {
48
+ try {
49
+ if (types.isComponent(attribute.type)) {
50
+ createComponent(attributeName, attribute, meta, metadata);
51
+ continue;
52
+ }
53
+
54
+ if (types.isDynamicZone(attribute.type)) {
55
+ createDynamicZone(attributeName, attribute, meta, metadata);
56
+ continue;
57
+ }
58
+
59
+ if (types.isRelation(attribute.type)) {
60
+ createRelation(attributeName, attribute, meta, metadata);
61
+ continue;
62
+ }
63
+
64
+ createAttribute(attributeName, attribute, meta, metadata);
65
+ } catch (error) {
66
+ console.log(error);
67
+ throw new Error(
68
+ `Error on attribute ${attributeName} in model ${meta.singularName}(${meta.uid}): ${error.message}`
69
+ );
70
+ }
71
+ }
72
+ }
73
+
74
+ for (const meta of metadata.values()) {
75
+ const columnToAttribute = Object.keys(meta.attributes).reduce((acc, key) => {
76
+ const attribute = meta.attributes[key];
77
+ return Object.assign(acc, { [attribute.columnName || key]: key });
78
+ }, {});
79
+
80
+ meta.columnToAttribute = columnToAttribute;
81
+ }
82
+
83
+ return metadata;
84
+ };
85
+
86
+ const hasComponentsOrDz = model => {
87
+ return Object.values(model.attributes).some(
88
+ ({ type }) => types.isComponent(type) || types.isDynamicZone(type)
89
+ );
90
+ };
91
+
92
+ // NOTE: we might just move the compo logic outside this layer too at some point
93
+ const createCompoLinkModelMeta = baseModelMeta => {
94
+ return {
95
+ // TODO: make sure there can't be any conflicts with a prefix
96
+ // singularName: 'compo',
97
+ uid: `${baseModelMeta.tableName}_components`,
98
+ tableName: `${baseModelMeta.tableName}_components`,
99
+ attributes: {
100
+ id: {
101
+ type: 'increments',
102
+ },
103
+ entity_id: {
104
+ type: 'integer',
105
+ column: {
106
+ unsigned: true,
107
+ },
108
+ },
109
+ component_id: {
110
+ type: 'integer',
111
+ column: {
112
+ unsigned: true,
113
+ },
114
+ },
115
+ component_type: {
116
+ type: 'string',
117
+ },
118
+ field: {
119
+ type: 'string',
120
+ },
121
+ order: {
122
+ type: 'integer',
123
+ column: {
124
+ unsigned: true,
125
+ defaultTo: 0,
126
+ },
127
+ },
128
+ },
129
+ indexes: [
130
+ {
131
+ name: `${baseModelMeta.tableName}_field_index`,
132
+ columns: ['field'],
133
+ type: null,
134
+ },
135
+ {
136
+ name: `${baseModelMeta.tableName}_component_type_index`,
137
+ columns: ['component_type'],
138
+ type: null,
139
+ },
140
+ {
141
+ name: `${baseModelMeta.tableName}_entity_fk`,
142
+ columns: ['entity_id'],
143
+ },
144
+ ],
145
+ foreignKeys: [
146
+ {
147
+ name: `${baseModelMeta.tableName}_entity_fk`,
148
+ columns: ['entity_id'],
149
+ referencedColumns: ['id'],
150
+ referencedTable: baseModelMeta.tableName,
151
+ onDelete: 'CASCADE',
152
+ },
153
+ ],
154
+ };
155
+ };
156
+
157
+ const createDynamicZone = (attributeName, attribute, meta) => {
158
+ Object.assign(attribute, {
159
+ type: 'relation',
160
+ relation: 'morphToMany',
161
+ // TODO: handle restrictions at some point
162
+ // target: attribute.components,
163
+ joinTable: {
164
+ name: meta.componentLink.tableName,
165
+ joinColumn: {
166
+ name: 'entity_id',
167
+ referencedColumn: 'id',
168
+ },
169
+ morphColumn: {
170
+ idColumn: {
171
+ name: 'component_id',
172
+ referencedColumn: 'id',
173
+ },
174
+ typeColumn: {
175
+ name: 'component_type',
176
+ },
177
+ typeField: '__component',
178
+ },
179
+ on: {
180
+ field: attributeName,
181
+ },
182
+ orderBy: {
183
+ order: 'asc',
184
+ },
185
+ },
186
+ });
187
+ };
188
+
189
+ const createComponent = (attributeName, attribute, meta) => {
190
+ Object.assign(attribute, {
191
+ type: 'relation',
192
+ relation: attribute.repeatable === true ? 'oneToMany' : 'oneToOne',
193
+ target: attribute.component,
194
+ joinTable: {
195
+ name: meta.componentLink.tableName,
196
+ joinColumn: {
197
+ name: 'entity_id',
198
+ referencedColumn: 'id',
199
+ },
200
+ inverseJoinColumn: {
201
+ name: 'component_id',
202
+ referencedColumn: 'id',
203
+ },
204
+ on: {
205
+ field: attributeName,
206
+ },
207
+ orderBy: {
208
+ order: 'asc',
209
+ },
210
+ },
211
+ });
212
+ };
213
+
214
+ const createAttribute = (attributeName, attribute) => {
215
+ const columnName = _.snakeCase(attributeName);
216
+ Object.assign(attribute, { columnName });
217
+ };
218
+
219
+ module.exports = createMetadata;