Merge pull request #5005 from strapi/single-types/add-single-type-kind

Add single type kind property to content types
This commit is contained in:
Alexandre BODIN 2020-01-16 18:07:02 +01:00 committed by GitHub
commit c47fae0638
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 545 additions and 239 deletions

View File

@ -5,10 +5,19 @@ const _ = require('lodash');
const { const {
validateContentTypeInput, validateContentTypeInput,
validateUpdateContentTypeInput, validateUpdateContentTypeInput,
validateKind,
} = require('./validation/content-type'); } = require('./validation/content-type');
module.exports = { module.exports = {
getContentTypes(ctx) { async getContentTypes(ctx) {
const { kind } = ctx.query;
try {
await validateKind(kind);
} catch (error) {
return ctx.send({ error }, 400);
}
const contentTypeService = const contentTypeService =
strapi.plugins['content-type-builder'].services.contenttypes; strapi.plugins['content-type-builder'].services.contenttypes;
@ -17,6 +26,13 @@ module.exports = {
if (uid.startsWith('strapi::')) return false; if (uid.startsWith('strapi::')) return false;
if (uid === 'plugins::upload.file') return false; // TODO: add a flag in the content type instead if (uid === 'plugins::upload.file') return false; // TODO: add a flag in the content type instead
if (
kind &&
_.get(strapi.contentTypes[uid], 'kind', 'collectionType') !== kind
) {
return false;
}
return true; return true;
}) })
.map(uid => .map(uid =>

View File

@ -6,6 +6,7 @@ const yup = require('yup');
const { isValidCategoryName, isValidIcon } = require('./common'); const { isValidCategoryName, isValidIcon } = require('./common');
const formatYupErrors = require('./yup-formatter'); const formatYupErrors = require('./yup-formatter');
const createSchema = require('./model-schema'); const createSchema = require('./model-schema');
const removeEmptyDefaults = require('./remove-empty-defaults');
const { modelTypes, DEFAULT_TYPES } = require('./constants'); const { modelTypes, DEFAULT_TYPES } = require('./constants');
const VALID_RELATIONS = ['oneWay', 'manyWay']; const VALID_RELATIONS = ['oneWay', 'manyWay'];
@ -63,26 +64,7 @@ const validateComponentInput = data => {
}; };
const validateUpdateComponentInput = data => { const validateUpdateComponentInput = data => {
// convert zero length string on default attributes to undefined removeEmptyDefaults(data);
if (_.has(data, ['component', 'attributes'])) {
Object.keys(data.component.attributes).forEach(attribute => {
if (data.component.attributes[attribute].default === '') {
data.component.attributes[attribute].default = undefined;
}
});
}
if (_.has(data, 'components') && Array.isArray(data.components)) {
data.components.forEach(data => {
if (_.has(data, 'attributes') && _.has(data, 'uid')) {
Object.keys(data.attributes).forEach(attribute => {
if (data.attributes[attribute].default === '') {
data.attributes[attribute].default = undefined;
}
});
}
});
}
return yup return yup
.object({ .object({

View File

@ -3,6 +3,9 @@
const CONTENT_TYPE = 'CONTENT_TYPE'; const CONTENT_TYPE = 'CONTENT_TYPE';
const COMPONENT = 'COMPONENT'; const COMPONENT = 'COMPONENT';
const SINGLE_TYPE = 'singleType';
const COLLECTION_TYPE = 'collectionType';
const DEFAULT_TYPES = [ const DEFAULT_TYPES = [
// advanced types // advanced types
'media', 'media',
@ -30,6 +33,10 @@ const FORBIDDEN_ATTRIBUTE_NAMES = ['__component', '__contentType'];
module.exports = { module.exports = {
DEFAULT_TYPES, DEFAULT_TYPES,
typeKinds: {
SINGLE_TYPE,
COLLECTION_TYPE,
},
modelTypes: { modelTypes: {
CONTENT_TYPE, CONTENT_TYPE,
COMPONENT, COMPONENT,

View File

@ -2,35 +2,61 @@
const _ = require('lodash'); const _ = require('lodash');
const yup = require('yup'); const yup = require('yup');
const formatYupErrors = require('./yup-formatter'); const formatYupErrors = require('./yup-formatter');
const createSchema = require('./model-schema'); const createSchema = require('./model-schema');
const removeEmptyDefaults = require('./remove-empty-defaults');
const { nestedComponentSchema } = require('./component'); const { nestedComponentSchema } = require('./component');
const { modelTypes, DEFAULT_TYPES } = require('./constants'); const { modelTypes, DEFAULT_TYPES, typeKinds } = require('./constants');
const VALID_RELATIONS = [ /**
'oneWay', * Allowed relation per type kind
'manyWay', */
'oneToOne', const VALID_RELATIONS = {
'oneToMany', [typeKinds.SINGLE_TYPE]: ['oneWay', 'manyWay'],
'manyToOne', [typeKinds.COLLECTION_TYPE]: [
'manyToMany', 'oneWay',
]; 'manyWay',
'oneToOne',
'oneToMany',
'manyToOne',
'manyToMany',
],
};
/**
* Allowed types
*/
const VALID_TYPES = [...DEFAULT_TYPES, 'component', 'dynamiczone']; const VALID_TYPES = [...DEFAULT_TYPES, 'component', 'dynamiczone'];
const contentTypeSchema = createSchema(VALID_TYPES, VALID_RELATIONS, { /**
modelType: modelTypes.CONTENT_TYPE, * Returns a yup schema to validate a content type payload
}); * @param {Object} data payload
*/
const createContentTypeSchema = data => {
const kind = _.get(data, 'kind', typeKinds.COLLECTION_TYPE);
const createContentTypeSchema = yup const contentTypeSchema = createSchema(
.object({ VALID_TYPES,
contentType: contentTypeSchema.required().noUnknown(), VALID_RELATIONS[kind] || [],
components: nestedComponentSchema, {
}) modelType: modelTypes.CONTENT_TYPE,
.noUnknown(); }
);
return yup
.object({
contentType: contentTypeSchema.required().noUnknown(),
components: nestedComponentSchema,
})
.noUnknown();
};
/**
* Validator for content type creation
*/
const validateContentTypeInput = data => { const validateContentTypeInput = data => {
return createContentTypeSchema return createContentTypeSchema(data)
.validate(data, { .validate(data, {
strict: true, strict: true,
abortEarly: false, abortEarly: false,
@ -38,29 +64,13 @@ const validateContentTypeInput = data => {
.catch(error => Promise.reject(formatYupErrors(error))); .catch(error => Promise.reject(formatYupErrors(error)));
}; };
/**
* Validator for content type edition
*/
const validateUpdateContentTypeInput = data => { const validateUpdateContentTypeInput = data => {
// convert zero length string on default attributes to undefined removeEmptyDefaults(data);
if (_.has(data, 'attributes')) {
Object.keys(data.attributes).forEach(attribute => {
if (data.attributes[attribute].default === '') {
data.attributes[attribute].default = undefined;
}
});
}
if (_.has(data, 'components') && Array.isArray(data.components)) { return createContentTypeSchema(data)
data.components.forEach(data => {
if (_.has(data, 'attributes') && _.has(data, 'uid')) {
Object.keys(data.attributes).forEach(attribute => {
if (data.attributes[attribute].default === '') {
data.attributes[attribute].default = undefined;
}
});
}
});
}
return createContentTypeSchema
.validate(data, { .validate(data, {
strict: true, strict: true,
abortEarly: false, abortEarly: false,
@ -68,7 +78,20 @@ const validateUpdateContentTypeInput = data => {
.catch(error => Promise.reject(formatYupErrors(error))); .catch(error => Promise.reject(formatYupErrors(error)));
}; };
/**
* Validates type kind
*/
const validateKind = kind => {
return yup
.string()
.oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE])
.nullable()
.validate(kind)
.catch(error => Promise.reject(formatYupErrors(error)));
};
module.exports = { module.exports = {
validateContentTypeInput, validateContentTypeInput,
validateUpdateContentTypeInput, validateUpdateContentTypeInput,
validateKind,
}; };

View File

@ -3,72 +3,88 @@
const _ = require('lodash'); const _ = require('lodash');
const yup = require('yup'); const yup = require('yup');
const { FORBIDDEN_ATTRIBUTE_NAMES } = require('./constants'); const {
modelTypes,
FORBIDDEN_ATTRIBUTE_NAMES,
typeKinds,
} = require('./constants');
const { isValidCollectionName, isValidKey } = require('./common'); const { isValidCollectionName, isValidKey } = require('./common');
const { getTypeShape } = require('./types'); const { getTypeShape } = require('./types');
const getRelationValidator = require('./relations'); const getRelationValidator = require('./relations');
const createSchema = (types, relations, { modelType } = {}) => const createSchema = (types, relations, { modelType } = {}) => {
yup const schema = yup.object({
.object({ name: yup
name: yup .string()
.string() .min(1)
.min(1) .required('name.required'),
.required('name.required'), description: yup.string(),
description: yup.string(), connection: yup.string(),
connection: yup.string(), collectionName: yup
collectionName: yup .string()
.string() .nullable()
.nullable() .test(isValidCollectionName),
.test(isValidCollectionName), attributes: yup.lazy(attributes => {
attributes: yup.lazy(attributes => { return yup
return yup .object()
.object() .shape(
.shape( _.mapValues(attributes, (attribute, key) => {
_.mapValues(attributes, (attribute, key) => { if (FORBIDDEN_ATTRIBUTE_NAMES.includes(key)) {
if (FORBIDDEN_ATTRIBUTE_NAMES.includes(key)) {
return yup.object().test({
name: 'forbiddenKeys',
message: `Attribute keys cannot be one of ${FORBIDDEN_ATTRIBUTE_NAMES.join(
', '
)}`,
test: () => false,
});
}
if (_.has(attribute, 'type')) {
const shape = {
type: yup
.string()
.oneOf(types)
.required(),
configurable: yup.boolean().nullable(),
private: yup.boolean().nullable(),
...getTypeShape(attribute, { modelType }),
};
return yup
.object(shape)
.test(isValidKey(key))
.noUnknown();
} else if (_.has(attribute, 'target')) {
const shape = getRelationValidator(attribute, relations);
return yup
.object(shape)
.test(isValidKey(key))
.noUnknown();
}
return yup.object().test({ return yup.object().test({
name: 'mustHaveTypeOrTarget', name: 'forbiddenKeys',
message: 'Attribute must have either a type or a target', message: `Attribute keys cannot be one of ${FORBIDDEN_ATTRIBUTE_NAMES.join(
', '
)}`,
test: () => false, test: () => false,
}); });
}) }
)
.required('attributes.required'); if (_.has(attribute, 'type')) {
}), const shape = {
}) type: yup
.noUnknown(); .string()
.oneOf(types)
.required(),
configurable: yup.boolean().nullable(),
private: yup.boolean().nullable(),
...getTypeShape(attribute, { modelType }),
};
return yup
.object(shape)
.test(isValidKey(key))
.noUnknown();
} else if (_.has(attribute, 'target')) {
const shape = getRelationValidator(attribute, relations);
return yup
.object(shape)
.test(isValidKey(key))
.noUnknown();
}
return yup.object().test({
name: 'mustHaveTypeOrTarget',
message: 'Attribute must have either a type or a target',
test: () => false,
});
})
)
.required('attributes.required');
}),
});
if (modelType === modelTypes.CONTENT_TYPE) {
return schema
.shape({
kind: yup
.string()
.oneOf([typeKinds.SINGLE_TYPE, typeKinds.COLLECTION_TYPE])
.nullable(),
})
.noUnknown();
}
return schema.noUnknown();
};
module.exports = createSchema; module.exports = createSchema;

View File

@ -2,14 +2,14 @@
const yup = require('yup'); const yup = require('yup');
const { validators, isValidName } = require('./common'); const { validators, isValidName } = require('./common');
const { typeKinds } = require('./constants');
const REVERSE_RELATIONS = ['oneToOne', 'oneToMany', 'manyToOne', 'manyToMany']; const REVERSE_RELATIONS = ['oneToOne', 'oneToMany', 'manyToOne', 'manyToMany'];
module.exports = (obj, validNatures) => { module.exports = (obj, validNatures) => {
const contentTypesUIDs = Object.keys(strapi.contentTypes).concat([ const contentTypesUIDs = Object.keys(strapi.contentTypes)
'__self__', .filter(key => strapi.contentTypes[key].kind === typeKinds.COLLECTION_TYPE)
'__contentType__', .concat(['__self__', '__contentType__']);
]);
return { return {
target: yup target: yup

View File

@ -0,0 +1,30 @@
'use strict';
const _ = require('lodash');
/**
* Convert zero length string on default attributes to undefined
*/
module.exports = data => {
if (_.has(data, 'attributes')) {
Object.keys(data.attributes).forEach(attribute => {
if (data.attributes[attribute].default === '') {
data.attributes[attribute].default = undefined;
}
});
}
if (_.has(data, 'components') && Array.isArray(data.components)) {
data.components.forEach(data => {
if (_.has(data, 'attributes') && _.has(data, 'uid')) {
Object.keys(data.attributes).forEach(attribute => {
if (data.attributes[attribute].default === '') {
data.attributes[attribute].default = undefined;
}
});
}
});
}
return data;
};

View File

@ -17,7 +17,7 @@ const { nameToSlug } = require('../utils/helpers');
* @param {Object} contentType * @param {Object} contentType
*/ */
const formatContentType = contentType => { const formatContentType = contentType => {
const { uid, plugin, connection, collectionName, info } = contentType; const { uid, kind, plugin, connection, collectionName, info } = contentType;
return { return {
uid, uid,
@ -26,6 +26,7 @@ const formatContentType = contentType => {
name: _.get(info, 'name') || _.upperFirst(pluralize(uid)), name: _.get(info, 'name') || _.upperFirst(pluralize(uid)),
description: _.get(info, 'description', ''), description: _.get(info, 'description', ''),
connection, connection,
kind: kind || 'collectionType',
collectionName, collectionName,
attributes: formatAttributes(contentType), attributes: formatAttributes(contentType),
}, },

View File

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Content types service format ContentType Returns consistent schemas 1`] = `
Object {
"plugin": "some-plugin",
"schema": Object {
"attributes": Object {
"title": Object {
"type": "string",
},
},
"collectionName": "tests",
"connection": "default",
"description": "My description",
"kind": "singleType",
"name": "My name",
},
"uid": "test-uid",
}
`;

View File

@ -0,0 +1,54 @@
const { formatContentType } = require('../ContentTypes');
describe('Content types service', () => {
describe('format ContentType', () => {
const contentType = {
uid: 'test-uid',
kind: 'singleType',
plugin: 'some-plugin',
connection: 'default',
collectionName: 'tests',
info: {
name: 'My name',
description: 'My description',
},
attributes: {
title: {
type: 'string',
},
},
};
it('Returns consistent schemas', () => {
expect(formatContentType(contentType)).toMatchSnapshot();
});
it('Sets default kind', () => {
expect(
formatContentType({
...contentType,
kind: undefined,
})
).toMatchObject({
schema: {
kind: 'collectionType',
},
});
});
it('Generates a default name', () => {
expect(
formatContentType({
...contentType,
info: {
name: undefined,
},
})
).toMatchObject({
schema: {
name: 'Test-uids',
},
});
});
});
});

View File

@ -6,6 +6,7 @@ const pluralize = require('pluralize');
const { isRelation, toUID, isConfigurable } = require('../../utils/attributes'); const { isRelation, toUID, isConfigurable } = require('../../utils/attributes');
const { nameToSlug, nameToCollectionName } = require('../../utils/helpers'); const { nameToSlug, nameToCollectionName } = require('../../utils/helpers');
const { typeKinds } = require('../../controllers/validation/constants');
const createSchemaHandler = require('./schema-handler'); const createSchemaHandler = require('./schema-handler');
module.exports = function createComponentBuilder() { module.exports = function createComponentBuilder() {
@ -80,6 +81,7 @@ module.exports = function createComponentBuilder() {
contentType contentType
.setUID(uid) .setUID(uid)
.set('connection', infos.connection || defaultConnection) .set('connection', infos.connection || defaultConnection)
.set('kind', infos.kind || typeKinds.COLLECTION_TYPE)
.set('collectionName', infos.collectionName || defaultCollectionName) .set('collectionName', infos.collectionName || defaultCollectionName)
.set(['info', 'name'], infos.name) .set(['info', 'name'], infos.name)
.set(['info', 'description'], infos.description) .set(['info', 'description'], infos.description)
@ -197,9 +199,12 @@ module.exports = function createComponentBuilder() {
} }
}); });
// TODO: handle kind change => update routes.json file somehow
contentType contentType
.set('connection', infos.connection) .set('connection', infos.connection)
.set('collectionName', infos.collectionName) .set('collectionName', infos.collectionName)
.set('kind', infos.kind || contentType.schema.kind)
.set(['info', 'name'], infos.name) .set(['info', 'name'], infos.name)
.set(['info', 'description'], infos.description) .set(['info', 'description'], infos.description)
.setAttributes(this.convertAttributes(newAttributes)); .setAttributes(this.convertAttributes(newAttributes));
@ -220,7 +225,6 @@ module.exports = function createComponentBuilder() {
ct.removeContentType(uid); ct.removeContentType(uid);
}); });
// TODO: clear api when a contentType is deleted
return this.contentTypes.get(uid).delete(); return this.contentTypes.get(uid).delete();
}, },
}; };

View File

@ -0,0 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Content Type Builder - Content types Collection Types Get collection type returns full schema and informations 1`] = `
Object {
"data": Object {
"schema": Object {
"attributes": Object {
"title": Object {
"type": "string",
},
},
"collectionName": "test_collection_types",
"connection": "default",
"description": "",
"kind": "collectionType",
"name": "Test Collection Type",
},
"uid": "application::test-collection-type.test-collection-type",
},
}
`;
exports[`Content Type Builder - Content types Single Types Get single type returns full schema and informations 1`] = `
Object {
"data": Object {
"schema": Object {
"attributes": Object {
"title": Object {
"type": "string",
},
},
"collectionName": "test_single_types",
"connection": "default",
"description": "",
"kind": "singleType",
"name": "Test Single Type",
},
"uid": "application::test-single-type.test-single-type",
},
}
`;

View File

@ -4,7 +4,7 @@ const waitRestart = require('../../../test/helpers/waitRestart');
let rq; let rq;
describe.only('Content Type Builder - Components', () => { describe('Content Type Builder - Components', () => {
beforeAll(async () => { beforeAll(async () => {
const token = await registerAndLogin(); const token = await registerAndLogin();
rq = createAuthRequest(token); rq = createAuthRequest(token);

View File

@ -0,0 +1,97 @@
/**
* Integration test for the content-type-buidler content types managment apis
*/
'use strict';
const { registerAndLogin } = require('../../../test/helpers/auth');
const { createAuthRequest } = require('../../../test/helpers/request');
const waitRestart = require('../../../test/helpers/waitRestart');
let rq;
describe('Content Type Builder - Content types', () => {
beforeAll(async () => {
const token = await registerAndLogin();
rq = createAuthRequest(token);
}, 60000);
afterEach(() => waitRestart());
describe('Collection Types', () => {
const collectionTypeUID =
'application::test-collection-type.test-collection-type';
test('Successfull creation of a collection type', async () => {
const res = await rq({
method: 'POST',
url: '/content-type-builder/content-types',
body: {
contentType: {
name: 'Test Collection Type',
attributes: {
title: {
type: 'string',
},
},
},
},
});
expect(res.statusCode).toBe(201);
expect(res.body).toEqual({
data: {
uid: collectionTypeUID,
},
});
});
test('Get collection type returns full schema and informations', async () => {
const res = await rq({
method: 'GET',
url: `/content-type-builder/content-types/${collectionTypeUID}`,
});
expect(res.statusCode).toBe(200);
expect(res.body).toMatchSnapshot();
});
});
describe('Single Types', () => {
const singleTypeUID = 'application::test-single-type.test-single-type';
test('Successfull creation of a single type', async () => {
const res = await rq({
method: 'POST',
url: '/content-type-builder/content-types',
body: {
contentType: {
kind: 'singleType',
name: 'Test Single Type',
attributes: {
title: {
type: 'string',
},
},
},
},
});
expect(res.statusCode).toBe(201);
expect(res.body).toEqual({
data: {
uid: singleTypeUID,
},
});
});
test('Get single type returns full schema and informations', async () => {
const res = await rq({
method: 'GET',
url: `/content-type-builder/content-types/${singleTypeUID}`,
});
expect(res.statusCode).toBe(200);
expect(res.body).toMatchSnapshot();
});
});
});

View File

@ -11,19 +11,6 @@ const _ = require('lodash');
const uuid = require('uuid/v4'); const uuid = require('uuid/v4');
module.exports = async () => { module.exports = async () => {
if (!_.get(strapi.plugins['users-permissions'], 'config.jwtSecret')) {
const jwtSecret = uuid();
_.set(strapi.plugins['users-permissions'], 'config.jwtSecret', jwtSecret);
strapi.reload.isWatching = false;
await strapi.fs.writePluginFile(
'users-permissions',
'config/jwt.json',
JSON.stringify({ jwtSecret }, null, 2)
);
strapi.reload.isWatching = true;
}
const pluginStore = strapi.store({ const pluginStore = strapi.store({
environment: '', environment: '',
type: 'plugin', type: 'plugin',
@ -173,7 +160,22 @@ module.exports = async () => {
await pluginStore.set({ key: 'advanced', value }); await pluginStore.set({ key: 'advanced', value });
} }
return strapi.plugins[ await strapi.plugins[
'users-permissions' 'users-permissions'
].services.userspermissions.initialize(); ].services.userspermissions.initialize();
if (!_.get(strapi.plugins['users-permissions'], 'config.jwtSecret')) {
const jwtSecret = uuid();
_.set(strapi.plugins['users-permissions'], 'config.jwtSecret', jwtSecret);
strapi.reload.isWatching = false;
await strapi.fs.writePluginFile(
'users-permissions',
'config/jwt.json',
JSON.stringify({ jwtSecret }, null, 2)
);
strapi.reload.isWatching = true;
}
}; };

View File

@ -474,18 +474,17 @@ module.exports = {
} }
// Create two first default roles. // Create two first default roles.
await Promise.all([ await strapi.query('role', 'users-permissions').create({
strapi.query('role', 'users-permissions').create({ name: 'Authenticated',
name: 'Authenticated', description: 'Default role given to authenticated user.',
description: 'Default role given to authenticated user.', type: 'authenticated',
type: 'authenticated', });
}),
strapi.query('role', 'users-permissions').create({ await strapi.query('role', 'users-permissions').create({
name: 'Public', name: 'Public',
description: 'Default role given to unauthenticated user.', description: 'Default role given to unauthenticated user.',
type: 'public', type: 'public',
}), });
]);
return this.updatePermissions(); return this.updatePermissions();
}, },

View File

@ -13,18 +13,9 @@ const chalk = require('chalk');
const CLITable = require('cli-table3'); const CLITable = require('cli-table3');
const utils = require('./utils'); const utils = require('./utils');
const { const loadModules = require('./core/load-modules');
loadConfig, const bootstrap = require('./core/bootstrap');
loadApis, const initCoreStore = require('./core/init-core-store');
loadAdmin,
loadPlugins,
loadMiddlewares,
loadHooks,
bootstrap,
loadExtensions,
initCoreStore,
loadComponents,
} = require('./core');
const initializeMiddlewares = require('./middlewares'); const initializeMiddlewares = require('./middlewares');
const initializeHooks = require('./hooks'); const initializeHooks = require('./hooks');
const createStrapiFs = require('./core/fs'); const createStrapiFs = require('./core/fs');
@ -304,51 +295,16 @@ class Strapi extends EventEmitter {
} }
}); });
const [ const modules = await loadModules(this);
config,
api,
admin,
plugins,
middlewares,
hook,
extensions,
components,
] = await Promise.all([
loadConfig(this),
loadApis(this),
loadAdmin(this),
loadPlugins(this),
loadMiddlewares(this),
loadHooks(this.config),
loadExtensions(this.config),
loadComponents(this),
]);
_.merge(this.config, config); _.merge(this.config, modules.config);
this.api = api; this.api = modules.api;
this.admin = admin; this.admin = modules.admin;
this.components = components; this.components = modules.components;
this.plugins = plugins; this.plugins = modules.plugins;
this.middleware = middlewares; this.middleware = modules.middlewares;
this.hook = hook; this.hook = modules.hook;
/**
* Handle plugin extensions
*/
// merge extensions config folders
_.mergeWith(this.plugins, extensions.merges, (objValue, srcValue, key) => {
// concat routes
if (_.isArray(srcValue) && _.isArray(objValue) && key === 'routes') {
return srcValue.concat(objValue);
}
});
// overwrite plugins with extensions overwrites
extensions.overwrites.forEach(({ path, mod }) => {
_.assign(_.get(this.plugins, path), mod);
});
// Populate AST with configurations.
await bootstrap(this); await bootstrap(this);

View File

@ -5,9 +5,11 @@ const _ = require('lodash');
const { createController, createService } = require('../core-api'); const { createController, createService } = require('../core-api');
const getURLFromSegments = require('../utils/url-from-segments'); const getURLFromSegments = require('../utils/url-from-segments');
const pickSchema = obj => const getKind = obj => obj.kind || 'collectionType';
_.cloneDeep(
_.pick(obj, [ const pickSchema = model => {
const schema = _.cloneDeep(
_.pick(model, [
'connection', 'connection',
'collectionName', 'collectionName',
'info', 'info',
@ -16,6 +18,10 @@ const pickSchema = obj =>
]) ])
); );
schema.kind = getKind(model);
return schema;
};
module.exports = function(strapi) { module.exports = function(strapi) {
// Retrieve Strapi version. // Retrieve Strapi version.
strapi.config.uuid = _.get(strapi.config.info, 'strapi.uuid', ''); strapi.config.uuid = _.get(strapi.config.info, 'strapi.uuid', '');
@ -75,10 +81,12 @@ module.exports = function(strapi) {
Object.assign(model, { Object.assign(model, {
__schema__: pickSchema(model), __schema__: pickSchema(model),
kind: getKind(model),
modelType: 'contentType', modelType: 'contentType',
uid: `application::${apiName}.${modelName}`, uid: `application::${apiName}.${modelName}`,
apiName, apiName,
modelName, modelName,
globalId: model.globalId || _.upperFirst(_.camelCase(modelName)), globalId: model.globalId || _.upperFirst(_.camelCase(modelName)),
collectionName: collectionName:
model.collectionName || `${modelName}`.toLocaleLowerCase(), model.collectionName || `${modelName}`.toLocaleLowerCase(),
@ -157,6 +165,7 @@ module.exports = function(strapi) {
Object.assign(model, { Object.assign(model, {
__schema__: pickSchema(model), __schema__: pickSchema(model),
modelType: 'contentType', modelType: 'contentType',
kind: getKind(model),
uid: `strapi::${key}`, uid: `strapi::${key}`,
plugin: 'admin', plugin: 'admin',
modelName: key, modelName: key,
@ -192,6 +201,7 @@ module.exports = function(strapi) {
Object.assign(model, { Object.assign(model, {
__schema__: pickSchema(model), __schema__: pickSchema(model),
modelType: 'contentType', modelType: 'contentType',
kind: getKind(model),
modelName: key, modelName: key,
uid: `plugins::${pluginName}.${key}`, uid: `plugins::${pluginName}.${key}`,
plugin: pluginName, plugin: pluginName,

View File

@ -1,25 +0,0 @@
'use strict';
const loadConfig = require('./load-config');
const loadApis = require('./load-apis');
const loadAdmin = require('./load-admin');
const loadPlugins = require('./load-plugins');
const loadMiddlewares = require('./load-middlewares');
const loadExtensions = require('./load-extensions');
const loadHooks = require('./load-hooks');
const bootstrap = require('./bootstrap');
const initCoreStore = require('./init-core-store');
const loadComponents = require('./load-components');
module.exports = {
loadConfig,
loadApis,
loadAdmin,
loadPlugins,
loadMiddlewares,
loadHooks,
loadExtensions,
loadComponents,
bootstrap,
initCoreStore,
};

View File

@ -0,0 +1,73 @@
/**
* Load Modules is the root module loader.
* This is where all the strapi enviornment is laoded
* - APIs
* - Plugins
* - Hooks
* - Middlewres
* - Components
* - ContentTypes
*/
'use strict';
const _ = require('lodash');
const loadConfig = require('./load-config');
const loadApis = require('./load-apis');
const loadAdmin = require('./load-admin');
const loadPlugins = require('./load-plugins');
const loadMiddlewares = require('./load-middlewares');
const loadExtensions = require('./load-extensions');
const loadHooks = require('./load-hooks');
const loadComponents = require('./load-components');
module.exports = async strapi => {
const [
config,
api,
admin,
plugins,
middlewares,
hook,
extensions,
components,
] = await Promise.all([
loadConfig(strapi),
loadApis(strapi),
loadAdmin(strapi),
loadPlugins(strapi),
loadMiddlewares(strapi),
loadHooks(strapi.config),
loadExtensions(strapi.config),
loadComponents(strapi),
]);
// TODO: move this into the appropriate loaders
/**
* Handle plugin extensions
*/
// merge extensions config folders
_.mergeWith(plugins, extensions.merges, (objValue, srcValue, key) => {
// concat routes
if (_.isArray(srcValue) && _.isArray(objValue) && key === 'routes') {
return srcValue.concat(objValue);
}
});
// overwrite plugins with extensions overwrites
extensions.overwrites.forEach(({ path, mod }) => {
_.assign(_.get(plugins, path), mod);
});
return {
config,
api,
admin,
plugins,
middlewares,
hook,
extensions,
components,
};
};

View File

@ -1,8 +1,8 @@
'use strict'; 'use strict';
const path = require('path'); const path = require('path');
const glob = require('./glob');
const _ = require('lodash'); const _ = require('lodash');
const glob = require('./glob');
const filePathToPath = require('./filepath-to-prop-path'); const filePathToPath = require('./filepath-to-prop-path');
/** /**