@strapi/strapi 4.0.0-next.9 → 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.
Files changed (140) hide show
  1. package/README.md +12 -12
  2. package/bin/strapi.js +32 -5
  3. package/lib/Strapi.js +140 -72
  4. package/lib/commands/build.js +16 -6
  5. package/lib/commands/console.js +1 -1
  6. package/lib/commands/content-types/list.js +22 -0
  7. package/lib/commands/develop.js +14 -15
  8. package/lib/commands/generate-template.js +4 -5
  9. package/lib/commands/hooks/list.js +22 -0
  10. package/lib/commands/middlewares/list.js +22 -0
  11. package/lib/commands/new.js +3 -1
  12. package/lib/commands/policies/list.js +22 -0
  13. package/lib/commands/routes/list.js +28 -0
  14. package/lib/commands/services/list.js +22 -0
  15. package/lib/commands/watchAdmin.js +18 -9
  16. package/lib/core/app-configuration/index.js +3 -19
  17. package/lib/core/bootstrap.js +3 -34
  18. package/lib/core/domain/content-type/index.js +3 -7
  19. package/lib/core/domain/module/index.js +8 -6
  20. package/lib/core/domain/module/validation.js +1 -4
  21. package/lib/core/loaders/admin.js +2 -2
  22. package/lib/core/loaders/apis.js +7 -7
  23. package/lib/core/loaders/components.js +3 -5
  24. package/lib/core/loaders/index.js +1 -0
  25. package/lib/core/loaders/middlewares.js +23 -123
  26. package/lib/core/loaders/plugins/get-enabled-plugins.js +48 -14
  27. package/lib/core/loaders/plugins/index.js +30 -14
  28. package/lib/core/loaders/policies.js +1 -1
  29. package/lib/core/loaders/src-index.js +39 -0
  30. package/lib/core/registries/apis.js +2 -16
  31. package/lib/core/registries/content-types.js +50 -6
  32. package/lib/core/registries/controllers.d.ts +7 -0
  33. package/lib/core/registries/controllers.js +74 -3
  34. package/lib/core/registries/hooks.d.ts +20 -0
  35. package/lib/core/registries/hooks.js +87 -0
  36. package/lib/core/registries/middlewares.d.ts +5 -0
  37. package/lib/core/registries/middlewares.js +61 -2
  38. package/lib/core/registries/modules.js +3 -3
  39. package/lib/core/registries/plugins.js +2 -2
  40. package/lib/core/registries/policies.d.ts +9 -0
  41. package/lib/core/registries/policies.js +57 -6
  42. package/lib/core/registries/services.d.ts +7 -0
  43. package/lib/core/registries/services.js +71 -15
  44. package/lib/core-api/controller/collection-type.js +38 -11
  45. package/lib/core-api/controller/index.d.ts +25 -0
  46. package/lib/core-api/controller/index.js +30 -11
  47. package/lib/core-api/controller/single-type.js +26 -7
  48. package/lib/core-api/controller/transform.js +28 -3
  49. package/lib/core-api/routes/index.js +71 -0
  50. package/lib/core-api/service/collection-type.js +22 -27
  51. package/lib/core-api/service/index.d.ts +21 -0
  52. package/lib/core-api/service/index.js +9 -19
  53. package/lib/core-api/service/pagination.js +7 -2
  54. package/lib/core-api/service/single-type.js +17 -20
  55. package/lib/factories.d.ts +48 -0
  56. package/lib/factories.js +84 -0
  57. package/lib/index.d.ts +10 -31
  58. package/lib/index.js +5 -1
  59. package/lib/middlewares/body.js +33 -0
  60. package/lib/middlewares/compression.js +8 -0
  61. package/lib/middlewares/cors.js +58 -0
  62. package/lib/middlewares/errors.js +40 -0
  63. package/lib/middlewares/favicon.js +19 -0
  64. package/lib/middlewares/index.d.ts +5 -0
  65. package/lib/middlewares/index.js +30 -116
  66. package/lib/middlewares/ip.js +8 -0
  67. package/lib/middlewares/logger.js +27 -0
  68. package/lib/middlewares/powered-by.js +20 -0
  69. package/lib/middlewares/public/index.js +98 -73
  70. package/lib/middlewares/query.js +46 -0
  71. package/lib/middlewares/response-time.js +15 -0
  72. package/lib/middlewares/responses.js +19 -0
  73. package/lib/middlewares/security.js +51 -0
  74. package/lib/middlewares/session/index.js +6 -6
  75. package/lib/migrations/draft-publish.js +57 -0
  76. package/lib/services/auth/index.js +87 -0
  77. package/lib/services/core-store.js +64 -49
  78. package/lib/services/cron.js +54 -0
  79. package/lib/services/entity-service/attributes/index.js +31 -0
  80. package/lib/services/entity-service/attributes/transforms.js +20 -0
  81. package/lib/services/entity-service/components.js +39 -15
  82. package/lib/services/entity-service/index.d.ts +91 -0
  83. package/lib/services/entity-service/index.js +118 -60
  84. package/lib/services/entity-service/params.js +48 -81
  85. package/lib/services/entity-validator/index.js +76 -43
  86. package/lib/services/entity-validator/validators.js +129 -43
  87. package/lib/services/errors.js +77 -0
  88. package/lib/services/fs.js +1 -1
  89. package/lib/services/metrics/index.js +38 -36
  90. package/lib/services/server/admin-api.js +14 -0
  91. package/lib/services/server/api.js +36 -0
  92. package/lib/services/server/compose-endpoint.js +141 -0
  93. package/lib/services/server/content-api.js +16 -0
  94. package/lib/{server.js → services/server/http-server.js} +0 -0
  95. package/lib/services/server/index.js +127 -0
  96. package/lib/services/server/koa.js +64 -0
  97. package/lib/services/server/middleware.js +122 -0
  98. package/lib/services/server/policy.js +32 -0
  99. package/lib/services/server/register-middlewares.js +110 -0
  100. package/lib/services/server/register-routes.js +106 -0
  101. package/lib/services/server/routing.js +120 -0
  102. package/lib/services/webhook-runner.js +1 -1
  103. package/lib/utils/ee.js +3 -3
  104. package/lib/utils/get-dirs.js +17 -0
  105. package/lib/utils/index.js +2 -0
  106. package/lib/utils/signals.js +24 -0
  107. package/lib/utils/update-notifier/index.js +2 -1
  108. package/package.json +93 -93
  109. package/lib/core/app-configuration/load-functions.js +0 -28
  110. package/lib/core-api/index.js +0 -39
  111. package/lib/middlewares/boom/defaults.json +0 -5
  112. package/lib/middlewares/boom/index.js +0 -147
  113. package/lib/middlewares/cors/index.js +0 -66
  114. package/lib/middlewares/cron/defaults.json +0 -5
  115. package/lib/middlewares/cron/index.js +0 -43
  116. package/lib/middlewares/favicon/defaults.json +0 -7
  117. package/lib/middlewares/favicon/index.js +0 -32
  118. package/lib/middlewares/gzip/defaults.json +0 -6
  119. package/lib/middlewares/gzip/index.js +0 -19
  120. package/lib/middlewares/helmet/defaults.json +0 -18
  121. package/lib/middlewares/helmet/index.js +0 -9
  122. package/lib/middlewares/ip/defaults.json +0 -7
  123. package/lib/middlewares/ip/index.js +0 -25
  124. package/lib/middlewares/language/defaults.json +0 -9
  125. package/lib/middlewares/language/index.js +0 -40
  126. package/lib/middlewares/logger/defaults.json +0 -5
  127. package/lib/middlewares/logger/index.js +0 -37
  128. package/lib/middlewares/parser/defaults.json +0 -11
  129. package/lib/middlewares/parser/index.js +0 -72
  130. package/lib/middlewares/poweredBy/defaults.json +0 -5
  131. package/lib/middlewares/poweredBy/index.js +0 -16
  132. package/lib/middlewares/public/defaults.json +0 -8
  133. package/lib/middlewares/responseTime/defaults.json +0 -5
  134. package/lib/middlewares/responseTime/index.js +0 -25
  135. package/lib/middlewares/responses/defaults.json +0 -5
  136. package/lib/middlewares/responses/index.js +0 -18
  137. package/lib/middlewares/router/defaults.json +0 -7
  138. package/lib/middlewares/router/index.js +0 -72
  139. package/lib/middlewares/router/utils/compose-endpoint.js +0 -169
  140. package/lib/utils/get-prefixed-dependencies.js +0 -7
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ const { pickBy } = require('lodash/fp');
4
+ const { addNamespace, hasNamespace } = require('../utils');
5
+
6
+ /**
7
+ * @typedef {import('./hooks').Hook} Hook
8
+ */
9
+
10
+ const hooksRegistry = () => {
11
+ const hooks = {};
12
+
13
+ return {
14
+ /**
15
+ * Returns this list of registered hooks uids
16
+ * @returns {string[]}
17
+ */
18
+ keys() {
19
+ return Object.keys(hooks);
20
+ },
21
+
22
+ /**
23
+ * Returns the instance of a hook.
24
+ * @param {string} uid
25
+ * @returns {Hook}
26
+ */
27
+ get(uid) {
28
+ return hooks[uid];
29
+ },
30
+
31
+ /**
32
+ * Returns a map with all the hooks in a namespace
33
+ * @param {string} namespace
34
+ * @returns {{ [key: string]: Hook }}
35
+ */
36
+ getAll(namespace) {
37
+ return pickBy((_, uid) => hasNamespace(uid, namespace))(hooks);
38
+ },
39
+
40
+ /**
41
+ * Registers a hook
42
+ * @param {string} uid
43
+ * @param {Hook} hook
44
+ */
45
+ set(uid, hook) {
46
+ hooks[uid] = hook;
47
+ return this;
48
+ },
49
+
50
+ /**
51
+ * Registers a map of hooks for a specific namespace
52
+ * @param {string} namespace
53
+ * @param {{ [key: string]: Hook }} newHooks
54
+ * @returns
55
+ */
56
+ add(namespace, hooks) {
57
+ for (const hookName in hooks) {
58
+ const hook = hooks[hookName];
59
+ const uid = addNamespace(hookName, namespace);
60
+
61
+ this.set(uid, hook);
62
+ }
63
+
64
+ return this;
65
+ },
66
+
67
+ /**
68
+ * Wraps a hook to extend it
69
+ * @param {string} uid
70
+ * @param {(hook: Hook) => Hook} extendFn
71
+ */
72
+ extend(uid, extendFn) {
73
+ const currentHook = this.get(uid);
74
+
75
+ if (!currentHook) {
76
+ throw new Error(`Hook ${uid} doesn't exist`);
77
+ }
78
+
79
+ const newHook = extendFn(currentHook);
80
+ hooks[uid] = newHook;
81
+
82
+ return this;
83
+ },
84
+ };
85
+ };
86
+
87
+ module.exports = hooksRegistry;
@@ -0,0 +1,5 @@
1
+ import { BaseContext, Middleware as KoaMiddleware } from 'koa';
2
+ import { Strapi } from '../../';
3
+ import { MiddlewareFactory } from '../../middlewares';
4
+
5
+ export type Middleware = KoaMiddleware | MiddlewareFactory;
@@ -3,16 +3,57 @@
3
3
  const { pickBy, has } = require('lodash/fp');
