@strapi/strapi 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/README.md +12 -12
- package/bin/strapi.js +41 -60
- package/lib/Strapi.js +234 -114
- package/lib/commands/build.js +16 -6
- package/lib/commands/console.js +1 -1
- package/lib/commands/content-types/list.js +22 -0
- package/lib/commands/develop.js +17 -18
- package/lib/commands/generate-template.js +4 -5
- package/lib/commands/hooks/list.js +22 -0
- package/lib/commands/middlewares/list.js +22 -0
- package/lib/commands/new.js +3 -1
- package/lib/commands/policies/list.js +22 -0
- package/lib/commands/routes/list.js +28 -0
- package/lib/commands/services/list.js +22 -0
- package/lib/commands/watchAdmin.js +18 -8
- package/lib/container.js +6 -6
- package/lib/core/app-configuration/config-loader.js +1 -37
- package/lib/core/app-configuration/index.js +6 -46
- package/lib/core/app-configuration/load-config-file.js +43 -0
- package/lib/core/bootstrap.js +5 -117
- package/lib/core/domain/component/index.js +24 -0
- package/lib/core/domain/component/validator.js +29 -0
- package/lib/core/domain/content-type/index.js +140 -0
- package/lib/core/domain/content-type/validator.js +64 -0
- package/lib/core/domain/module/index.js +108 -0
- package/lib/core/domain/module/validation.js +33 -0
- package/lib/core/loaders/admin.js +16 -0
- package/lib/core/loaders/apis.js +159 -0
- package/lib/core/{load-components.js → loaders/components.js} +5 -7
- package/lib/core/loaders/index.js +11 -0
- package/lib/core/loaders/middlewares.js +36 -0
- package/lib/core/loaders/plugins/get-enabled-plugins.js +116 -0
- package/lib/core/loaders/plugins/index.js +123 -0
- package/lib/core/loaders/policies.js +28 -0
- package/lib/core/loaders/src-index.js +39 -0
- package/lib/core/registries/apis.js +29 -0
- package/lib/core/{app-configuration/config-provider.js → registries/config.js} +4 -11
- package/lib/core/registries/content-types.js +97 -0
- package/lib/core/registries/controllers.d.ts +7 -0
- package/lib/core/registries/controllers.js +114 -0
- package/lib/core/registries/hooks.d.ts +20 -0
- package/lib/core/registries/hooks.js +87 -0
- package/lib/core/registries/middlewares.d.ts +5 -0
- package/lib/core/registries/middlewares.js +89 -0
- package/lib/core/registries/modules.js +44 -0
- package/lib/core/registries/plugins.js +28 -0
- package/lib/core/registries/policies.d.ts +9 -0
- package/lib/core/registries/policies.js +89 -0
- package/lib/core/registries/services.d.ts +7 -0
- package/lib/core/registries/services.js +114 -0
- package/lib/core/utils.js +35 -0
- package/lib/core-api/controller/collection-type.js +45 -26
- package/lib/core-api/controller/index.d.ts +25 -0
- package/lib/core-api/controller/index.js +33 -11
- package/lib/core-api/controller/single-type.js +29 -15
- package/lib/core-api/controller/transform.js +62 -6
- package/lib/core-api/routes/index.js +71 -0
- package/lib/core-api/service/collection-type.js +43 -21
- package/lib/core-api/service/index.d.ts +21 -0
- package/lib/core-api/service/index.js +8 -67
- package/lib/core-api/service/pagination.js +130 -0
- package/lib/core-api/service/single-type.js +17 -19
- package/lib/factories.d.ts +48 -0
- package/lib/factories.js +84 -0
- package/lib/index.d.ts +10 -31
- package/lib/index.js +5 -1
- package/lib/middlewares/body.js +33 -0
- package/lib/middlewares/compression.js +8 -0
- package/lib/middlewares/cors.js +58 -0
- package/lib/middlewares/errors.js +40 -0
- package/lib/middlewares/favicon.js +19 -0
- package/lib/middlewares/index.d.ts +5 -0
- package/lib/middlewares/index.js +30 -117
- package/lib/middlewares/ip.js +8 -0
- package/lib/middlewares/logger.js +27 -0
- package/lib/middlewares/powered-by.js +20 -0
- package/lib/middlewares/public/index.js +98 -73
- package/lib/middlewares/query.js +46 -0
- package/lib/middlewares/response-time.js +15 -0
- package/lib/middlewares/responses.js +19 -0
- package/lib/middlewares/security.js +51 -0
- package/lib/middlewares/session/index.js +6 -6
- package/lib/migrations/draft-publish.js +57 -0
- package/lib/services/auth/index.js +87 -0
- package/lib/services/core-store.js +64 -51
- package/lib/services/cron.js +54 -0
- package/lib/services/entity-service/attributes/index.js +31 -0
- package/lib/services/entity-service/attributes/transforms.js +20 -0
- package/lib/services/entity-service/components.js +39 -15
- package/lib/services/entity-service/index.d.ts +91 -0
- package/lib/services/entity-service/index.js +120 -59
- package/lib/services/entity-service/params.js +52 -94
- package/lib/services/entity-validator/index.js +76 -43
- package/lib/services/entity-validator/validators.js +129 -43
- package/lib/services/errors.js +77 -0
- package/lib/{core → services}/fs.js +10 -2
- package/lib/services/metrics/index.js +41 -38
- package/lib/services/metrics/sender.js +2 -2
- package/lib/services/server/admin-api.js +14 -0
- package/lib/services/server/api.js +36 -0
- package/lib/services/server/compose-endpoint.js +141 -0
- package/lib/services/server/content-api.js +16 -0
- package/lib/{server.js → services/server/http-server.js} +0 -0
- package/lib/services/server/index.js +127 -0
- package/lib/services/server/koa.js +64 -0
- package/lib/services/server/middleware.js +122 -0
- package/lib/services/server/policy.js +32 -0
- package/lib/services/server/register-middlewares.js +110 -0
- package/lib/services/server/register-routes.js +106 -0
- package/lib/services/server/routing.js +120 -0
- package/lib/services/utils/upload-files.js +1 -1
- package/lib/services/webhook-runner.js +1 -1
- package/lib/utils/ee.js +3 -3
- package/lib/utils/get-dirs.js +17 -0
- package/lib/utils/index.js +2 -0
- package/lib/utils/is-initialized.js +1 -1
- package/lib/utils/run-checks.js +0 -15
- package/lib/utils/signals.js +24 -0
- package/lib/utils/startup-logger.js +2 -2
- package/lib/utils/update-notifier/index.js +3 -2
- package/package.json +93 -96
- package/lib/commands/generate.js +0 -76
- package/lib/core/index.js +0 -17
- package/lib/core/load-apis.js +0 -20
- package/lib/core/load-extensions.js +0 -71
- package/lib/core/load-functions.js +0 -21
- package/lib/core/load-middlewares.js +0 -130
- package/lib/core/load-modules.js +0 -55
- package/lib/core/load-plugins.js +0 -68
- package/lib/core/load-policies.js +0 -36
- package/lib/core/walk.js +0 -27
- package/lib/core-api/index.js +0 -39
- package/lib/load/check-reserved-filename.js +0 -10
- package/lib/load/load-config-files.js +0 -22
- package/lib/load/require-file-parse.js +0 -15
- package/lib/middlewares/boom/defaults.json +0 -5
- package/lib/middlewares/boom/index.js +0 -147
- package/lib/middlewares/cors/index.js +0 -66
- package/lib/middlewares/cron/defaults.json +0 -5
- package/lib/middlewares/cron/index.js +0 -43
- package/lib/middlewares/favicon/defaults.json +0 -7
- package/lib/middlewares/favicon/index.js +0 -32
- package/lib/middlewares/gzip/defaults.json +0 -6
- package/lib/middlewares/gzip/index.js +0 -19
- package/lib/middlewares/helmet/defaults.json +0 -18
- package/lib/middlewares/helmet/index.js +0 -9
- package/lib/middlewares/ip/defaults.json +0 -7
- package/lib/middlewares/ip/index.js +0 -25
- package/lib/middlewares/language/defaults.json +0 -9
- package/lib/middlewares/language/index.js +0 -40
- package/lib/middlewares/logger/defaults.json +0 -5
- package/lib/middlewares/logger/index.js +0 -37
- package/lib/middlewares/parser/defaults.json +0 -11
- package/lib/middlewares/parser/index.js +0 -71
- package/lib/middlewares/poweredBy/defaults.json +0 -5
- package/lib/middlewares/poweredBy/index.js +0 -16
- package/lib/middlewares/public/defaults.json +0 -8
- package/lib/middlewares/responseTime/defaults.json +0 -5
- package/lib/middlewares/responseTime/index.js +0 -25
- package/lib/middlewares/responses/defaults.json +0 -5
- package/lib/middlewares/responses/index.js +0 -18
- package/lib/middlewares/router/defaults.json +0 -7
- package/lib/middlewares/router/index.js +0 -64
- package/lib/middlewares/router/utils/composeEndpoint.js +0 -25
- package/lib/middlewares/router/utils/routerChecker.js +0 -92
- package/lib/utils/get-prefixed-dependencies.js +0 -7
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Job } = require('node-schedule');
|
|
4
|
+
const { isFunction } = require('lodash/fp');
|
|
5
|
+
|
|
6
|
+
const createCronService = () => {
|
|
7
|
+
let jobsSpecs = [];
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
add(tasks = {}) {
|
|
11
|
+
for (const taskExpression in tasks) {
|
|
12
|
+
const taskValue = tasks[taskExpression];
|
|
13
|
+
|
|
14
|
+
let fn;
|
|
15
|
+
let options;
|
|
16
|
+
if (isFunction(taskValue)) {
|
|
17
|
+
fn = taskValue.bind(tasks);
|
|
18
|
+
options = taskExpression;
|
|
19
|
+
} else if (isFunction(taskValue.task)) {
|
|
20
|
+
fn = taskValue.task.bind(taskValue);
|
|
21
|
+
options = taskValue.options;
|
|
22
|
+
} else {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Could not schedule a cron job for "${taskExpression}": no function found.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const fnWithStrapi = (...args) => fn({ strapi }, ...args);
|
|
29
|
+
|
|
30
|
+
const job = new Job(null, fnWithStrapi);
|
|
31
|
+
jobsSpecs.push({ job, options });
|
|
32
|
+
}
|
|
33
|
+
return this;
|
|
34
|
+
},
|
|
35
|
+
start() {
|
|
36
|
+
if (!strapi.config.get('server.cron.enabled')) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
jobsSpecs.forEach(({ job, options }) => job.schedule(options));
|
|
40
|
+
return this;
|
|
41
|
+
},
|
|
42
|
+
stop() {
|
|
43
|
+
jobsSpecs.forEach(({ job }) => job.cancel());
|
|
44
|
+
return this;
|
|
45
|
+
},
|
|
46
|
+
destroy() {
|
|
47
|
+
this.stop();
|
|
48
|
+
jobsSpecs = [];
|
|
49
|
+
return this;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
module.exports = createCronService;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const transforms = require('./transforms');
|
|
4
|
+
|
|
5
|
+
const applyTransforms = (data, context) => {
|
|
6
|
+
const { contentType } = context;
|
|
7
|
+
|
|
8
|
+
const entries = Object.entries(data);
|
|
9
|
+
|
|
10
|
+
for (const [attributeName, value] of entries) {
|
|
11
|
+
const attribute = contentType.attributes[attributeName];
|
|
12
|
+
|
|
13
|
+
if (!attribute) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const transform = transforms[attribute.type];
|
|
18
|
+
|
|
19
|
+
if (transform) {
|
|
20
|
+
const attributeContext = { ...context, attributeName, attribute };
|
|
21
|
+
|
|
22
|
+
data[attributeName] = transform(value, attributeContext);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return data;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
module.exports = {
|
|
30
|
+
applyTransforms,
|
|
31
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { getOr, toNumber, isString, isBuffer } = require('lodash/fp');
|
|
4
|
+
const bcrypt = require('bcryptjs');
|
|
5
|
+
|
|
6
|
+
const transforms = {
|
|
7
|
+
password(value, context) {
|
|
8
|
+
const { attribute } = context;
|
|
9
|
+
|
|
10
|
+
if (!isString(value) && !isBuffer(value)) {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const rounds = toNumber(getOr(10, 'encryption.rounds', attribute));
|
|
15
|
+
|
|
16
|
+
return bcrypt.hashSync(value, rounds);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
module.exports = transforms;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
|
-
const { has, prop, omit } = require('lodash/fp');
|
|
4
|
+
const { has, prop, omit, toString } = require('lodash/fp');
|
|
5
5
|
|
|
6
6
|
const { contentTypes: contentTypesUtils } = require('@strapi/utils');
|
|
7
|
+
const { ApplicationError } = require('@strapi/utils').errors;
|
|
7
8
|
|
|
8
9
|
const omitComponentData = (contentType, data) => {
|
|
9
10
|
const { attributes } = contentType;
|
|
@@ -131,11 +132,26 @@ const updateComponents = async (uid, entityToUpdate, data) => {
|
|
|
131
132
|
componentValue.map(value => updateOrCreateComponent(componentUID, value))
|
|
132
133
|
);
|
|
133
134
|
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
componentBody[attributeName] = components.filter(_.negate(_.isNil)).map(({ id }, idx) => {
|
|
136
|
+
return {
|
|
137
|
+
id,
|
|
138
|
+
__pivot: {
|
|
139
|
+
order: idx + 1,
|
|
140
|
+
field: attributeName,
|
|
141
|
+
component_type: componentUID,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
});
|
|
136
145
|
} else {
|
|
137
146
|
const component = await updateOrCreateComponent(componentUID, componentValue);
|
|
138
|
-
componentBody[attributeName] = component &&
|
|
147
|
+
componentBody[attributeName] = component && {
|
|
148
|
+
id: component.id,
|
|
149
|
+
__pivot: {
|
|
150
|
+
order: 1,
|
|
151
|
+
field: attributeName,
|
|
152
|
+
component_type: componentUID,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
139
155
|
}
|
|
140
156
|
|
|
141
157
|
continue;
|
|
@@ -151,9 +167,17 @@ const updateComponents = async (uid, entityToUpdate, data) => {
|
|
|
151
167
|
}
|
|
152
168
|
|
|
153
169
|
componentBody[attributeName] = await Promise.all(
|
|
154
|
-
dynamiczoneValues.map(async value => {
|
|
170
|
+
dynamiczoneValues.map(async (value, idx) => {
|
|
155
171
|
const { id } = await updateOrCreateComponent(value.__component, value);
|
|
156
|
-
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
id,
|
|
175
|
+
__component: value.__component,
|
|
176
|
+
__pivot: {
|
|
177
|
+
order: idx + 1,
|
|
178
|
+
field: attributeName,
|
|
179
|
+
},
|
|
180
|
+
};
|
|
157
181
|
})
|
|
158
182
|
);
|
|
159
183
|
|
|
@@ -175,19 +199,19 @@ const deleteOldComponents = async (
|
|
|
175
199
|
|
|
176
200
|
const idsToKeep = _.castArray(componentValue)
|
|
177
201
|
.filter(has('id'))
|
|
178
|
-
.map(prop('id'))
|
|
202
|
+
.map(prop('id'))
|
|
203
|
+
.map(toString);
|
|
179
204
|
|
|
180
205
|
const allIds = _.castArray(previousValue)
|
|
181
206
|
.filter(has('id'))
|
|
182
|
-
.map(prop('id'))
|
|
207
|
+
.map(prop('id'))
|
|
208
|
+
.map(toString);
|
|
183
209
|
|
|
184
210
|
idsToKeep.forEach(id => {
|
|
185
211
|
if (!allIds.includes(id)) {
|
|
186
|
-
|
|
212
|
+
throw new ApplicationError(
|
|
187
213
|
`Some of the provided components in ${attributeName} are not related to the entity`
|
|
188
214
|
);
|
|
189
|
-
err.status = 400;
|
|
190
|
-
throw err;
|
|
191
215
|
}
|
|
192
216
|
});
|
|
193
217
|
|
|
@@ -206,14 +230,14 @@ const deleteOldDZComponents = async (uid, entityToUpdate, attributeName, dynamic
|
|
|
206
230
|
const idsToKeep = _.castArray(dynamiczoneValues)
|
|
207
231
|
.filter(has('id'))
|
|
208
232
|
.map(({ id, __component }) => ({
|
|
209
|
-
id,
|
|
233
|
+
id: toString(id),
|
|
210
234
|
__component,
|
|
211
235
|
}));
|
|
212
236
|
|
|
213
237
|
const allIds = _.castArray(previousValue)
|
|
214
238
|
.filter(has('id'))
|
|
215
239
|
.map(({ id, __component }) => ({
|
|
216
|
-
id,
|
|
240
|
+
id: toString(id),
|
|
217
241
|
__component,
|
|
218
242
|
}));
|
|
219
243
|
|
|
@@ -293,7 +317,7 @@ const createComponent = async (uid, data) => {
|
|
|
293
317
|
|
|
294
318
|
const componentData = await createComponents(uid, data);
|
|
295
319
|
|
|
296
|
-
return
|
|
320
|
+
return strapi.query(uid).create({
|
|
297
321
|
data: Object.assign(omitComponentData(model, data), componentData),
|
|
298
322
|
});
|
|
299
323
|
};
|
|
@@ -304,7 +328,7 @@ const updateComponent = async (uid, componentToUpdate, data) => {
|
|
|
304
328
|
|
|
305
329
|
const componentData = await updateComponents(uid, componentToUpdate, data);
|
|
306
330
|
|
|
307
|
-
return
|
|
331
|
+
return strapi.query(uid).update({
|
|
308
332
|
where: {
|
|
309
333
|
id: componentToUpdate.id,
|
|
310
334
|
},
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Database } from '@strapi/database';
|
|
2
|
+
import { Strapi } from '../../';
|
|
3
|
+
|
|
4
|
+
type ID = number | string;
|
|
5
|
+
|
|
6
|
+
type EntityServiceAction =
|
|
7
|
+
| 'findMany'
|
|
8
|
+
| 'findPage'
|
|
9
|
+
| 'findWithRelationCounts'
|
|
10
|
+
| 'findOne'
|
|
11
|
+
| 'count'
|
|
12
|
+
| 'create'
|
|
13
|
+
| 'update'
|
|
14
|
+
| 'delete';
|
|
15
|
+
|
|
16
|
+
type PaginationInfo = {
|
|
17
|
+
page: number;
|
|
18
|
+
pageSize: number;
|
|
19
|
+
pageCount: number;
|
|
20
|
+
total: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type Params<T> = {
|
|
24
|
+
fields?: (keyof T)[];
|
|
25
|
+
filters?: any;
|
|
26
|
+
_q?: string;
|
|
27
|
+
populate?: any;
|
|
28
|
+
sort?: any;
|
|
29
|
+
start?: number;
|
|
30
|
+
limit?: number;
|
|
31
|
+
page?: number;
|
|
32
|
+
pageSize?: number;
|
|
33
|
+
publicationState?: string;
|
|
34
|
+
data?: any;
|
|
35
|
+
files?: any;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export interface EntityService {
|
|
39
|
+
uploadFiles<K extends keyof AllTypes, T extends AllTypes[K]>(uid: K, entity, files);
|
|
40
|
+
wrapParams<K extends keyof AllTypes, T extends AllTypes[K]>(
|
|
41
|
+
params: Params<T>,
|
|
42
|
+
{ uid: K, action: EntityServiceAction }
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
findMany<K extends keyof AllTypes, T extends AllTypes[K]>(
|
|
46
|
+
uid: K,
|
|
47
|
+
params: Params<T>
|
|
48
|
+
): Promise<T[]>;
|
|
49
|
+
findPage<K extends keyof AllTypes, T extends AllTypes[K]>(
|
|
50
|
+
uid: K,
|
|
51
|
+
params: Params<T>
|
|
52
|
+
): Promise<{
|
|
53
|
+
results: T[];
|
|
54
|
+
pagination: PaginationInfo;
|
|
55
|
+
}>;
|
|
56
|
+
|
|
57
|
+
findWithRelationCounts<K extends keyof AllTypes, T extends AllTypes[K]>(
|
|
58
|
+
uid: K,
|
|
59
|
+
params: Params<T>
|
|
60
|
+
): Promise<{
|
|
61
|
+
results: T[];
|
|
62
|
+
pagination: PaginationInfo;
|
|
63
|
+
}>;
|
|
64
|
+
|
|
65
|
+
findOne<K extends keyof AllTypes, T extends AllTypes[K]>(
|
|
66
|
+
uid: K,
|
|
67
|
+
entityId: ID,
|
|
68
|
+
params: Params<T>
|
|
69
|
+
): Promise<T>;
|
|
70
|
+
|
|
71
|
+
count<K extends keyof AllTypes, T extends AllTypes[K]>(uid: K, params: Params<T>): Promise<any>;
|
|
72
|
+
create<K extends keyof AllTypes, T extends AllTypes[K]>(uid: K, params: Params<T>): Promise<any>;
|
|
73
|
+
update<K extends keyof AllTypes, T extends AllTypes[K]>(
|
|
74
|
+
uid: K,
|
|
75
|
+
entityId: ID,
|
|
76
|
+
params: Params<T>
|
|
77
|
+
): Promise<any>;
|
|
78
|
+
delete<K extends keyof AllTypes, T extends AllTypes[K]>(
|
|
79
|
+
uid: K,
|
|
80
|
+
entityId: ID,
|
|
81
|
+
params: Params<T>
|
|
82
|
+
): Promise<any>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default function(opts: {
|
|
86
|
+
strapi: Strapi;
|
|
87
|
+
db: Database;
|
|
88
|
+
// TODO: define types
|
|
89
|
+
eventHub: any;
|
|
90
|
+
entityValidator: any;
|
|
91
|
+
}): EntityService;
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const _ = require('lodash');
|
|
3
4
|
const delegate = require('delegates');
|
|
4
5
|
const {
|
|
5
|
-
|
|
6
|
+
InvalidTimeError,
|
|
7
|
+
InvalidDateError,
|
|
8
|
+
InvalidDateTimeError,
|
|
9
|
+
} = require('@strapi/database').errors;
|
|
10
|
+
const {
|
|
6
11
|
webhook: webhookUtils,
|
|
7
12
|
contentTypes: contentTypesUtils,
|
|
8
|
-
|
|
13
|
+
sanitize,
|
|
9
14
|
} = require('@strapi/utils');
|
|
15
|
+
const { ValidationError } = require('@strapi/utils').errors;
|
|
10
16
|
const uploadFiles = require('../utils/upload-files');
|
|
11
17
|
|
|
12
18
|
const {
|
|
@@ -16,12 +22,21 @@ const {
|
|
|
16
22
|
deleteComponents,
|
|
17
23
|
} = require('./components');
|
|
18
24
|
const { transformParamsToQuery, pickSelectionParams } = require('./params');
|
|
19
|
-
|
|
20
|
-
const { MANY_RELATIONS } = relationsUtils.constants;
|
|
25
|
+
const { applyTransforms } = require('./attributes');
|
|
21
26
|
|
|
22
27
|
// TODO: those should be strapi events used by the webhooks not the other way arround
|
|
23
28
|
const { ENTRY_CREATE, ENTRY_UPDATE, ENTRY_DELETE } = webhookUtils.webhookEvents;
|
|
24
29
|
|
|
30
|
+
const databaseErrorsToTransform = [InvalidTimeError, InvalidDateTimeError, InvalidDateError];
|
|
31
|
+
|
|
32
|
+
const creationPipeline = (data, context) => {
|
|
33
|
+
return applyTransforms(data, context);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const updatePipeline = (data, context) => {
|
|
37
|
+
return applyTransforms(data, context);
|
|
38
|
+
};
|
|
39
|
+
|
|
25
40
|
module.exports = ctx => {
|
|
26
41
|
const implementation = createDefaultImplementation(ctx);
|
|
27
42
|
|
|
@@ -42,31 +57,57 @@ module.exports = ctx => {
|
|
|
42
57
|
// delegate every method in implementation
|
|
43
58
|
Object.keys(service.implementation).forEach(key => delegator.method(key));
|
|
44
59
|
|
|
60
|
+
// wrap methods to handle Database Errors
|
|
61
|
+
service.decorate(oldService => {
|
|
62
|
+
const newService = _.mapValues(
|
|
63
|
+
oldService,
|
|
64
|
+
(method, methodName) =>
|
|
65
|
+
async function(...args) {
|
|
66
|
+
try {
|
|
67
|
+
return await oldService[methodName].call(this, ...args);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (
|
|
70
|
+
databaseErrorsToTransform.some(errorToTransform => error instanceof errorToTransform)
|
|
71
|
+
) {
|
|
72
|
+
throw new ValidationError(error.message);
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return newService;
|
|
80
|
+
});
|
|
81
|
+
|
|
45
82
|
return service;
|
|
46
83
|
};
|
|
47
84
|
|
|
85
|
+
/**
|
|
86
|
+
* @type {import('.').default}
|
|
87
|
+
*/
|
|
48
88
|
const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) => ({
|
|
49
89
|
uploadFiles,
|
|
50
90
|
|
|
51
|
-
async
|
|
91
|
+
async wrapParams(options = {}) {
|
|
52
92
|
return options;
|
|
53
93
|
},
|
|
54
94
|
|
|
55
|
-
emitEvent(uid, event, entity) {
|
|
95
|
+
async emitEvent(uid, event, entity) {
|
|
56
96
|
const model = strapi.getModel(uid);
|
|
97
|
+
const sanitizedEntity = await sanitize.sanitizers.defaultSanitizeOutput(model, entity);
|
|
57
98
|
|
|
58
99
|
eventHub.emit(event, {
|
|
59
100
|
model: model.modelName,
|
|
60
|
-
entry:
|
|
101
|
+
entry: sanitizedEntity,
|
|
61
102
|
});
|
|
62
103
|
},
|
|
63
104
|
|
|
64
|
-
async
|
|
105
|
+
async findMany(uid, opts) {
|
|
65
106
|
const { kind } = strapi.getModel(uid);
|
|
66
107
|
|
|
67
|
-
const
|
|
108
|
+
const wrappedParams = await this.wrapParams(opts, { uid, action: 'findMany' });
|
|
68
109
|
|
|
69
|
-
const query = transformParamsToQuery(uid,
|
|
110
|
+
const query = transformParamsToQuery(uid, wrappedParams);
|
|
70
111
|
|
|
71
112
|
if (kind === 'singleType') {
|
|
72
113
|
return db.query(uid).findOne(query);
|
|
@@ -76,41 +117,20 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
|
|
76
117
|
},
|
|
77
118
|
|
|
78
119
|
async findPage(uid, opts) {
|
|
79
|
-
const
|
|
120
|
+
const wrappedParams = await this.wrapParams(opts, { uid, action: 'findPage' });
|
|
80
121
|
|
|
81
|
-
const query = transformParamsToQuery(uid,
|
|
122
|
+
const query = transformParamsToQuery(uid, wrappedParams);
|
|
82
123
|
|
|
83
124
|
return db.query(uid).findPage(query);
|
|
84
125
|
},
|
|
85
126
|
|
|
127
|
+
// TODO: streamline the logic based on the populate option
|
|
86
128
|
async findWithRelationCounts(uid, opts) {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
const { params } = await this.wrapOptions(opts, { uid, action: 'findWithRelationCounts' });
|
|
129
|
+
const wrappedParams = await this.wrapParams(opts, { uid, action: 'findWithRelationCounts' });
|
|
90
130
|
|
|
91
|
-
const query = transformParamsToQuery(uid,
|
|
131
|
+
const query = transformParamsToQuery(uid, wrappedParams);
|
|
92
132
|
|
|
93
|
-
const {
|
|
94
|
-
|
|
95
|
-
const populate = (query.populate || []).reduce((populate, attributeName) => {
|
|
96
|
-
const attribute = attributes[attributeName];
|
|
97
|
-
|
|
98
|
-
if (
|
|
99
|
-
MANY_RELATIONS.includes(attribute.relation) &&
|
|
100
|
-
contentTypesUtils.isVisibleAttribute(model, attributeName)
|
|
101
|
-
) {
|
|
102
|
-
populate[attributeName] = { count: true };
|
|
103
|
-
} else {
|
|
104
|
-
populate[attributeName] = true;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return populate;
|
|
108
|
-
}, {});
|
|
109
|
-
|
|
110
|
-
const { results, pagination } = await db.query(uid).findPage({
|
|
111
|
-
...query,
|
|
112
|
-
populate,
|
|
113
|
-
});
|
|
133
|
+
const { results, pagination } = await db.query(uid).findPage(query);
|
|
114
134
|
|
|
115
135
|
return {
|
|
116
136
|
results,
|
|
@@ -119,23 +139,24 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
|
|
119
139
|
},
|
|
120
140
|
|
|
121
141
|
async findOne(uid, entityId, opts) {
|
|
122
|
-
const
|
|
142
|
+
const wrappedParams = await this.wrapParams(opts, { uid, action: 'findOne' });
|
|
123
143
|
|
|
124
|
-
const query = transformParamsToQuery(uid, pickSelectionParams(
|
|
144
|
+
const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
|
|
125
145
|
|
|
126
146
|
return db.query(uid).findOne({ ...query, where: { id: entityId } });
|
|
127
147
|
},
|
|
128
148
|
|
|
129
149
|
async count(uid, opts) {
|
|
130
|
-
const
|
|
150
|
+
const wrappedParams = await this.wrapParams(opts, { uid, action: 'count' });
|
|
131
151
|
|
|
132
|
-
const query = transformParamsToQuery(uid,
|
|
152
|
+
const query = transformParamsToQuery(uid, wrappedParams);
|
|
133
153
|
|
|
134
154
|
return db.query(uid).count(query);
|
|
135
155
|
},
|
|
136
156
|
|
|
137
157
|
async create(uid, opts) {
|
|
138
|
-
const
|
|
158
|
+
const wrappedParams = await this.wrapParams(opts, { uid, action: 'create' });
|
|
159
|
+
const { data, files } = wrappedParams;
|
|
139
160
|
|
|
140
161
|
const model = strapi.getModel(uid);
|
|
141
162
|
|
|
@@ -143,30 +164,33 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
|
|
143
164
|
const validData = await entityValidator.validateEntityCreation(model, data, { isDraft });
|
|
144
165
|
|
|
145
166
|
// select / populate
|
|
146
|
-
const query = transformParamsToQuery(uid, pickSelectionParams(
|
|
167
|
+
const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
|
|
147
168
|
|
|
148
169
|
// TODO: wrap into transaction
|
|
149
170
|
const componentData = await createComponents(uid, validData);
|
|
150
171
|
|
|
151
172
|
let entity = await db.query(uid).create({
|
|
152
173
|
...query,
|
|
153
|
-
data: Object.assign(omitComponentData(model, validData), componentData),
|
|
174
|
+
data: creationPipeline(Object.assign(omitComponentData(model, validData), componentData), {
|
|
175
|
+
contentType: model,
|
|
176
|
+
}),
|
|
154
177
|
});
|
|
155
178
|
|
|
156
179
|
// TODO: upload the files then set the links in the entity like with compo to avoid making too many queries
|
|
157
180
|
// FIXME: upload in components
|
|
158
181
|
if (files && Object.keys(files).length > 0) {
|
|
159
182
|
await this.uploadFiles(uid, entity, files);
|
|
160
|
-
entity = await this.findOne(uid, entity.id,
|
|
183
|
+
entity = await this.findOne(uid, entity.id, wrappedParams);
|
|
161
184
|
}
|
|
162
185
|
|
|
163
|
-
this.emitEvent(uid, ENTRY_CREATE, entity);
|
|
186
|
+
await this.emitEvent(uid, ENTRY_CREATE, entity);
|
|
164
187
|
|
|
165
188
|
return entity;
|
|
166
189
|
},
|
|
167
190
|
|
|
168
191
|
async update(uid, entityId, opts) {
|
|
169
|
-
const
|
|
192
|
+
const wrappedParams = await this.wrapParams(opts, { uid, action: 'update' });
|
|
193
|
+
const { data, files } = wrappedParams;
|
|
170
194
|
|
|
171
195
|
const model = strapi.getModel(uid);
|
|
172
196
|
|
|
@@ -178,11 +202,16 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
|
|
178
202
|
|
|
179
203
|
const isDraft = contentTypesUtils.isDraft(entityToUpdate, model);
|
|
180
204
|
|
|
181
|
-
const validData = await entityValidator.validateEntityUpdate(
|
|
182
|
-
|
|
183
|
-
|
|
205
|
+
const validData = await entityValidator.validateEntityUpdate(
|
|
206
|
+
model,
|
|
207
|
+
data,
|
|
208
|
+
{
|
|
209
|
+
isDraft,
|
|
210
|
+
},
|
|
211
|
+
entityToUpdate
|
|
212
|
+
);
|
|
184
213
|
|
|
185
|
-
const query = transformParamsToQuery(uid, pickSelectionParams(
|
|
214
|
+
const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
|
|
186
215
|
|
|
187
216
|
// TODO: wrap in transaction
|
|
188
217
|
const componentData = await updateComponents(uid, entityToUpdate, validData);
|
|
@@ -190,26 +219,28 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
|
|
190
219
|
let entity = await db.query(uid).update({
|
|
191
220
|
...query,
|
|
192
221
|
where: { id: entityId },
|
|
193
|
-
data: Object.assign(omitComponentData(model, validData), componentData),
|
|
222
|
+
data: updatePipeline(Object.assign(omitComponentData(model, validData), componentData), {
|
|
223
|
+
contentType: model,
|
|
224
|
+
}),
|
|
194
225
|
});
|
|
195
226
|
|
|
196
227
|
// TODO: upload the files then set the links in the entity like with compo to avoid making too many queries
|
|
197
228
|
// FIXME: upload in components
|
|
198
229
|
if (files && Object.keys(files).length > 0) {
|
|
199
230
|
await this.uploadFiles(uid, entity, files);
|
|
200
|
-
entity = await this.findOne(uid, entity.id,
|
|
231
|
+
entity = await this.findOne(uid, entity.id, wrappedParams);
|
|
201
232
|
}
|
|
202
233
|
|
|
203
|
-
this.emitEvent(uid, ENTRY_UPDATE, entity);
|
|
234
|
+
await this.emitEvent(uid, ENTRY_UPDATE, entity);
|
|
204
235
|
|
|
205
236
|
return entity;
|
|
206
237
|
},
|
|
207
238
|
|
|
208
239
|
async delete(uid, entityId, opts) {
|
|
209
|
-
const
|
|
240
|
+
const wrappedParams = await this.wrapParams(opts, { uid, action: 'delete' });
|
|
210
241
|
|
|
211
242
|
// select / populate
|
|
212
|
-
const query = transformParamsToQuery(uid, pickSelectionParams(
|
|
243
|
+
const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));
|
|
213
244
|
|
|
214
245
|
const entityToDelete = await db.query(uid).findOne({
|
|
215
246
|
...query,
|
|
@@ -223,17 +254,47 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
|
|
223
254
|
await deleteComponents(uid, entityToDelete);
|
|
224
255
|
await db.query(uid).delete({ where: { id: entityToDelete.id } });
|
|
225
256
|
|
|
226
|
-
this.emitEvent(uid, ENTRY_DELETE, entityToDelete);
|
|
257
|
+
await this.emitEvent(uid, ENTRY_DELETE, entityToDelete);
|
|
227
258
|
|
|
228
259
|
return entityToDelete;
|
|
229
260
|
},
|
|
230
261
|
|
|
262
|
+
// FIXME: used only for the CM to be removed
|
|
231
263
|
async deleteMany(uid, opts) {
|
|
232
|
-
const
|
|
264
|
+
const wrappedParams = await this.wrapParams(opts, { uid, action: 'delete' });
|
|
233
265
|
|
|
234
266
|
// select / populate
|
|
235
|
-
const query = transformParamsToQuery(uid,
|
|
267
|
+
const query = transformParamsToQuery(uid, wrappedParams);
|
|
236
268
|
|
|
237
269
|
return db.query(uid).deleteMany(query);
|
|
238
270
|
},
|
|
271
|
+
|
|
272
|
+
load(uid, entity, field, params = {}) {
|
|
273
|
+
const { attributes } = strapi.getModel(uid);
|
|
274
|
+
|
|
275
|
+
const attribute = attributes[field];
|
|
276
|
+
|
|
277
|
+
const loadParams = {};
|
|
278
|
+
|
|
279
|
+
switch (attribute.type) {
|
|
280
|
+
case 'relation': {
|
|
281
|
+
Object.assign(loadParams, transformParamsToQuery(attribute.target, params));
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
case 'component': {
|
|
285
|
+
Object.assign(loadParams, transformParamsToQuery(attribute.component, params));
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
case 'dynamiczone': {
|
|
289
|
+
Object.assign(loadParams, transformParamsToQuery(null, params));
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
case 'media': {
|
|
293
|
+
Object.assign(loadParams, transformParamsToQuery('plugin::upload.file', params));
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return db.query(uid).load(entity, field, loadParams);
|
|
299
|
+
},
|
|
239
300
|
});
|