@strapi/strapi 4.0.0-next.7 → 4.0.1
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 +14 -14
- package/bin/strapi.js +46 -60
- package/lib/Strapi.js +193 -98
- package/lib/commands/build.js +19 -8
- package/lib/commands/console.js +1 -1
- package/lib/commands/content-types/list.js +22 -0
- package/lib/commands/controllers/list.js +22 -0
- package/lib/commands/develop.js +22 -27
- 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 -9
- package/lib/core/app-configuration/index.js +3 -36
- package/lib/core/bootstrap.js +25 -0
- package/lib/core/domain/content-type/index.js +26 -29
- package/lib/core/domain/content-type/validator.js +22 -3
- package/lib/core/domain/module/index.js +42 -11
- package/lib/core/domain/module/validation.js +16 -19
- package/lib/core/loaders/admin.js +16 -0
- package/lib/core/loaders/apis.js +159 -0
- package/lib/core/loaders/{load-components.js → components.js} +5 -6
- package/lib/core/loaders/index.js +11 -0
- package/lib/core/loaders/middlewares.js +36 -0
- package/lib/core/{load-plugins → loaders/plugins}/get-enabled-plugins.js +55 -19
- package/lib/core/loaders/plugins/get-user-plugins-config.js +37 -0
- package/lib/core/{load-plugins → loaders/plugins}/index.js +35 -19
- 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/registries/content-types.js +66 -10
- package/lib/core/registries/controllers.d.ts +7 -0
- package/lib/core/registries/controllers.js +92 -7
- 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 +65 -5
- package/lib/core/registries/modules.js +3 -3
- package/lib/core/registries/plugins.js +2 -2
- package/lib/core/registries/policies.d.ts +9 -0
- package/lib/core/registries/policies.js +65 -5
- package/lib/core/registries/services.d.ts +7 -0
- package/lib/core/registries/services.js +88 -17
- 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 +125 -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 -116
- 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 +72 -77
- 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 +131 -43
- package/lib/services/errors.js +77 -0
- package/lib/{core → services}/fs.js +1 -1
- package/lib/services/metrics/index.js +38 -36
- 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/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/signals.js +24 -0
- package/lib/utils/startup-logger.js +2 -2
- package/lib/utils/update-notifier/index.js +3 -2
- package/package.json +94 -97
- package/lib/commands/generate.js +0 -76
- package/lib/core/loaders/bootstrap.js +0 -176
- package/lib/core/loaders/load-apis.js +0 -20
- package/lib/core/loaders/load-functions.js +0 -21
- package/lib/core/loaders/load-middlewares.js +0 -136
- package/lib/core/loaders/load-modules.js +0 -21
- package/lib/core/loaders/load-policies.js +0 -36
- package/lib/core/loaders/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 -56
- package/lib/middlewares/router/utils/composeEndpoint.js +0 -25
- package/lib/middlewares/router/utils/routerChecker.js +0 -96
- package/lib/utils/get-prefixed-dependencies.js +0 -7
|
@@ -1,43 +1,114 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const _ = require('lodash');
|
|
4
3
|
const { pickBy, has } = require('lodash/fp');
|
|
4
|
+
const { addNamespace, hasNamespace } = require('../utils');
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import('./services').Service} Service
|
|
8
|
+
* @typedef {import('./services').ServiceFactory} ServiceFactory
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const servicesRegistry = strapi => {
|
|
7
12
|
const services = {};
|
|
8
|
-
const
|
|
13
|
+
const instantiatedServices = {};
|
|
9
14
|
|
|
10
15
|
return {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Returns this list of registered services uids
|
|
18
|
+
* @returns {string[]}
|
|
19
|
+
*/
|
|
20
|
+
keys() {
|
|
21
|
+
return Object.keys(services);
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns the instance of a service. Instantiate the service if not already done
|
|
26
|
+
* @param {string} uid
|
|
27
|
+
* @returns {Service}
|
|
28
|
+
*/
|
|
29
|
+
get(uid) {
|
|
30
|
+
if (instantiatedServices[uid]) {
|
|
31
|
+
return instantiatedServices[uid];
|
|
14
32
|
}
|
|
15
33
|
|
|
16
|
-
const service = services[
|
|
34
|
+
const service = services[uid];
|
|
17
35
|
if (service) {
|
|
18
|
-
|
|
19
|
-
return
|
|
36
|
+
instantiatedServices[uid] = typeof service === 'function' ? service({ strapi }) : service;
|
|
37
|
+
return instantiatedServices[uid];
|
|
20
38
|
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns a map with all the services in a namespace
|
|
43
|
+
* @param {string} namespace
|
|
44
|
+
* @returns {{ [key: string]: Service }}
|
|
45
|
+
*/
|
|
46
|
+
getAll(namespace) {
|
|
47
|
+
const filteredServices = pickBy((_, uid) => hasNamespace(uid, namespace))(services);
|
|
21
48
|
|
|
22
|
-
|
|
49
|
+
// create lazy accessor to avoid instantiating the services;
|
|
50
|
+
const map = {};
|
|
51
|
+
for (const uid in filteredServices) {
|
|
52
|
+
Object.defineProperty(map, uid, {
|
|
53
|
+
enumerable: true,
|
|
54
|
+
get: () => {
|
|
55
|
+
return this.get(uid);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return map;
|
|
23
61
|
},
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Registers a service
|
|
65
|
+
* @param {string} uid
|
|
66
|
+
* @param {Service} service
|
|
67
|
+
*/
|
|
68
|
+
set(uid, service) {
|
|
69
|
+
services[uid] = service;
|
|
70
|
+
delete instantiatedServices[uid];
|
|
71
|
+
return this;
|
|
29
72
|
},
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Registers a map of services for a specific namespace
|
|
76
|
+
* @param {string} namespace
|
|
77
|
+
* @param {{ [key: string]: Service|ServiceFactory }} newServices
|
|
78
|
+
* @returns
|
|
79
|
+
*/
|
|
30
80
|
add(namespace, newServices) {
|
|
31
81
|
for (const serviceName in newServices) {
|
|
32
82
|
const service = newServices[serviceName];
|
|
33
|
-
const uid =
|
|
83
|
+
const uid = addNamespace(serviceName, namespace);
|
|
84
|
+
|
|
34
85
|
if (has(uid, services)) {
|
|
35
86
|
throw new Error(`Service ${uid} has already been registered.`);
|
|
36
87
|
}
|
|
37
88
|
services[uid] = service;
|
|
38
89
|
}
|
|
90
|
+
|
|
91
|
+
return this;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Wraps a service to extend it
|
|
96
|
+
* @param {string} uid
|
|
97
|
+
* @param {(service: Service) => Service} extendFn
|
|
98
|
+
*/
|
|
99
|
+
extend(uid, extendFn) {
|
|
100
|
+
const currentService = this.get(uid);
|
|
101
|
+
|
|
102
|
+
if (!currentService) {
|
|
103
|
+
throw new Error(`Service ${uid} doesn't exist`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const newService = extendFn(currentService);
|
|
107
|
+
instantiatedServices[uid] = newService;
|
|
108
|
+
|
|
109
|
+
return this;
|
|
39
110
|
},
|
|
40
111
|
};
|
|
41
112
|
};
|
|
42
113
|
|
|
43
|
-
module.exports =
|
|
114
|
+
module.exports = servicesRegistry;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const hasNamespace = (name, namespace) => {
|
|
4
|
+
if (!namespace) {
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (namespace.endsWith('::')) {
|
|
9
|
+
return name.startsWith(namespace);
|
|
10
|
+
} else {
|
|
11
|
+
return name.startsWith(`${namespace}.`);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const addNamespace = (name, namespace) => {
|
|
16
|
+
if (namespace.endsWith('::')) {
|
|
17
|
+
return `${namespace}${name}`;
|
|
18
|
+
} else {
|
|
19
|
+
return `${namespace}.${name}`;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const removeNamespace = (name, namespace) => {
|
|
24
|
+
if (namespace.endsWith('::')) {
|
|
25
|
+
return name.replace(namespace, '');
|
|
26
|
+
} else {
|
|
27
|
+
return name.replace(`${namespace}.`, '');
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
addNamespace,
|
|
33
|
+
removeNamespace,
|
|
34
|
+
hasNamespace,
|
|
35
|
+
};
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { isObject } = require('lodash/fp');
|
|
4
|
+
const { ValidationError } = require('@strapi/utils').errors;
|
|
5
|
+
|
|
6
|
+
const { parseBody } = require('./transform');
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
*
|
|
7
10
|
* Returns a collection type controller to handle default core-api actions
|
|
8
11
|
*/
|
|
9
|
-
const createCollectionTypeController = ({
|
|
12
|
+
const createCollectionTypeController = ({ contentType }) => {
|
|
13
|
+
const { uid } = contentType;
|
|
14
|
+
|
|
10
15
|
return {
|
|
11
16
|
/**
|
|
12
17
|
* Retrieve records.
|
|
@@ -16,9 +21,10 @@ const createCollectionTypeController = ({ service, sanitize, parseMultipartData
|
|
|
16
21
|
async find(ctx) {
|
|
17
22
|
const { query } = ctx;
|
|
18
23
|
|
|
19
|
-
const { results, pagination } = await service.find(
|
|
24
|
+
const { results, pagination } = await strapi.service(uid).find(query);
|
|
25
|
+
const sanitizedResults = await this.sanitizeOutput(results, ctx);
|
|
20
26
|
|
|
21
|
-
return transformResponse(
|
|
27
|
+
return this.transformResponse(sanitizedResults, { pagination });
|
|
22
28
|
},
|
|
23
29
|
|
|
24
30
|
/**
|
|
@@ -30,9 +36,10 @@ const createCollectionTypeController = ({ service, sanitize, parseMultipartData
|
|
|
30
36
|
const { id } = ctx.params;
|
|
31
37
|
const { query } = ctx;
|
|
32
38
|
|
|
33
|
-
const entity = await service.findOne(id,
|
|
39
|
+
const entity = await strapi.service(uid).findOne(id, query);
|
|
40
|
+
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
|
|
34
41
|
|
|
35
|
-
return transformResponse(
|
|
42
|
+
return this.transformResponse(sanitizedEntity);
|
|
36
43
|
},
|
|
37
44
|
|
|
38
45
|
/**
|
|
@@ -41,17 +48,22 @@ const createCollectionTypeController = ({ service, sanitize, parseMultipartData
|
|
|
41
48
|
* @return {Object}
|
|
42
49
|
*/
|
|
43
50
|
async create(ctx) {
|
|
44
|
-
const {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
} else {
|
|
51
|
-
entity = await service.create({ params: query, data: body });
|
|
51
|
+
const { query } = ctx.request;
|
|
52
|
+
|
|
53
|
+
const { data, files } = parseBody(ctx);
|
|
54
|
+
|
|
55
|
+
if (!isObject(data)) {
|
|
56
|
+
throw new ValidationError('Missing "data" payload in the request body');
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
const sanitizedInputData = await this.sanitizeInput(data, ctx);
|
|
60
|
+
|
|
61
|
+
const entity = await strapi
|
|
62
|
+
.service(uid)
|
|
63
|
+
.create({ ...query, data: sanitizedInputData, files });
|
|
64
|
+
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
|
|
65
|
+
|
|
66
|
+
return this.transformResponse(sanitizedEntity);
|
|
55
67
|
},
|
|
56
68
|
|
|
57
69
|
/**
|
|
@@ -61,17 +73,22 @@ const createCollectionTypeController = ({ service, sanitize, parseMultipartData
|
|
|
61
73
|
*/
|
|
62
74
|
async update(ctx) {
|
|
63
75
|
const { id } = ctx.params;
|
|
64
|
-
const {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
} else {
|
|
71
|
-
entity = await service.update(id, { params: query, data: body });
|
|
76
|
+
const { query } = ctx.request;
|
|
77
|
+
|
|
78
|
+
const { data, files } = parseBody(ctx);
|
|
79
|
+
|
|
80
|
+
if (!isObject(data)) {
|
|
81
|
+
throw new ValidationError('Missing "data" payload in the request body');
|
|
72
82
|
}
|
|
73
83
|
|
|
74
|
-
|
|
84
|
+
const sanitizedInputData = await this.sanitizeInput(data, ctx);
|
|
85
|
+
|
|
86
|
+
const entity = await strapi
|
|
87
|
+
.service(uid)
|
|
88
|
+
.update(id, { ...query, data: sanitizedInputData, files });
|
|
89
|
+
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
|
|
90
|
+
|
|
91
|
+
return this.transformResponse(sanitizedEntity);
|
|
75
92
|
},
|
|
76
93
|
|
|
77
94
|
/**
|
|
@@ -83,8 +100,10 @@ const createCollectionTypeController = ({ service, sanitize, parseMultipartData
|
|
|
83
100
|
const { id } = ctx.params;
|
|
84
101
|
const { query } = ctx;
|
|
85
102
|
|
|
86
|
-
const entity = await service.delete(id,
|
|
87
|
-
|
|
103
|
+
const entity = await strapi.service(uid).delete(id, query);
|
|
104
|
+
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
|
|
105
|
+
|
|
106
|
+
return this.transformResponse(sanitizedEntity);
|
|
88
107
|
},
|
|
89
108
|
};
|
|
90
109
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Context } from 'koa';
|
|
2
|
+
|
|
3
|
+
type Response = object;
|
|
4
|
+
|
|
5
|
+
interface BaseController {
|
|
6
|
+
transformResponse(data: object, meta: object): object;
|
|
7
|
+
sanitizeOutput(data: object, ctx: Context): Promise<object>;
|
|
8
|
+
sanitizeInput(data: object, ctx: Context): Promise<object>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SingleTypeController extends BaseController {
|
|
12
|
+
find(ctx: Context): Promise<Response>;
|
|
13
|
+
update(ctx: Context): Promise<Response>;
|
|
14
|
+
delete(ctx: Context): Promise<Response>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CollectionTypeController extends BaseController {
|
|
18
|
+
find(ctx: Context): Promise<Response>;
|
|
19
|
+
findOne(ctx: Context): Promise<Response>;
|
|
20
|
+
create(ctx: Context): Promise<Response>;
|
|
21
|
+
update(ctx: Context): Promise<Response>;
|
|
22
|
+
delete(ctx: Context): Promise<Response>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type Controller = SingleTypeController | CollectionTypeController;
|
|
@@ -1,23 +1,45 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { getOr } = require('lodash/fp');
|
|
4
4
|
|
|
5
|
+
const { contentTypes, sanitize } = require('@strapi/utils');
|
|
6
|
+
|
|
7
|
+
const { transformResponse } = require('./transform');
|
|
5
8
|
const createSingleTypeController = require('./single-type');
|
|
6
9
|
const createCollectionTypeController = require('./collection-type');
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const getAuthFromKoaContext = getOr({}, 'state.auth');
|
|
12
|
+
|
|
13
|
+
const createController = ({ contentType }) => {
|
|
14
|
+
const ctx = { contentType };
|
|
15
|
+
|
|
16
|
+
const proto = {
|
|
17
|
+
transformResponse(data, meta) {
|
|
18
|
+
return transformResponse(data, meta, { contentType });
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
sanitizeOutput(data, ctx) {
|
|
22
|
+
const auth = getAuthFromKoaContext(ctx);
|
|
23
|
+
|
|
24
|
+
return sanitize.contentAPI.output(data, contentType, { auth });
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
sanitizeInput(data, ctx) {
|
|
28
|
+
const auth = getAuthFromKoaContext(ctx);
|
|
29
|
+
|
|
30
|
+
return sanitize.contentAPI.input(data, contentType, { auth });
|
|
15
31
|
},
|
|
16
32
|
};
|
|
17
33
|
|
|
18
|
-
|
|
19
|
-
|
|
34
|
+
let ctrl;
|
|
35
|
+
|
|
36
|
+
if (contentTypes.isSingleType(contentType)) {
|
|
37
|
+
ctrl = createSingleTypeController(ctx);
|
|
38
|
+
} else {
|
|
39
|
+
ctrl = createCollectionTypeController(ctx);
|
|
20
40
|
}
|
|
21
41
|
|
|
22
|
-
return
|
|
42
|
+
return Object.assign(Object.create(proto), ctrl);
|
|
23
43
|
};
|
|
44
|
+
|
|
45
|
+
module.exports = { createController };
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { isObject } = require('lodash/fp');
|
|
4
|
+
const { ValidationError } = require('@strapi/utils').errors;
|
|
5
|
+
|
|
6
|
+
const { parseBody } = require('./transform');
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* Returns a single type controller to handle default core-api actions
|
|
7
10
|
*/
|
|
8
|
-
const createSingleTypeController = ({
|
|
11
|
+
const createSingleTypeController = ({ contentType }) => {
|
|
12
|
+
const { uid } = contentType;
|
|
13
|
+
|
|
9
14
|
return {
|
|
10
15
|
/**
|
|
11
16
|
* Retrieve single type content
|
|
@@ -14,8 +19,11 @@ const createSingleTypeController = ({ service, parseMultipartData, sanitize }) =
|
|
|
14
19
|
*/
|
|
15
20
|
async find(ctx) {
|
|
16
21
|
const { query } = ctx;
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
|
|
23
|
+
const entity = await strapi.service(uid).find(query);
|
|
24
|
+
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
|
|
25
|
+
|
|
26
|
+
return this.transformResponse(sanitizedEntity);
|
|
19
27
|
},
|
|
20
28
|
|
|
21
29
|
/**
|
|
@@ -24,24 +32,30 @@ const createSingleTypeController = ({ service, parseMultipartData, sanitize }) =
|
|
|
24
32
|
* @return {Object}
|
|
25
33
|
*/
|
|
26
34
|
async update(ctx) {
|
|
27
|
-
const {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
entity = await service.createOrUpdate({ params: query, data, files });
|
|
33
|
-
} else {
|
|
34
|
-
entity = await service.createOrUpdate({ params: query, data: body });
|
|
35
|
+
const { query } = ctx.request;
|
|
36
|
+
const { data, files } = parseBody(ctx);
|
|
37
|
+
|
|
38
|
+
if (!isObject(data)) {
|
|
39
|
+
throw new ValidationError('Missing "data" payload in the request body');
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
const sanitizedInputData = await this.sanitizeInput(data, ctx);
|
|
43
|
+
|
|
44
|
+
const entity = await strapi
|
|
45
|
+
.service(uid)
|
|
46
|
+
.createOrUpdate({ ...query, data: sanitizedInputData, files });
|
|
47
|
+
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
|
|
48
|
+
|
|
49
|
+
return this.transformResponse(sanitizedEntity);
|
|
38
50
|
},
|
|
39
51
|
|
|
40
52
|
async delete(ctx) {
|
|
41
53
|
const { query } = ctx;
|
|
42
54
|
|
|
43
|
-
const entity = await service.delete(
|
|
44
|
-
|
|
55
|
+
const entity = await strapi.service(uid).delete(query);
|
|
56
|
+
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
|
|
57
|
+
|
|
58
|
+
return this.transformResponse(sanitizedEntity);
|
|
45
59
|
},
|
|
46
60
|
};
|
|
47
61
|
};
|
|
@@ -1,41 +1,97 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { isNil, isPlainObject } = require('lodash/fp');
|
|
4
|
+
const { parseMultipartData } = require('@strapi/utils');
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
+
const parseBody = ctx => {
|
|
7
|
+
if (ctx.is('multipart')) {
|
|
8
|
+
return parseMultipartData(ctx);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { data } = ctx.request.body || {};
|
|
12
|
+
|
|
13
|
+
return { data };
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const transformResponse = (resource, meta = {}, { contentType } = {}) => {
|
|
6
17
|
if (isNil(resource)) {
|
|
7
18
|
return resource;
|
|
8
19
|
}
|
|
9
20
|
|
|
10
21
|
return {
|
|
11
|
-
data: transformEntry(resource),
|
|
22
|
+
data: transformEntry(resource, contentType),
|
|
12
23
|
meta,
|
|
13
24
|
};
|
|
14
25
|
};
|
|
15
26
|
|
|
16
|
-
const
|
|
27
|
+
const transformComponent = (data, component) => {
|
|
28
|
+
if (Array.isArray(data)) {
|
|
29
|
+
return data.map(datum => transformComponent(datum, component));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const res = transformEntry(data, component);
|
|
33
|
+
|
|
34
|
+
if (isNil(res)) {
|
|
35
|
+
return res;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { id, attributes } = res;
|
|
39
|
+
return { id, ...attributes };
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const transformEntry = (entry, type) => {
|
|
17
43
|
if (isNil(entry)) {
|
|
18
44
|
return entry;
|
|
19
45
|
}
|
|
20
46
|
|
|
21
47
|
if (Array.isArray(entry)) {
|
|
22
|
-
return entry.map(singleEntry => transformEntry(singleEntry));
|
|
48
|
+
return entry.map(singleEntry => transformEntry(singleEntry, type));
|
|
23
49
|
}
|
|
24
50
|
|
|
25
51
|
if (!isPlainObject(entry)) {
|
|
26
52
|
throw new Error('Entry must be an object');
|
|
27
53
|
}
|
|
28
54
|
|
|
29
|
-
const { id, ...
|
|
55
|
+
const { id, ...properties } = entry;
|
|
56
|
+
|
|
57
|
+
const attributeValues = {};
|
|
58
|
+
|
|
59
|
+
for (const key in properties) {
|
|
60
|
+
const property = properties[key];
|
|
61
|
+
const attribute = type && type.attributes[key];
|
|
62
|
+
|
|
63
|
+
if (attribute && attribute.type === 'relation') {
|
|
64
|
+
const data = transformEntry(property, strapi.contentType(attribute.target));
|
|
65
|
+
|
|
66
|
+
attributeValues[key] = { data };
|
|
67
|
+
} else if (attribute && attribute.type === 'component') {
|
|
68
|
+
attributeValues[key] = transformComponent(property, strapi.components[attribute.component]);
|
|
69
|
+
} else if (attribute && attribute.type === 'dynamiczone') {
|
|
70
|
+
if (isNil(property)) {
|
|
71
|
+
attributeValues[key] = property;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
attributeValues[key] = property.map(subProperty => {
|
|
75
|
+
return transformComponent(subProperty, strapi.components[subProperty.__component]);
|
|
76
|
+
});
|
|
77
|
+
} else if (attribute && attribute.type === 'media') {
|
|
78
|
+
const data = transformEntry(property, strapi.contentType('plugin::upload.file'));
|
|
79
|
+
|
|
80
|
+
attributeValues[key] = { data };
|
|
81
|
+
} else {
|
|
82
|
+
attributeValues[key] = property;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
30
85
|
|
|
31
86
|
return {
|
|
32
87
|
id,
|
|
33
|
-
attributes,
|
|
88
|
+
attributes: attributeValues,
|
|
34
89
|
// NOTE: not necessary for now
|
|
35
90
|
// meta: {},
|
|
36
91
|
};
|
|
37
92
|
};
|
|
38
93
|
|
|
39
94
|
module.exports = {
|
|
95
|
+
parseBody,
|
|
40
96
|
transformResponse,
|
|
41
97
|
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { isSingleType } = require('@strapi/utils').contentTypes;
|
|
4
|
+
|
|
5
|
+
const createRoutes = ({ contentType }) => {
|
|
6
|
+
if (isSingleType(contentType)) {
|
|
7
|
+
return getSingleTypeRoutes(contentType);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return getCollectionTypeRoutes(contentType);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const getSingleTypeRoutes = ({ uid, info }) => {
|
|
14
|
+
return {
|
|
15
|
+
find: {
|
|
16
|
+
method: 'GET',
|
|
17
|
+
path: `/${info.singularName}`,
|
|
18
|
+
handler: `${uid}.find`,
|
|
19
|
+
config: {},
|
|
20
|
+
},
|
|
21
|
+
update: {
|
|
22
|
+
method: 'PUT',
|
|
23
|
+
path: `/${info.singularName}`,
|
|
24
|
+
handler: `${uid}.update`,
|
|
25
|
+
config: {},
|
|
26
|
+
},
|
|
27
|
+
delete: {
|
|
28
|
+
method: 'DELETE',
|
|
29
|
+
path: `/${info.singularName}`,
|
|
30
|
+
handler: `${uid}.delete`,
|
|
31
|
+
config: {},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const getCollectionTypeRoutes = ({ uid, info }) => {
|
|
37
|
+
return {
|
|
38
|
+
find: {
|
|
39
|
+
method: 'GET',
|
|
40
|
+
path: `/${info.pluralName}`,
|
|
41
|
+
handler: `${uid}.find`,
|
|
42
|
+
config: {},
|
|
43
|
+
},
|
|
44
|
+
findOne: {
|
|
45
|
+
method: 'GET',
|
|
46
|
+
path: `/${info.pluralName}/:id`,
|
|
47
|
+
handler: `${uid}.findOne`,
|
|
48
|
+
config: {},
|
|
49
|
+
},
|
|
50
|
+
create: {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
path: `/${info.pluralName}`,
|
|
53
|
+
handler: `${uid}.create`,
|
|
54
|
+
config: {},
|
|
55
|
+
},
|
|
56
|
+
update: {
|
|
57
|
+
method: 'PUT',
|
|
58
|
+
path: `/${info.pluralName}/:id`,
|
|
59
|
+
handler: `${uid}.update`,
|
|
60
|
+
config: {},
|
|
61
|
+
},
|
|
62
|
+
delete: {
|
|
63
|
+
method: 'DELETE',
|
|
64
|
+
path: `/${info.pluralName}/:id`,
|
|
65
|
+
handler: `${uid}.delete`,
|
|
66
|
+
config: {},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
module.exports = { createRoutes };
|