4
4
  const { addNamespace, hasNamespace } = require('../utils');
5
5
 
6
+ /**
7
+ * @typedef {import('./middlewares').Middleware} Middleware
8
+ */
9
+
10
+ // TODO: move instantiation part here instead of in the server service
6
11
  const middlewaresRegistry = () => {
7
12
  const middlewares = {};
8
13
 
9
14
  return {
10
- get(middlewareUID) {
11
- return middlewares[middlewareUID];
15
+ /**
16
+ * Returns this list of registered middlewares uids
17
+ * @returns {string[]}
18
+ */
19
+ keys() {
20
+ return Object.keys(middlewares);
21
+ },
22
+
23
+ /**
24
+ * Returns the instance of a middleware. Instantiate the middleware if not already done
25
+ * @param {string} uid
26
+ * @returns {Middleware}
27
+ */
28
+ get(uid) {
29
+ return middlewares[uid];
12
30
  },
31
+
32
+ /**
33
+ * Returns a map with all the middlewares in a namespace
34
+ * @param {string} namespace
35
+ * @returns {{ [key: string]: Middleware }}
36
+ */
13
37
  getAll(namespace) {
14
38
  return pickBy((_, uid) => hasNamespace(uid, namespace))(middlewares);
15
39
  },
40
+
41
+ /**
42
+ * Registers a middleware
43
+ * @param {string} uid
44
+ * @param {Middleware} middleware
45
+ */
46
+ set(uid, middleware) {
47
+ middlewares[uid] = middleware;
48
+ return this;
49
+ },
50
+
51
+ /**
52
+ * Registers a map of middlewares for a specific namespace
53
+ * @param {string} namespace
54
+ * @param {{ [key: string]: Middleware }} newMiddlewares
55
+ * @returns
56
+ */
16
57
  add(namespace, rawMiddlewares) {
17
58
  for (const middlewareName in rawMiddlewares) {
18
59
  const middleware = rawMiddlewares[middlewareName];
@@ -24,6 +65,24 @@ const middlewaresRegistry = () => {
24
65
  middlewares[uid] = middleware;
25
66
  }
26
67
  },
68
+
69
+ /**
70
+ * Wraps a middleware to extend it
71
+ * @param {string} uid
72
+ * @param {(middleware: Middleware) => Middleware} extendFn
73
+ */
74
+ extend(uid, extendFn) {
75
+ const currentMiddleware = this.get(uid);
76
+
77
+ if (!currentMiddleware) {
78
+ throw new Error(`Middleware ${uid} doesn't exist`);
79
+ }
80
+
81
+ const newMiddleware = extendFn(currentMiddleware);
82
+ middlewares[uid] = newMiddleware;
83
+
84
+ return this;
85
+ },
27
86
  };
28
87
  };
29
88
 
@@ -25,17 +25,17 @@ const modulesRegistry = strapi => {
25
25
  },
26
26
  async bootstrap() {
27
27
  for (const mod of Object.values(modules)) {
28
- await mod.bootstrap(strapi);
28
+ await mod.bootstrap();
29
29
  }
30
30
  },
31
31
  async register() {
32
32
  for (const mod of Object.values(modules)) {
33
- await mod.register(strapi);
33
+ await mod.register();
34
34
  }
35
35
  },
36
36
  async destroy() {
37
37
  for (const mod of Object.values(modules)) {
38
- await mod.destroy(strapi);
38
+ await mod.destroy();
39
39
  }
40
40
  },
41
41
  };
@@ -17,8 +17,8 @@ const pluginsRegistry = strapi => {
17
17
  throw new Error(`Plugin ${name} has already been registered.`);
18
18
  }
19
19
 
20
- const moduleInstance = strapi.container.get('modules').add(`plugin::${name}`, pluginConfig);
21
- plugins[name] = moduleInstance;
20
+ const pluginModule = strapi.container.get('modules').add(`plugin::${name}`, pluginConfig);
21
+ plugins[name] = pluginModule;
22
22
 
23
23
  return plugins[name];
24
24
  },
