@strapi/database 4.0.0-next.6 → 4.0.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.
- package/lib/dialects/dialect.js +45 -0
- package/lib/dialects/index.js +6 -112
- package/lib/dialects/mysql/index.js +51 -0
- package/lib/dialects/mysql/schema-inspector.js +199 -0
- package/lib/dialects/postgresql/index.js +49 -0
- package/lib/dialects/postgresql/schema-inspector.js +232 -0
- package/lib/dialects/sqlite/index.js +74 -0
- package/lib/dialects/sqlite/schema-inspector.js +151 -0
- package/lib/entity-manager.js +18 -14
- package/lib/entity-repository.js +2 -3
- package/lib/errors.js +45 -3
- package/lib/fields.d.ts +2 -3
- package/lib/fields.js +7 -16
- package/lib/index.d.ts +67 -22
- package/lib/index.js +44 -27
- package/lib/lifecycles/index.d.ts +50 -0
- package/lib/{lifecycles.js → lifecycles/index.js} +25 -14
- package/lib/lifecycles/subscribers/index.d.ts +9 -0
- package/lib/lifecycles/subscribers/models-lifecycles.js +19 -0
- package/lib/lifecycles/subscribers/timestamps.js +65 -0
- package/lib/metadata/index.js +84 -95
- package/lib/metadata/relations.js +16 -0
- package/lib/migrations/index.d.ts +9 -0
- package/lib/migrations/index.js +69 -0
- package/lib/migrations/storage.js +51 -0
- package/lib/query/helpers/join.js +3 -5
- package/lib/query/helpers/order-by.js +21 -11
- package/lib/query/helpers/populate.js +35 -10
- package/lib/query/helpers/search.js +26 -12
- package/lib/query/helpers/transform.js +42 -14
- package/lib/query/helpers/where.js +92 -57
- package/lib/query/query-builder.js +116 -34
- package/lib/schema/__tests__/schema-diff.test.js +14 -1
- package/lib/schema/builder.js +315 -284
- package/lib/schema/diff.js +376 -0
- package/lib/schema/index.d.ts +49 -0
- package/lib/schema/index.js +47 -50
- package/lib/schema/schema.js +21 -18
- package/lib/schema/storage.js +79 -0
- package/lib/utils/content-types.js +1 -2
- package/package.json +26 -21
- package/lib/configuration.js +0 -49
- package/lib/schema/schema-diff.js +0 -337
- package/lib/schema/schema-storage.js +0 -44
package/lib/index.js
CHANGED
|
@@ -6,41 +6,48 @@ const { getDialect } = require('./dialects');
|
|
|
6
6
|
const createSchemaProvider = require('./schema');
|
|
7
7
|
const createMetadata = require('./metadata');
|
|
8
8
|
const { createEntityManager } = require('./entity-manager');
|
|
9
|
-
const {
|
|
9
|
+
const { createMigrationsProvider } = require('./migrations');
|
|
10
|
+
const { createLifecyclesProvider } = require('./lifecycles');
|
|
11
|
+
const errors = require('./errors');
|
|
10
12
|
|
|
11
13
|
// TODO: move back into strapi
|
|
12
14
|
const { transformContentTypes } = require('./utils/content-types');
|
|
13
15
|
|
|
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
|
+
|
|
14
26
|
class Database {
|
|
15
27
|
constructor(config) {
|
|
16
28
|
this.metadata = createMetadata(config.models);
|
|
17
29
|
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
this.config = {
|
|
31
|
+
connection: {},
|
|
32
|
+
settings: {
|
|
33
|
+
forceMigration: true,
|
|
34
|
+
},
|
|
35
|
+
...config,
|
|
36
|
+
};
|
|
20
37
|
|
|
21
|
-
this.config = config;
|
|
22
38
|
this.dialect = getDialect(this);
|
|
39
|
+
this.dialect.configure();
|
|
23
40
|
|
|
24
|
-
|
|
25
|
-
this.schema = createSchemaProvider(this);
|
|
41
|
+
this.connection = createConnection(this.config.connection);
|
|
26
42
|
|
|
27
|
-
this.
|
|
43
|
+
this.dialect.initialize();
|
|
28
44
|
|
|
29
|
-
this.
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async initialize() {
|
|
33
|
-
await this.dialect.initialize();
|
|
45
|
+
this.schema = createSchemaProvider(this);
|
|
34
46
|
|
|
35
|
-
this.
|
|
47
|
+
this.migrations = createMigrationsProvider(this);
|
|
48
|
+
this.lifecycles = createLifecyclesProvider(this);
|
|
36
49
|
|
|
37
|
-
|
|
38
|
-
this.lifecycles.subscribe(async event => {
|
|
39
|
-
const { model } = event;
|
|
40
|
-
if (event.action in model.lifecycles) {
|
|
41
|
-
await model.lifecycles[event.action](event);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
50
|
+
this.entityManager = createEntityManager(this);
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
query(uid) {
|
|
@@ -51,6 +58,21 @@ class Database {
|
|
|
51
58
|
return this.entityManager.getRepository(uid);
|
|
52
59
|
}
|
|
53
60
|
|
|
61
|
+
getConnection(tableName) {
|
|
62
|
+
const schema = this.connection.getSchemaName();
|
|
63
|
+
const connection = tableName ? this.connection(tableName) : this.connection;
|
|
64
|
+
return schema ? connection.withSchema(schema) : connection;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getSchemaConnection(trx = this.connection) {
|
|
68
|
+
const schema = this.connection.getSchemaName();
|
|
69
|
+
return schema ? trx.schema.withSchema(schema) : trx.schema;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
queryBuilder(uid) {
|
|
73
|
+
return this.entityManager.createQueryBuilder(uid);
|
|
74
|
+
}
|
|
75
|
+
|
|
54
76
|
async destroy() {
|
|
55
77
|
await this.lifecycles.clear();
|
|
56
78
|
await this.connection.destroy();
|
|
@@ -59,14 +81,9 @@ class Database {
|
|
|
59
81
|
|
|
60
82
|
// TODO: move into strapi
|
|
61
83
|
Database.transformContentTypes = transformContentTypes;
|
|
62
|
-
Database.init = async config =>
|
|
63
|
-
const db = new Database(config);
|
|
64
|
-
|
|
65
|
-
await db.initialize();
|
|
66
|
-
|
|
67
|
-
return db;
|
|
68
|
-
};
|
|
84
|
+
Database.init = async config => new Database(config);
|
|
69
85
|
|
|
70
86
|
module.exports = {
|
|
71
87
|
Database,
|
|
88
|
+
errors,
|
|
72
89
|
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Database } from '../';
|
|
2
|
+
import { Model } from '../schema';
|
|
3
|
+
import { Subscriber } from './subscribers';
|
|
4
|
+
|
|
5
|
+
export type Action =
|
|
6
|
+
| 'beforeCreate'
|
|
7
|
+
| 'afterCreate'
|
|
8
|
+
| 'beforeFindOne'
|
|
9
|
+
| 'afterFindOne'
|
|
10
|
+
| 'beforeFindMany'
|
|
11
|
+
| 'afterFindMany'
|
|
12
|
+
| 'beforeCount'
|
|
13
|
+
| 'afterCount'
|
|
14
|
+
| 'beforeCreateMany'
|
|
15
|
+
| 'afterCreateMany'
|
|
16
|
+
| 'beforeUpdate'
|
|
17
|
+
| 'afterUpdate'
|
|
18
|
+
| 'beforeUpdateMany'
|
|
19
|
+
| 'afterUpdateMany'
|
|
20
|
+
| 'beforeDelete'
|
|
21
|
+
| 'afterDelete'
|
|
22
|
+
| 'beforeDeleteMany'
|
|
23
|
+
| 'afterDeleteMany';
|
|
24
|
+
|
|
25
|
+
export interface Params {
|
|
26
|
+
select?: any;
|
|
27
|
+
where?: any;
|
|
28
|
+
_q?: any;
|
|
29
|
+
orderBy?: any;
|
|
30
|
+
groupBy?: any;
|
|
31
|
+
offset?: any;
|
|
32
|
+
limit?: any;
|
|
33
|
+
populate?: any;
|
|
34
|
+
data?: any;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface Event {
|
|
38
|
+
action: Action;
|
|
39
|
+
model: Model;
|
|
40
|
+
params: Params;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface LifecycleProvider {
|
|
44
|
+
subscribe(subscriber: Subscriber): () => void;
|
|
45
|
+
clear(): void;
|
|
46
|
+
run(action: Action, uid: string, properties: any): Promise<void>;
|
|
47
|
+
createEvent(action: Action, uid: string, properties: any): Event;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function createLifecyclesProvider(db: Database): LifecycleProvider;
|
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
let subscribers = [];
|
|
3
|
+
const assert = require('assert').strict;
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
const timestampsLifecyclesSubscriber = require('./subscribers/timestamps');
|
|
6
|
+
const modelLifecyclesSubscriber = require('./subscribers/models-lifecycles');
|
|
7
|
+
|
|
8
|
+
const isValidSubscriber = subscriber => {
|
|
9
|
+
return (
|
|
10
|
+
typeof subscriber === 'function' || (typeof subscriber === 'object' && subscriber !== null)
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @type {import('.').createLifecyclesProvider}
|
|
16
|
+
*/
|
|
17
|
+
const createLifecyclesProvider = db => {
|
|
18
|
+
let subscribers = [timestampsLifecyclesSubscriber, modelLifecyclesSubscriber];
|
|
19
|
+
|
|
20
|
+
return {
|
|
7
21
|
subscribe(subscriber) {
|
|
8
|
-
|
|
22
|
+
assert(isValidSubscriber(subscriber), 'Invalid subscriber. Expected function or object');
|
|
23
|
+
|
|
9
24
|
subscribers.push(subscriber);
|
|
10
25
|
|
|
11
|
-
return () =>
|
|
12
|
-
|
|
13
|
-
|
|
26
|
+
return () => subscribers.splice(subscribers.indexOf(subscriber), 1);
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
clear() {
|
|
30
|
+
subscribers = [];
|
|
14
31
|
},
|
|
15
32
|
|
|
16
33
|
createEvent(action, uid, properties) {
|
|
@@ -41,15 +58,9 @@ const createLifecyclesManager = db => {
|
|
|
41
58
|
}
|
|
42
59
|
}
|
|
43
60
|
},
|
|
44
|
-
|
|
45
|
-
clear() {
|
|
46
|
-
subscribers = [];
|
|
47
|
-
},
|
|
48
61
|
};
|
|
49
|
-
|
|
50
|
-
return lifecycleManager;
|
|
51
62
|
};
|
|
52
63
|
|
|
53
64
|
module.exports = {
|
|
54
|
-
|
|
65
|
+
createLifecyclesProvider,
|
|
55
66
|
};
|
|
@@ -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;
|
package/lib/metadata/index.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module metadata
|
|
3
|
-
*
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
'use strict';
|
|
7
2
|
|
|
8
3
|
const _ = require('lodash/fp');
|
|
@@ -16,43 +11,13 @@ class Metadata extends Map {
|
|
|
16
11
|
}
|
|
17
12
|
}
|
|
18
13
|
|
|
14
|
+
// TODO: check if there isn't an attribute with an id already
|
|
19
15
|
/**
|
|
20
16
|
* Create Metadata from models configurations
|
|
21
|
-
*
|
|
22
|
-
* timestamps => not optional anymore but auto added. Auto added on the content type or in the db layer ?
|
|
23
|
-
*
|
|
24
|
-
* options => options are handled on the layer above. Options convert to fields on the CT
|
|
25
|
-
*
|
|
26
|
-
* filters => not in v1
|
|
27
|
-
*
|
|
28
|
-
* attributes
|
|
29
|
-
*
|
|
30
|
-
* - type
|
|
31
|
-
* - mapping field name - column name
|
|
32
|
-
* - mapping field type - column type
|
|
33
|
-
* - formatter / parser => coming from field type so no
|
|
34
|
-
* - indexes / checks / contstraints
|
|
35
|
-
* - relations => reference to the target model (function or string to avoid circular deps ?)
|
|
36
|
-
* - name of the LEFT/RIGHT side foreign keys
|
|
37
|
-
* - name of join table
|
|
38
|
-
*
|
|
39
|
-
* - compo/dz => reference to the components
|
|
40
|
-
* - validators
|
|
41
|
-
* - hooks
|
|
42
|
-
* - default value
|
|
43
|
-
* - required -> should add a not null option instead of the API required
|
|
44
|
-
* - unique -> should add a DB unique option instead of the unique in the API (Unique by locale or something else for example)
|
|
45
|
-
*
|
|
46
|
-
* lifecycles
|
|
47
|
-
*
|
|
48
|
-
* private fields ? => handled on a different layer
|
|
49
17
|
* @param {object[]} models
|
|
50
18
|
* @returns {Metadata}
|
|
51
19
|
*/
|
|
52
20
|
const createMetadata = (models = []) => {
|
|
53
|
-
// TODO: reorder to make sure we can create everything or delete everything in the right order
|
|
54
|
-
// TODO: allow passing the join config in the attribute
|
|
55
|
-
// TODO: allow passing column config in the attribute
|
|
56
21
|
const metadata = new Metadata();
|
|
57
22
|
|
|
58
23
|
// init pass
|
|
@@ -62,7 +27,6 @@ const createMetadata = (models = []) => {
|
|
|
62
27
|
uid: model.uid,
|
|
63
28
|
tableName: model.tableName,
|
|
64
29
|
attributes: {
|
|
65
|
-
// TODO: check if there isn't an attribute with an id already
|
|
66
30
|
id: {
|
|
67
31
|
type: 'increments',
|
|
68
32
|
},
|
|
@@ -83,67 +47,12 @@ const createMetadata = (models = []) => {
|
|
|
83
47
|
for (const [attributeName, attribute] of Object.entries(meta.attributes)) {
|
|
84
48
|
try {
|
|
85
49
|
if (types.isComponent(attribute.type)) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
Object.assign(attribute, {
|
|
89
|
-
type: 'relation',
|
|
90
|
-
relation: attribute.repeatable === true ? 'oneToMany' : 'oneToOne',
|
|
91
|
-
target: attribute.component,
|
|
92
|
-
joinTable: {
|
|
93
|
-
name: meta.componentLink.tableName,
|
|
94
|
-
joinColumn: {
|
|
95
|
-
name: 'entity_id',
|
|
96
|
-
referencedColumn: 'id',
|
|
97
|
-
},
|
|
98
|
-
inverseJoinColumn: {
|
|
99
|
-
name: 'component_id',
|
|
100
|
-
referencedColumn: 'id',
|
|
101
|
-
},
|
|
102
|
-
on: {
|
|
103
|
-
field: attributeName,
|
|
104
|
-
},
|
|
105
|
-
orderBy: {
|
|
106
|
-
order: 'asc',
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
|
|
50
|
+
createComponent(attributeName, attribute, meta, metadata);
|
|
111
51
|
continue;
|
|
112
52
|
}
|
|
113
53
|
|
|
114
54
|
if (types.isDynamicZone(attribute.type)) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
Object.assign(attribute, {
|
|
118
|
-
type: 'relation',
|
|
119
|
-
relation: 'morphToMany',
|
|
120
|
-
// TODO: handle restrictions at some point
|
|
121
|
-
// target: attribute.components,
|
|
122
|
-
joinTable: {
|
|
123
|
-
name: meta.componentLink.tableName,
|
|
124
|
-
joinColumn: {
|
|
125
|
-
name: 'entity_id',
|
|
126
|
-
referencedColumn: 'id',
|
|
127
|
-
},
|
|
128
|
-
morphColumn: {
|
|
129
|
-
idColumn: {
|
|
130
|
-
name: 'component_id',
|
|
131
|
-
referencedColumn: 'id',
|
|
132
|
-
},
|
|
133
|
-
typeColumn: {
|
|
134
|
-
name: 'component_type',
|
|
135
|
-
},
|
|
136
|
-
typeField: '__component',
|
|
137
|
-
},
|
|
138
|
-
on: {
|
|
139
|
-
field: attributeName,
|
|
140
|
-
},
|
|
141
|
-
orderBy: {
|
|
142
|
-
order: 'asc',
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
|
|
55
|
+
createDynamicZone(attributeName, attribute, meta, metadata);
|
|
147
56
|
continue;
|
|
148
57
|
}
|
|
149
58
|
|
|
@@ -151,7 +60,10 @@ const createMetadata = (models = []) => {
|
|
|
151
60
|
createRelation(attributeName, attribute, meta, metadata);
|
|
152
61
|
continue;
|
|
153
62
|
}
|
|
63
|
+
|
|
64
|
+
createAttribute(attributeName, attribute, meta, metadata);
|
|
154
65
|
} catch (error) {
|
|
66
|
+
console.log(error);
|
|
155
67
|
throw new Error(
|
|
156
68
|
`Error on attribute ${attributeName} in model ${meta.singularName}(${meta.uid}): ${error.message}`
|
|
157
69
|
);
|
|
@@ -159,6 +71,15 @@ const createMetadata = (models = []) => {
|
|
|
159
71
|
}
|
|
160
72
|
}
|
|
161
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
|
+
|
|
162
83
|
return metadata;
|
|
163
84
|
};
|
|
164
85
|
|
|
@@ -201,7 +122,7 @@ const createCompoLinkModelMeta = baseModelMeta => {
|
|
|
201
122
|
type: 'integer',
|
|
202
123
|
column: {
|
|
203
124
|
unsigned: true,
|
|
204
|
-
defaultTo:
|
|
125
|
+
defaultTo: 0,
|
|
205
126
|
},
|
|
206
127
|
},
|
|
207
128
|
},
|
|
@@ -209,10 +130,16 @@ const createCompoLinkModelMeta = baseModelMeta => {
|
|
|
209
130
|
{
|
|
210
131
|
name: `${baseModelMeta.tableName}_field_index`,
|
|
211
132
|
columns: ['field'],
|
|
133
|
+
type: null,
|
|
212
134
|
},
|
|
213
135
|
{
|
|
214
136
|
name: `${baseModelMeta.tableName}_component_type_index`,
|
|
215
137
|
columns: ['component_type'],
|
|
138
|
+
type: null,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: `${baseModelMeta.tableName}_entity_fk`,
|
|
142
|
+
columns: ['entity_id'],
|
|
216
143
|
},
|
|
217
144
|
],
|
|
218
145
|
foreignKeys: [
|
|
@@ -227,4 +154,66 @@ const createCompoLinkModelMeta = baseModelMeta => {
|
|
|
227
154
|
};
|
|
228
155
|
};
|
|
229
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
|
+
|
|
230
219
|
module.exports = createMetadata;
|
|
@@ -235,6 +235,12 @@ const createMorphToMany = (attributeName, attribute, meta, metadata) => {
|
|
|
235
235
|
},
|
|
236
236
|
},
|
|
237
237
|
},
|
|
238
|
+
indexes: [
|
|
239
|
+
{
|
|
240
|
+
name: `${joinTableName}_fk`,
|
|
241
|
+
columns: [joinColumnName],
|
|
242
|
+
},
|
|
243
|
+
],
|
|
238
244
|
foreignKeys: [
|
|
239
245
|
{
|
|
240
246
|
name: `${joinTableName}_fk`,
|
|
@@ -415,6 +421,16 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|
|
415
421
|
},
|
|
416
422
|
// TODO: add extra pivot attributes -> user should use an intermediate entity
|
|
417
423
|
},
|
|
424
|
+
indexes: [
|
|
425
|
+
{
|
|
426
|
+
name: `${joinTableName}_fk`,
|
|
427
|
+
columns: [joinColumnName],
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
name: `${joinTableName}_inv_fk`,
|
|
431
|
+
columns: [inverseJoinColumnName],
|
|
432
|
+
},
|
|
433
|
+
],
|
|
418
434
|
foreignKeys: [
|
|
419
435
|
{
|
|
420
436
|
name: `${joinTableName}_fk`,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fse = require('fs-extra');
|
|
5
|
+
const Umzug = require('umzug');
|
|
6
|
+
|
|
7
|
+
const createStorage = require('./storage');
|
|
8
|
+
|
|
9
|
+
// TODO: check multiple commands in one sql statement
|
|
10
|
+
const migrationResolver = path => {
|
|
11
|
+
// if sql file run with knex raw
|
|
12
|
+
if (path.match(/\.sql$/)) {
|
|
13
|
+
const sql = fse.readFileSync(path, 'utf8');
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
up: knex => knex.raw(sql),
|
|
17
|
+
down() {},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// NOTE: we can add some ts register if we want to handle ts migration files at some point
|
|
22
|
+
return require(path);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const createUmzugProvider = db => {
|
|
26
|
+
const migrationDir = path.join(strapi.dirs.root, 'database/migrations');
|
|
27
|
+
|
|
28
|
+
fse.ensureDirSync(migrationDir);
|
|
29
|
+
|
|
30
|
+
const wrapFn = fn => db => db.getConnection().transaction(trx => Promise.resolve(fn(trx)));
|
|
31
|
+
const storage = createStorage({ db, tableName: 'strapi_migrations' });
|
|
32
|
+
|
|
33
|
+
return new Umzug({
|
|
34
|
+
storage,
|
|
35
|
+
migrations: {
|
|
36
|
+
path: migrationDir,
|
|
37
|
+
pattern: /\.(js|sql)$/,
|
|
38
|
+
params: [db],
|
|
39
|
+
wrap: wrapFn,
|
|
40
|
+
customResolver: migrationResolver,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// NOTE: when needed => add internal migrations for core & plugins. How do we overlap them with users migrations ?
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates migrations provider
|
|
49
|
+
* @type {import('.').createMigrationsProvider}
|
|
50
|
+
*/
|
|
51
|
+
const createMigrationsProvider = db => {
|
|
52
|
+
const migrations = createUmzugProvider(db);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
async shouldRun() {
|
|
56
|
+
const pending = await migrations.pending();
|
|
57
|
+
|
|
58
|
+
return pending.length > 0;
|
|
59
|
+
},
|
|
60
|
+
async up() {
|
|
61
|
+
await migrations.up();
|
|
62
|
+
},
|
|
63
|
+
async down() {
|
|
64
|
+
await migrations.down();
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
module.exports = { createMigrationsProvider };
|