@strapi/strapi 4.5.0-alpha.0 → 4.5.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/Strapi.js CHANGED
@@ -23,6 +23,8 @@ const entityValidator = require('./services/entity-validator');
23
23
  const createTelemetry = require('./services/metrics');
24
24
  const requestContext = require('./services/request-context');
25
25
  const createAuth = require('./services/auth');
26
+ const createCustomFields = require('./services/custom-fields');
27
+ const createContentAPI = require('./services/content-api');
26
28
  const createUpdateNotifier = require('./utils/update-notifier');
27
29
  const createStartupLogger = require('./utils/startup-logger');
28
30
  const { LIFECYCLES } = require('./utils/lifecycles');
@@ -35,12 +37,14 @@ const hooksRegistry = require('./core/registries/hooks');
35
37
  const controllersRegistry = require('./core/registries/controllers');
36
38
  const modulesRegistry = require('./core/registries/modules');
37
39
  const pluginsRegistry = require('./core/registries/plugins');
40
+ const customFieldsRegistry = require('./core/registries/custom-fields');
38
41
  const createConfigProvider = require('./core/registries/config');
39
42
  const apisRegistry = require('./core/registries/apis');
40
43
  const bootstrap = require('./core/bootstrap');
41
44
  const loaders = require('./core/loaders');
42
45
  const { destroyOnSignal } = require('./utils/signals');
43
46
  const sanitizersRegistry = require('./core/registries/sanitizers');
47
+ const convertCustomFieldType = require('./utils/convert-custom-field-type');
44
48
 
45
49
  // TODO: move somewhere else
46
50
  const draftAndPublishSync = require('./migrations/draft-publish');