@@ -0,0 +1,9 @@
1
+ import { BaseContext } from 'koa';
2
+ import { Strapi } from '../../';
3
+
4
+ interface PolicyContext extends BaseContext {
5
+ type: string;
6
+ is(name): boolean;
7
+ }
8
+
9
+ export type Policy = (ctx: PolicyContext, { strapi: Strapi }) => boolean | undefined;
@@ -3,16 +3,57 @@
3
3
  const { pickBy, has } = require('lodash/fp');
4
4
  const { addNamespace, hasNamespace } = require('../utils');
5
5
 
6
+ /**
7
+ * @typedef {import('./policies').Policy} Policy
8
+ */
9
+
10
+ // TODO: move instantiation part here instead of in the policy utils
6
11
  const policiesRegistry = () => {
7
12
  const policies = {};
8
13
 
9
14
  return {
10
- get(policyUID) {
11
- return policies[policyUID];
15
+ /**
16
+ * Returns this list of registered policies uids
17
+ * @returns {string[]}
18
+ */
19
+ keys() {
20
+ return Object.keys(policies);
21
+ },
22
+
23
+ /**
24
+ * Returns the instance of a policy. Instantiate the policy if not already done
25
+ * @param {string} uid
26
+ * @returns {Policy}
27
+ */
28
+ get(uid) {
29
+ return policies[uid];
12
30
  },
31
+
32
+ /**
33
+ * Returns a map with all the policies in a namespace
34
+ * @param {string} namespace
35
+ * @returns {{ [key: string]: Policy }}
36
+ */
13
37
  getAll(namespace) {
14
38
  return pickBy((_, uid) => hasNamespace(uid, namespace))(policies);
15
39
  },
40
+
41
+ /**
42
+ * Registers a policy
43
+ * @param {string} uid
44
+ * @param {Policy} policy
45
+ */
46
+ set(uid, policy) {
47
+ policies[uid] = policy;
48
+ return this;
49
+ },
50
+
51
+ /**
52
+ * Registers a map of policies for a specific namespace
53
+ * @param {string} namespace
54
+ * @param {{ [key: string]: Policy }} newPolicies
55
+ * @returns
56
+ */
16
57
  add(namespace, newPolicies) {
17
58
  for (const policyName in newPolicies) {
18
59
  const policy = newPolicies[policyName];
@@ -24,13 +65,23 @@ const policiesRegistry = () => {
24
65
  policies[uid] = policy;
25
66
  }
26
67
  },
27
- extend(policyUID, extendFn) {
28
- const currentPolicy = this.get(policyUID);
68
+
69
+ /**
70
+ * Wraps a policy to extend it
71
+ * @param {string} uid
72
+ * @param {(policy: Policy) => Policy} extendFn
73
+ */
74
+ extend(uid, extendFn) {
75
+ const currentPolicy = this.get(uid);
76
+
29
77
  if (!currentPolicy) {
30
- throw new Error(`Policy ${policyUID} doesn't exist`);
78
+ throw new Error(`Policy ${uid} doesn't exist`);
31
79
  }
80
+
32
81
  const newPolicy = extendFn(currentPolicy);
33
- policies[policyUID] = newPolicy;
82
+ policies[uid] = newPolicy;
83
+
84
+ return this;
34
85
  },
35
86
  };
36
87
  };
@@ -0,0 +1,7 @@
1
+ import { Strapi } from '../../';
2
+
3
+ export type Service = {
4
+ [key: string]: (...args: any) => any;
5
+ };
6
+
7
+ export type ServiceFactory = ({ strapi: Strapi }) => Service;
@@ -1,36 +1,82 @@
1
1
  'use strict';
2
2
 
3
- const _ = require('lodash');
4
3
  const { pickBy, has } = require('lodash/fp');
5
4
  const { addNamespace, hasNamespace } = require('../utils');
6
5
 
6
+ /**
7
+ * @typedef {import('./services').Service} Service
8
+ * @typedef {import('./services').ServiceFactory} ServiceFactory
9
+ */
10
+
7
11
  const servicesRegistry = strapi => {
8
12
  const services = {};
9
- const instanciatedServices = {};
13
+ const instantiatedServices = {};
10
14
 
11
15
  return {
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
+ */
12
29
  get(uid) {
13
- if (instanciatedServices[uid]) {
14
- return instanciatedServices[uid];
30
+ if (instantiatedServices[uid]) {
31
+ return instantiatedServices[uid];
15
32
  }
16
33
 
17
34
  const service = services[uid];
18
35
  if (service) {
19
- instanciatedServices[uid] = service({ strapi });
20
- return instanciatedServices[uid];
36
+ instantiatedServices[uid] = typeof service === 'function' ? service({ strapi }) : service;
37
+ return instantiatedServices[uid];
21
38
  }
22
-
23
- return undefined;
24
39
  },
40
+
41
+ /**
42
+ * Returns a map with all the services in a namespace
43
+ * @param {string} namespace
44
+ * @returns {{ [key: string]: Service }}
45
+ */
25
46
  getAll(namespace) {
26
47
  const filteredServices = pickBy((_, uid) => hasNamespace(uid, namespace))(services);
27
48
 
28
- return _.mapValues(filteredServices, (service, serviceUID) => this.get(serviceUID));
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;
29
61
  },
30
- set(uid, value) {
31
- instanciatedServices[uid] = value;
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];
32
71
  return this;
33
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
+ */
34
80
  add(namespace, newServices) {
35
81
  for (const serviceName in newServices) {
36
82
  const service = newServices[serviceName];
@@ -44,13 +90,23 @@ const servicesRegistry = strapi => {
44
90
 
45
91
  return this;
46
92
  },
47
- extend(serviceUID, extendFn) {
48
- const currentService = this.get(serviceUID);
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
+
49
102
  if (!currentService) {
50
- throw new Error(`Service ${serviceUID} doesn't exist`);
103
+ throw new Error(`Service ${uid} doesn't exist`);
51
104
  }
105
+
52
106
  const newService = extendFn(currentService);
53
- instanciatedServices[serviceUID] = newService;
107
+ instantiatedServices[uid] = newService;
108
+
109
+ return this;
54
110
  },
55
111
  };
56
112
  };
@@ -1,12 +1,17 @@
1
1
  'use strict';
2
2
 
3
+ const { isObject } = require('lodash/fp');
4
+ const { ValidationError } = require('@strapi/utils').errors;
5
+
3
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 = ({ service, sanitize, transformResponse }) => {
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, transformResponse }
16
21
  async find(ctx) {
17
22
  const { query } = ctx;
18
23
 
19
- const { results, pagination } = await service.find({ params: query });
24
+ const { results, pagination } = await strapi.service(uid).find(query);
25
+ const sanitizedResults = await this.sanitizeOutput(results, ctx);
20
26
 
21
- return transformResponse(sanitize(results), { pagination });
27
+ return this.transformResponse(sanitizedResults, { pagination });
22
28
  },
23
29
 
24
30
  /**
@@ -30,9 +36,10 @@ const createCollectionTypeController = ({ service, sanitize, transformResponse }
30
36
  const { id } = ctx.params;
31
37
  const { query } = ctx;
32
38
 
33
- const entity = await service.findOne(id, { params: query });
39
+ const entity = await strapi.service(uid).findOne(id, query);
40
+ const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
34
41
 
35
- return transformResponse(sanitize(entity));
42
+ return this.transformResponse(sanitizedEntity);
36
43
  },
37
44
 
38
45
  /**
@@ -45,9 +52,18 @@ const createCollectionTypeController = ({ service, sanitize, transformResponse }
45
52
 
46
53
  const { data, files } = parseBody(ctx);
47
54
 
48
- const entity = await service.create({ params: query, data, files });
55
+ if (!isObject(data)) {
56
+ throw new ValidationError('Missing "data" payload in the request body');
57
+ }
58
+
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);
49
65
 
50
- return transformResponse(sanitize(entity));
66
+ return this.transformResponse(sanitizedEntity);
51
67
  },
52
68
 
53
69
  /**
@@ -61,9 +77,18 @@ const createCollectionTypeController = ({ service, sanitize, transformResponse }
61
77
 
62
78
  const { data, files } = parseBody(ctx);
63
79
 
64
- const entity = await service.update(id, { params: query, data, files });
80
+ if (!isObject(data)) {
81
+ throw new ValidationError('Missing "data" payload in the request body');
82
+ }
65
83
 
66
- return transformResponse(sanitize(entity));
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);
67
92
  },
68
93
 
69
94
  /**
@@ -75,8 +100,10 @@ const createCollectionTypeController = ({ service, sanitize, transformResponse }
75
100
  const { id } = ctx.params;
76
101
  const { query } = ctx;
77
102
 
78
- const entity = await service.delete(id, { params: query });
79
- return transformResponse(sanitize(entity));
103
+ const entity = await strapi.service(uid).delete(id, query);
104
+ const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
105
+
106
+ return this.transformResponse(sanitizedEntity);
80
107
  },
81
108
  };
82
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,26 +1,45 @@
1
1
  'use strict';
2
2
 
3
- const { sanitizeEntity, contentTypes } = require('@strapi/utils');
3
+ const { getOr } = require('lodash/fp');
4
+
5
+ const { contentTypes, sanitize } = require('@strapi/utils');
4
6
 
5
7
  const { transformResponse } = require('./transform');
6
8
  const createSingleTypeController = require('./single-type');
7
9
  const createCollectionTypeController = require('./collection-type');
8
10
 
9
- module.exports = ({ service, model }) => {
10
- const ctx = {
11
- model,
12
- service,
11
+ const getAuthFromKoaContext = getOr({}, 'state.auth');
12
+
13
+ const createController = ({ contentType }) => {
14
+ const ctx = { contentType };
15
+
16
+ const proto = {
13
17
  transformResponse(data, meta) {
14
- return transformResponse(data, meta, { contentType: model });
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 });
15
25
  },
16
- sanitize(data) {
17
- return sanitizeEntity(data, { model: strapi.getModel(model.uid) });
26
+
27
+ sanitizeInput(data, ctx) {
28
+ const auth = getAuthFromKoaContext(ctx);
29
+
30
+ return sanitize.contentAPI.input(data, contentType, { auth });
18
31
  },
19
32
  };
20
33
 
21
- if (contentTypes.isSingleType(model)) {
22
- return createSingleTypeController(ctx);
34
+ let ctrl;
35
+
36
+ if (contentTypes.isSingleType(contentType)) {
37
+ ctrl = createSingleTypeController(ctx);
38
+ } else {
39
+ ctrl = createCollectionTypeController(ctx);
23
40
  }
24
41
 
25
- return createCollectionTypeController(ctx);
42
+ return Object.assign(Object.create(proto), ctrl);
26
43
  };
44
+
45
+ module.exports = { createController };
@@ -1,11 +1,16 @@
1
1
  'use strict';
2
2
 
3
+ const { isObject } = require('lodash/fp');
4
+ const { ValidationError } = require('@strapi/utils').errors;
5
+
3
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 = ({ service, sanitize, transformResponse }) => {
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, sanitize, transformResponse }) =>
14
19
  */
15
20
  async find(ctx) {
16
21
  const { query } = ctx;
17
- const entity = await service.find({ params: query });
18
- return transformResponse(sanitize(entity));
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
  /**
@@ -27,16 +35,27 @@ const createSingleTypeController = ({ service, sanitize, transformResponse }) =>
27
35
  const { query } = ctx.request;
28
36
  const { data, files } = parseBody(ctx);
29
37
 
30
- const entity = await service.createOrUpdate({ params: query, data, files });
38
+ if (!isObject(data)) {
39
+ throw new ValidationError('Missing "data" payload in the request body');
40
+ }
31
41
 
32
- return transformResponse(sanitize(entity));
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);
33
50
  },
34
51
 
35
52
  async delete(ctx) {
36
53
  const { query } = ctx;
37
54
 
38
- const entity = await service.delete({ params: query });
39
- return transformResponse(sanitize(entity));
55
+ const entity = await strapi.service(uid).delete(query);
56
+ const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
57
+
58
+ return this.transformResponse(sanitizedEntity);
40
59
  },
41
60
  };
42
61
  };