@@ -75,7 +79,7 @@ class Strapi {
75
79
  // Load the app configuration from the dist directory
76
80
  const appConfig = loadConfiguration({ appDir: rootDirs.app, distDir: rootDirs.dist }, opts);
77
81
 
78
- // Instanciate the Strapi container
82
+ // Instantiate the Strapi container
79
83
  this.container = createContainer(this);
80
84
 
81
85
  // Register every Strapi registry in the container
@@ -88,8 +92,10 @@ class Strapi {
88
92
  this.container.register('controllers', controllersRegistry(this));
89
93
  this.container.register('modules', modulesRegistry(this));
90
94
  this.container.register('plugins', pluginsRegistry(this));
95
+ this.container.register('custom-fields', customFieldsRegistry(this));
91
96
  this.container.register('apis', apisRegistry(this));
92
97
  this.container.register('auth', createAuth(this));
98
+ this.container.register('content-api', createContentAPI(this));
93
99
  this.container.register('sanitizers', sanitizersRegistry(this));
94
100
 
95
101
  // Create a mapping of every useful directory (for the app, dist and static directories)
@@ -99,7 +105,7 @@ class Strapi {
99
105
  this.isLoaded = false;
100
106
  this.reload = this.reload();
101
107
 
102
- // Instanciate the Koa app & the HTTP server
108
+ // Instantiate the Koa app & the HTTP server
103
109
  this.server = createServer(this);
104
110
 
105
111
  // Strapi utils instanciation
@@ -111,6 +117,8 @@ class Strapi {
111
117
  this.telemetry = createTelemetry(this);
112
118
  this.requestContext = requestContext;
113
119
 
120
+ this.customFields = createCustomFields(this);
121
+
114
122
  createUpdateNotifier(this).notify();
115
123
  }
116
124
 
@@ -190,6 +198,10 @@ class Strapi {
190
198
  return this.container.get('auth');
191
199
  }
192
200
 
201
+ get contentAPI() {
202
+ return this.container.get('content-api');
203
+ }
204
+
193
205
  get sanitizers() {
194
206
  return this.container.get('sanitizers');
195
207
  }
@@ -379,6 +391,8 @@ class Strapi {
379
391
  this.telemetry.register();
380
392
 
381
393
  await this.runLifecyclesFunctions(LIFECYCLES.REGISTER);
394
+ // NOTE: Swap type customField for underlying data type
395
+ convertCustomFieldType(this);
382
396
 
383
397
  return this;
384
398
  }
@@ -447,6 +461,8 @@ class Strapi {
447
461
  await this.server.initMiddlewares();
448
462
  await this.server.initRouting();
449
463
 
464
+ await this.contentAPI.permissions.registerActions();
465
+
450
466
  await this.runLifecyclesFunctions(LIFECYCLES.BOOTSTRAP);
451
467
 
452
468
  this.cron.start();
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ const { has } = require('lodash/fp');
4
+ const validators = require('../../services/entity-validator/validators');
5
+
6
+ const customFieldsRegistry = (strapi) => {
7
+ const customFields = {};
8
+
9
+ return {
10
+ getAll() {
11
+ return customFields;
12
+ },
13
+ get(customField) {
14
+ const registeredCustomField = customFields[customField];
15
+ if (!registeredCustomField) {
16
+ throw new Error(`Could not find Custom Field: ${customField}`);
17
+ }
18
+
19
+ return registeredCustomField;
20
+ },
21
+ add(customField) {
22
+ const customFieldList = Array.isArray(customField) ? customField : [customField];
23
+
24
+ for (const cf of customFieldList) {
25
+ if (!has('name', cf) || !has('type', cf)) {
26
+ throw new Error(`Custom fields require a 'name' and 'type' key`);
27
+ }
28
+
29
+ const { name, plugin, type } = cf;
30
+ if (!has(type, validators)) {
31
+ throw new Error(
32
+ `Custom field type: '${type}' is not a valid Strapi type or it can't be used with a Custom Field`
33
+ );
34
+ }
35
+
36
+ const isValidObjectKey = /^(?![0-9])[a-zA-Z0-9$_-]+$/g;
37
+ if (!isValidObjectKey.test(name)) {
38
+ throw new Error(`Custom field name: '${name}' is not a valid object key`);
39
+ }
40
+
41
+ // When no plugin is specified, or it isn't found in Strapi, default to global
42
+ const uid = strapi.plugin(plugin) ? `plugin::${plugin}.${name}` : `global::${name}`;
43
+
44
+ if (has(uid, customFields)) {
45
+ throw new Error(`Custom field: '${uid}' has already been registered`);
46
+ }
47
+
48
+ customFields[uid] = cf;
49
+ }
50
+ },
51
+ };
52
+ };
53
+
54
+ module.exports = customFieldsRegistry;
@@ -32,6 +32,7 @@ const createAuthentication = () => {
32
32
 
33
33
  return this;
34
34
  },
35
+
35
36
  async authenticate(ctx, next) {
36
37
  const { route } = ctx.state;
37
38
 
@@ -47,7 +48,7 @@ const createAuthentication = () => {
47
48
  for (const strategy of strategiesToUse) {
48
49
  const result = await strategy.authenticate(ctx);
49
50
 
50
- const { authenticated = false, error = null, credentials } = result || {};
51
+ const { authenticated = false, credentials, ability = null, error = null } = result || {};
51
52
 
52
53
  if (error !== null) {
53
54
  return ctx.unauthorized(error);
@@ -58,6 +59,7 @@ const createAuthentication = () => {
58
59
  ctx.state.auth = {
59
60
  strategy,
60
61
  credentials,
62
+ ability,
61
63
  };
62
64
 
63
65
  return next();
@@ -66,6 +68,7 @@ const createAuthentication = () => {
66
68
 
67
69
  return ctx.unauthorized('Missing or invalid credentials');
68
70
  },
71
+
69
72
  async verify(auth, config = {}) {
70
73
  if (config === false) {
71
74
  return;
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const instantiatePermissionsUtilities = require('./permissions');
5
+
6
+ const transformRoutePrefixFor = (pluginName) => (route) => {
7
+ const prefix = route.config && route.config.prefix;
8
+ const path = prefix !== undefined ? `${prefix}${route.path}` : `/${pluginName}${route.path}`;
9
+
10
+ return {
11
+ ...route,
12
+ path,
13
+ };
14
+ };
15
+
16
+ /**
17
+ * Create a content API container that holds logic, tools and utils. (eg: permissions, ...)
18
+ */
19
+ const createContentAPI = (strapi) => {
20
+ const getRoutesMap = async () => {
21
+ const routesMap = {};
22
+
23
+ _.forEach(strapi.api, (api, apiName) => {
24
+ const routes = _.flatMap(api.routes, (route) => {
25
+ if (_.has(route, 'routes')) {
26
+ return route.routes;
27
+ }
28
+
29
+ return route;
30
+ }).filter((route) => route.info.type === 'content-api');
31
+
32
+ if (routes.length === 0) {
33
+ return;
34
+ }
35
+
36
+ const apiPrefix = strapi.config.get('api.rest.prefix');
37
+ routesMap[`api::${apiName}`] = routes.map((route) => ({
38
+ ...route,
39
+ path: `${apiPrefix}${route.path}`,
40
+ }));
41
+ });
42
+
43
+ _.forEach(strapi.plugins, (plugin, pluginName) => {
44
+ const transformPrefix = transformRoutePrefixFor(pluginName);
45
+
46
+ const routes = _.flatMap(plugin.routes, (route) => {
47
+ if (_.has(route, 'routes')) {
48
+ return route.routes.map(transformPrefix);
49
+ }
50
+
51
+ return transformPrefix(route);
52
+ }).filter((route) => route.info.type === 'content-api');
53
+
54
+ if (routes.length === 0) {
55
+ return;
56
+ }
57
+
58
+ const apiPrefix = strapi.config.get('api.rest.prefix');
59
+ routesMap[`plugin::${pluginName}`] = routes.map((route) => ({
60
+ ...route,
61
+ path: `${apiPrefix}${route.path}`,
62
+ }));
63
+ });
64
+
65
+ return routesMap;
66
+ };
67
+
68
+ return {
69
+ permissions: instantiatePermissionsUtilities(strapi),
70
+ getRoutesMap,
71
+ };
72
+ };
73
+
74
+ module.exports = createContentAPI;
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ const permissions = require('@strapi/permissions');
4
+
5
+ module.exports = ({ providers }) => permissions.engine.new({ providers });
@@ -0,0 +1,148 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const { createActionProvider, createConditionProvider } = require('./providers');
5
+ const createPermissionEngine = require('./engine');
6
+
7
+ /**
8
+ * Creates an handler that checks if the permission's action exists in the action registry
9
+ */
10
+ const createValidatePermissionHandler =
11
+ (actionProvider) =>
12
+ ({ permission }) => {
13
+ const action = actionProvider.get(permission.action);
14
+
15
+ // If the action isn't registered into the action provider, then ignore the permission and warn the user
16
+ if (!action) {
17
+ strapi.log.debug(
18
+ `Unknown action "${permission.action}" supplied when registering a new permission`
19
+ );
20
+ return false;
21
+ }
22
+ };
23
+
24
+ /**
25
+ * Create instances of providers and permission engine for the core content-API service.
26
+ * Also, expose utilities to get informations about available actions and such.
27
+ *
28
+ * @param {Strapi.Strapi} strapi
29
+ */
30
+ module.exports = (strapi) => {
31
+ // NOTE: Here we define both an action and condition provider,
32
+ // but at the moment, we're only using the action one.
33
+ const providers = {
34
+ action: createActionProvider(),
35
+ condition: createConditionProvider(),
36
+ };
37
+
38
+ /**
39
+ * Get a tree representation of the available Content API actions
40
+ * based on the methods of the Content API controllers.
41
+ *
42
+ * @note Only actions bound to a content-API route are returned.
43
+ *
44
+ * @return {{ [api: string]: { [controller: string]: string[] }}}
45
+ */
46
+ const getActionsMap = () => {
47
+ const actionMap = {};
48
+
49
+ /**
50
+ * Check if a controller's action is bound to the
51
+ * content-api by looking at a potential __type__ symbol
52
+ *
53
+ * @param {object} action
54
+ *
55
+ * @return {boolean}
56
+ */
57
+ const isContentApi = (action) => {
58
+ if (!_.has(action, Symbol.for('__type__'))) {
59
+ return false;
60
+ }
61
+
62
+ return action[Symbol.for('__type__')].includes('content-api');
63
+ };
64
+
65
+ /**
66
+ * Register actions from a specific API source into the result tree
67
+ *
68
+ * @param {{ [apiName]: { controllers: { [controller]: object } }}} apis The API container
69
+ * @param {string} source The prefix to use in front the API name
70
+ *
71
+ * @return {void}
72
+ */
73
+ const registerAPIsActions = (apis, source) => {
74
+ _.forEach(apis, (api, apiName) => {
75
+ const controllers = _.reduce(
76
+ api.controllers,
77
+ (acc, controller, controllerName) => {
78
+ const contentApiActions = _.pickBy(controller, isContentApi);
79
+
80
+ if (_.isEmpty(contentApiActions)) {
81
+ return acc;
82
+ }
83
+
84
+ acc[controllerName] = Object.keys(contentApiActions);
85
+
86
+ return acc;
87
+ },
88
+ {}
89
+ );
90
+
91
+ if (!_.isEmpty(controllers)) {
92
+ actionMap[`${source}::${apiName}`] = { controllers };
93
+ }
94
+ });
95
+ };
96
+
97
+ registerAPIsActions(strapi.api, 'api');
98
+ registerAPIsActions(strapi.plugins, 'plugin');
99
+
100
+ return actionMap;
101
+ };
102
+
103
+ /**
104
+ * Register all the content-API's controllers actions into the action provider.
105
+ * This method make use of the {@link getActionsMap} to generate the list of actions to register.
106
+ *
107
+ * @return {void}
108
+ */
109
+ const registerActions = async () => {
110
+ const actionsMap = getActionsMap();
111
+
112
+ // For each API
113
+ for (const [api, value] of Object.entries(actionsMap)) {
114
+ const { controllers } = value;
115
+
116
+ // Register controllers methods as actions
117
+ for (const [controller, actions] of Object.entries(controllers)) {
118
+ // Register each action individually
119
+ await Promise.all(
120
+ actions.map((action) => {
121
+ const actionUID = `${api}.${controller}.${action}`;
122
+
123
+ return providers.action.register(actionUID, {
124
+ api,
125
+ controller,
126
+ action,
127
+ uid: actionUID,
128
+ });
129
+ })
130
+ );
131
+ }
132
+ }
133
+ };
134
+
135
+ // Create an instance of a content-API permission engine
136
+ // and binds a custom validation handler to it
137
+ const engine = createPermissionEngine({ providers }).on(
138
+ 'before-format::validate.permission',
139
+ createValidatePermissionHandler(providers.action)
140
+ );
141
+
142
+ return {
143
+ engine,
144
+ providers,
145
+ registerActions,
146
+ getActionsMap,
147
+ };
148
+ };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const { providerFactory } = require('@strapi/utils');
4
+
5
+ module.exports = (options = {}) => {
6
+ const provider = providerFactory(options);
7
+
8
+ return {
9
+ ...provider,
10
+
11
+ async register(action, payload) {
12
+ if (strapi.isLoaded) {
13
+ throw new Error(`You can't register new actions outside the bootstrap function.`);
14
+ }
15
+
16
+ return provider.register(action, payload);
17
+ },
18
+ };
19
+ };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const { providerFactory } = require('@strapi/utils');
4
+
5
+ module.exports = (options = {}) => {
6
+ const provider = providerFactory(options);
7
+
8
+ return {
9
+ ...provider,
10
+
11
+ async register(condition) {
12
+ if (strapi.isLoaded) {
13
+ throw new Error(`You can't register new conditions outside the bootstrap function.`);
14
+ }
15
+
16
+ return provider.register(condition.name, condition);
17
+ },
18
+ };
19
+ };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const createActionProvider = require('./action');
4
+ const createConditionProvider = require('./condition');
5
+
6
+ module.exports = {
7
+ createActionProvider,
8
+ createConditionProvider,
9
+ };
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ const createCustomFields = (strapi) => {
4
+ return {
5
+ register(customField) {
6
+ strapi.container.get('custom-fields').add(customField);
7
+ },
8
+ };
9
+ };
10
+
11
+ module.exports = createCustomFields;
@@ -47,7 +47,6 @@ const createComponents = async (uid, data) => {
47
47
  componentValue.map((value) => createComponent(componentUID, value))
48
48
  );
49
49
 
50
- // TODO: add order
51
50
  componentBody[attributeName] = components.map(({ id }) => {
52
51
  return {
53
52
  id,
@@ -10,6 +10,7 @@ const {
10
10
  sanitize,
11
11
  } = require('@strapi/utils');
12
12
  const { ValidationError } = require('@strapi/utils').errors;
13
+ const { isAnyToMany } = require('@strapi/utils').relations;
13
14
  const { transformParamsToQuery } = require('@strapi/utils').convertQueryParams;
14
15
  const uploadFiles = require('../utils/upload-files');
15
16
 
@@ -23,6 +24,13 @@ const {
23
24
  const { pickSelectionParams } = require('./params');
24
25
  const { applyTransforms } = require('./attributes');
25
26
 
27
+ const transformLoadParamsToQuery = (uid, field, params = {}, pagination = {}) => {
28
+ return {
29
+ ...transformParamsToQuery(uid, { populate: { [field]: params } }).populate[field],
30
+ ...pagination,
31
+ };
32
+ };
33
+
26
34
  // TODO: those should be strapi events used by the webhooks not the other way arround
27
35
  const { ENTRY_CREATE, ENTRY_UPDATE, ENTRY_DELETE } = webhookUtils.webhookEvents;
28
36
 
@@ -245,35 +253,28 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
245
253
  },
246
254
 
247
255
  load(uid, entity, field, params = {}) {
248
- const { attributes } = strapi.getModel(uid);
256
+ if (!_.isString(field)) {
257
+ throw new Error(`Invalid load. Expected "${field}" to be a string`);
258
+ }
249
259
 
250
- const attribute = attributes[field];
260
+ return db.query(uid).load(entity, field, transformLoadParamsToQuery(uid, field, params));
261
+ },
251
262
 
252
- const loadParams = {};
263
+ loadPages(uid, entity, field, params = {}, pagination = {}) {
264
+ if (!_.isString(field)) {
265
+ throw new Error(`Invalid load. Expected "${field}" to be a string`);
266
+ }
253
267
 
254
- switch (attribute.type) {
255
- case 'relation': {
256
- Object.assign(loadParams, transformParamsToQuery(attribute.target, params));
257
- break;
258
- }
259
- case 'component': {
260
- Object.assign(loadParams, transformParamsToQuery(attribute.component, params));
261
- break;
262
- }
263
- case 'dynamiczone': {
264
- Object.assign(loadParams, transformParamsToQuery(null, params));
265
- break;
266
- }
267
- case 'media': {
268
- Object.assign(loadParams, transformParamsToQuery('plugin::upload.file', params));
269
- break;
270
- }
271
- default: {
272
- break;
273
- }
268
+ const { attributes } = strapi.getModel(uid);
269
+ const attribute = attributes[field];
270
+
271
+ if (!isAnyToMany(attribute)) {
272
+ throw new Error(`Invalid load. Expected "${field}" to be an anyToMany relational attribute`);
274
273
  }
275
274
 
276
- return db.query(uid).load(entity, field, loadParams);
275
+ const query = transformLoadParamsToQuery(uid, field, params, pagination);
276
+
277
+ return db.query(uid).loadPages(entity, field, query);
277
278
  },
278
279
  });
279
280
 
@@ -7,6 +7,7 @@ const isDocker = require('is-docker');
7
7
  const fetch = require('node-fetch');
8
8
  const ciEnv = require('ci-info');
9
9
  const { isUsingTypeScriptSync } = require('@strapi/typescript-utils');
10
+ const { env } = require('@strapi/utils');
10
11
  const ee = require('../../utils/ee');
11
12
  const machineID = require('../../utils/machine-id');
12
13
  const stringifyDeep = require('./stringify-deep');
@@ -54,6 +55,7 @@ module.exports = (strapi) => {
54
55
  projectType: isEE ? 'Enterprise' : 'Community',
55
56
  useTypescriptOnServer: isUsingTypeScriptSync(serverRootPath),
56
57
  useTypescriptOnAdmin: isUsingTypeScriptSync(adminRootPath),
58
+ isHostedOnStrapiCloud: env('STRAPI_HOSTING', null) === 'strapi.cloud',
57
59
  };
58
60
 
59
61
  addPackageJsonStrapiMetadata(anonymousMetadata, strapi);
@@ -29,6 +29,9 @@ export type NonUniqueAttribute = { unique: false };
29
29
  export type ConfigurableAttribute = { configurable: true };
30
30
  export type NonConfigurableAttribute = { configurable: false };
31
31
 
32
+ // custom field
33
+ export type CustomField<T extends string, P extends object = undefined> = { customField: T, options?: P };
34
+
32
35
  // min/max
33
36
  export type SetMinMax<T extends MinMaxOption<U>, U = number> = T;
34
37
 
@@ -5,6 +5,28 @@ import type { StringMap } from './utils';
5
5
  import type { GenericController } from '../../../core-api/controller'
6
6
  import type { GenericService } from '../../../core-api/service'
7
7
 
8
+ // TODO move custom fields types to a separate file
9
+ interface CustomFieldServerOptions {
10
+ /**
11
+ * The name of the custom field
12
+ */
13
+ name: string;
14
+
15
+ /**
16
+ * The name of the plugin creating the custom field
17
+ */
18
+ plugin?: string;
19
+
20
+ /**
21
+ * The existing Strapi data type the custom field uses
22
+ */
23
+ type: string;
24
+ }
25
+
26
+ interface CustomFields {
27
+ register: (customFields: CustomFieldServerOptions[] | CustomFieldServerOptions) => void;
28
+ }
29
+
8
30
  /**
9
31
  * The Strapi interface implemented by the main Strapi class.
10
32
  */
@@ -24,6 +46,11 @@ export interface Strapi {
24
46
  */
25
47
  readonly auth: any;
26
48
 
49
+ /**
50
+ * Getter for the Strapi content API container
51
+ */
52
+ readonly contentAPI: any;
53
+
27
54
  /**
28
55
  * Getter for the Strapi sanitizers container
29
56
  */
@@ -65,6 +92,13 @@ export interface Strapi {
65
92
  */
66
93
  contentType(uid: string): any;
67
94
 
95
+ /**
96
+ * The custom fields registry
97
+ *
98
+ * It returns the custom fields interface
99
+ */
100
+ readonly customFields: CustomFields;
101
+
68
102
  /**
69
103
  * Getter for the Strapi policies container
70
104
  *
@@ -195,7 +229,7 @@ export interface Strapi {
195
229
  /**
196
230
  * Restart the server and reload all the configuration.
197
231
  * It re-runs all the lifecycles phases.
198
- *
232
+ *
199
233
  * @example
200
234
  * ``` ts
201
235
  * setImmediate(() => strapi.reload());
@@ -223,13 +257,13 @@ export interface Strapi {
223
257
  /**
224
258
  * Opent he administration panel in a browser if the option is enabled.
225
259
  * You can disable it using the admin.autoOpen configuration variable.
226
- *
260
+ *
227
261
  * Note: It only works in development envs.
228
262
  */
229
263
  openAdmin(options: { isInitialized: boolean }): Promise<void>;
230
264
 
231
265
  /**
232
- * Load the admin panel server logic into the server code and initialize its configuration.
266
+ * Load the admin panel server logic into the server code and initialize its configuration.
233
267
  */
234
268
  loadAdmin(): Promise<void>;
235
269
 
@@ -288,7 +322,7 @@ export interface Strapi {
288
322
  container: any;
289
323
 
290
324
  /**
291
- * References to all the directories handled by Strapi
325
+ * References to all the directories handled by Strapi
292
326
  */
293
327
  dirs: StrapiDirectories;
294
328
 
@@ -323,7 +357,7 @@ export interface Strapi {
323
357
  startupLogger: any;
324
358
 
325
359
  /**
326
- * Strapi logger used to send errors, warning or information messages
360
+ * Strapi logger used to send errors, warning or information messages
327
361
  */
328
362
  log: any;
329
363
 
@@ -356,7 +390,7 @@ export interface Strapi {
356
390
  /**
357
391
  * Entity Service instance
358
392
  */
359
- entityService: any;
393
+ entityService: any;
360
394
  }
361
395
 
362
396
  export interface Lifecycles {
@@ -389,4 +423,4 @@ export interface StrapiDirectories {
389
423
  middlewares: string;
390
424
  config: string;
391
425
  };
392
- }
426
+ }
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ const convertCustomFieldType = (strapi) => {
4
+ const allContentTypeSchemaAttributes = Object.values(strapi.contentTypes).map(
5
+ (schema) => schema.attributes
6
+ );
7
+ const allComponentSchemaAttributes = Object.values(strapi.components).map(
8
+ (schema) => schema.attributes
9
+ );
10
+ const allSchemasAttributes = [...allContentTypeSchemaAttributes, ...allComponentSchemaAttributes];
11
+
12
+ for (const schemaAttrbutes of allSchemasAttributes) {
13
+ for (const attribute of Object.values(schemaAttrbutes)) {
14
+ if (attribute.type === 'customField') {
15
+ const customField = strapi.container.get('custom-fields').get(attribute.customField);
16
+ attribute.type = customField.type;
17
+ }
18
+ }
19
+ }
20
+ };
21
+
22
+ module.exports = convertCustomFieldType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/strapi",
3
- "version": "4.5.0-alpha.0",
3
+ "version": "4.5.0-beta.0",
4
4
  "description": "An open source headless CMS solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier. Databases supported: MySQL, MariaDB, PostgreSQL, SQLite",
5
5
  "keywords": [
6
6
  "strapi",
@@ -78,19 +78,20 @@
78
78
  "test:unit": "jest --verbose"
79
79
  },
80
80
  "dependencies": {
81
- "@koa/cors": "3.4.1",
81
+ "@koa/cors": "3.4.2",
82
82
  "@koa/router": "10.1.1",
83
- "@strapi/admin": "4.5.0-alpha.0",
84
- "@strapi/database": "4.5.0-alpha.0",
85
- "@strapi/generate-new": "4.5.0-alpha.0",
86
- "@strapi/generators": "4.5.0-alpha.0",
87
- "@strapi/logger": "4.5.0-alpha.0",
88
- "@strapi/plugin-content-manager": "4.5.0-alpha.0",
89
- "@strapi/plugin-content-type-builder": "4.5.0-alpha.0",
90
- "@strapi/plugin-email": "4.5.0-alpha.0",
91
- "@strapi/plugin-upload": "4.5.0-alpha.0",
92
- "@strapi/typescript-utils": "4.5.0-alpha.0",
93
- "@strapi/utils": "4.5.0-alpha.0",
83
+ "@strapi/admin": "4.5.0-beta.0",
84
+ "@strapi/database": "4.5.0-beta.0",
85
+ "@strapi/generate-new": "4.5.0-beta.0",
86
+ "@strapi/generators": "4.5.0-beta.0",
87
+ "@strapi/logger": "4.5.0-beta.0",
88
+ "@strapi/permissions": "4.5.0-beta.0",
89
+ "@strapi/plugin-content-manager": "4.5.0-beta.0",
90
+ "@strapi/plugin-content-type-builder": "4.5.0-beta.0",
91
+ "@strapi/plugin-email": "4.5.0-beta.0",
92
+ "@strapi/plugin-upload": "4.5.0-beta.0",
93
+ "@strapi/typescript-utils": "4.5.0-beta.0",
94
+ "@strapi/utils": "4.5.0-beta.0",
94
95
  "bcryptjs": "2.4.3",
95
96
  "boxen": "5.1.2",
96
97
  "chalk": "4.1.2",
@@ -139,5 +140,5 @@
139
140
  "node": ">=14.19.1 <=18.x.x",
140
141
  "npm": ">=6.0.0"
141
142
  },
142
- "gitHead": "c9a98c4dbcf3c4f2a449f8d96e7cbe4cd9b1e0f5"
143
+ "gitHead": "ee98b9a9cbb6e0e07e781ff9e87eb170c72e50df"
143
144
  }