From 3ded0ef2bd3439b42ee9fbfc9e6d5824ea9e828d Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Wed, 7 Jul 2021 18:04:39 +0200 Subject: [PATCH] Init compo & add publication field --- .../core/admin/services/permission/queries.js | 1 + packages/core/database/examples/index.js | 54 +++-- packages/core/database/examples/models.js | 24 +- packages/core/database/lib/entity-manager.js | 61 ++--- .../core/database/lib/entity-repository.js | 7 +- .../core/database/lib/metadata/relations.js | 4 +- packages/core/database/lib/query/helpers.js | 10 +- .../core/database/lib/utils/content-types.js | 6 +- .../core/strapi/lib/core-api/service/index.js | 6 +- .../strapi/lib/services/entity-service.js | 214 +++++++++++------- .../api/basic-dp-compo-repeatable.test.e2e.js | 12 + .../tests/api/basic-dp-compo.test.e2e.js | 12 + .../tests/publication-state.test.e2e.js | 7 +- packages/core/utils/lib/content-types.js | 35 ++- 14 files changed, 304 insertions(+), 149 deletions(-) diff --git a/packages/core/admin/services/permission/queries.js b/packages/core/admin/services/permission/queries.js index d5fafb5c30..92fe1c2a8b 100644 --- a/packages/core/admin/services/permission/queries.js +++ b/packages/core/admin/services/permission/queries.js @@ -26,6 +26,7 @@ const permissionDomain = require('../../domain/permission/index'); * @returns {Promise} */ const deleteByRolesIds = async rolesIds => { + // FIXME: need to delete associations in delete many await strapi.query('strapi::permission').deleteMany({ where: { role: { id: rolesIds }, diff --git a/packages/core/database/examples/index.js b/packages/core/database/examples/index.js index f658fdc526..ec5002f2db 100644 --- a/packages/core/database/examples/index.js +++ b/packages/core/database/examples/index.js @@ -21,26 +21,28 @@ async function main(connection) { await orm.schema.sync(); await orm.schema.reset(); + await orm.query('compo').create({ + data: { + key: 'A', + value: 1, + }, + }); + await orm.query('article').create({ // select: {}, // populate: {}, data: { - compo: { - id: 1, - __pivot: { - order: 1, - field: 'compo', - }, - }, + compo: 1, }, }); - const articles = await orm.query('article').findMany({ - limit: 5, - where: { title: 'Article 001', createdAt: { $null: true } }, + await orm.query('article').findMany({ + populate: ['category.compo'], }); - console.log(articles); + // console.log(await orm.query('article').load(1, 'compo')); + + // await orm.query('article').delete({ where: { id: 1 } }); // await tests(orm); } finally { @@ -391,28 +393,40 @@ const tests = async orm => { // }); await orm.query('article').findMany({ - limit: 5, where: { - compos: { - key: 'xx', + $not: { + $or: [ + { + category: { + title: 'Article 003', + }, + }, + { + title: { + $in: ['Article 001', 'Article 002'], + }, + }, + ], + title: { + $not: 'Article 007', + }, }, }, + orderBy: [{ category: { name: 'asc' } }], + offset: 0, + limit: 10, populate: { category: { - select: ['id', 'title'], - limit: 5, - offset: 2, orderBy: 'title', populate: { - articles: { + categories: { populate: { tags: true, }, }, }, }, - compos: true, + seo: true, }, - orderBy: { compos: { key: 'DESC' } }, }); }; diff --git a/packages/core/database/examples/models.js b/packages/core/database/examples/models.js index 9d670a533b..b49eda7a38 100644 --- a/packages/core/database/examples/models.js +++ b/packages/core/database/examples/models.js @@ -38,24 +38,24 @@ const article = { title: { type: 'string', }, - // category: { - // type: 'relation', - // relation: 'manyToOne', - // target: 'category', - // inversedBy: 'articles', - // // useJoinTable: false, - // }, + category: { + type: 'relation', + relation: 'manyToOne', + target: 'category', + inversedBy: 'articles', + // useJoinTable: false, + }, // tags: { // type: 'relation', // relation: 'manyToMany', // target: 'tag', // inversedBy: 'articles', // }, - compo: { - type: 'component', - component: 'compo', - // repeatable: true, - }, + // compo: { + // type: 'component', + // component: 'compo', + // // repeatable: true, + // }, // cover: { // type: 'media', // single: true, diff --git a/packages/core/database/lib/entity-manager.js b/packages/core/database/lib/entity-manager.js index 969b915742..9ef85281fd 100644 --- a/packages/core/database/lib/entity-manager.js +++ b/packages/core/database/lib/entity-manager.js @@ -55,19 +55,19 @@ const createEntityManager = db => { const repoMap = {}; return { - async findOne(uid, params) { + findOne(uid, params) { const qb = this.createQueryBuilder(uid) .init(params) .first(); - return await qb.execute(); + return qb.execute(); }, // should we name it findOne because people are used to it ? - async findMany(uid, params) { + findMany(uid, params) { const qb = this.createQueryBuilder(uid).init(params); - return await qb.execute(); + return qb.execute(); }, async count(uid, params = {}) { @@ -195,15 +195,14 @@ const createEntityManager = db => { async delete(uid, params = {}) { const { where, select, populate } = params; - const metadata = db.metadata.get(uid); if (_.isEmpty(where)) { throw new Error('Delete requires a where parameter'); } const entity = await this.findOne(uid, { - where, select: select && ['id'].concat(select), + where, populate, }); @@ -235,27 +234,6 @@ const createEntityManager = db => { return { count: deletedRows }; }, - // populate already loaded entry - async populate(uid, entry, name, params) { - return { - ...entry, - relation: await this.load(entry, name, params), - }; - }, - - // loads a relation - load(uid, entry, name, params) { - const { attributes } = db.metadata.get(uid); - - return this.getRepository(attributes[name].target.uid).findMany({ - ...params, - where: { - ...params.where, - // [parent]: entry.id, - }, - }); - }, - /** * Attach relations to a new entity * @@ -497,6 +475,35 @@ const createEntityManager = db => { } }, + // populate already loaded entry + async populate(uid, entry, name, params) { + return { + ...entry, + relation: await this.load(entry, name, params), + }; + }, + + // loads a relation + async load(uid, id, field, params) { + const { attributes } = db.metadata.get(uid); + + const attribute = attributes[field]; + + if (!attribute || attribute.type !== 'relation') { + throw new Error('Invalid load expected a relational attribute'); + } + + const entry = await this.findOne(uid, { + select: ['id'], + where: { id }, + populate: { + [field]: params || true, + }, + }); + + return entry[field]; + }, + // cascading // aggregations // -> avg diff --git a/packages/core/database/lib/entity-repository.js b/packages/core/database/lib/entity-repository.js index 597c98d958..6d9f7eb2bc 100644 --- a/packages/core/database/lib/entity-repository.js +++ b/packages/core/database/lib/entity-repository.js @@ -84,12 +84,13 @@ const createRepository = (uid, db) => { }, attachRelations(id, data) { - console.log(id, data) return db.entityManager.attachRelations(uid, id, data); }, + updateRelations(id, data) { return db.entityManager.updateRelations(uid, id, data); }, + deleteRelations(id) { return db.entityManager.deleteRelations(uid, id); }, @@ -97,7 +98,9 @@ const createRepository = (uid, db) => { // TODO: add relation API populate() {}, - load() {}, + load(id, field, params) { + return db.entityManager.load(uid, id, field, params); + }, }; }; diff --git a/packages/core/database/lib/metadata/relations.js b/packages/core/database/lib/metadata/relations.js index e00cbfef5b..8d14af660e 100644 --- a/packages/core/database/lib/metadata/relations.js +++ b/packages/core/database/lib/metadata/relations.js @@ -193,10 +193,10 @@ const relationFactoryMap = { manyToMany: createManyToMany, }; -const createJoinColum = (metadata, { attribute /*attributeName, meta */ }) => { +const createJoinColum = (metadata, { attribute, attributeName /*meta */ }) => { const targetMeta = metadata.get(attribute.target); - const joinColumnName = _.snakeCase(`${targetMeta.singularName}_id`); + const joinColumnName = _.snakeCase(`${attributeName}_id`); const joinColumn = { name: joinColumnName, referencedColumn: 'id', diff --git a/packages/core/database/lib/query/helpers.js b/packages/core/database/lib/query/helpers.js index a3462a1054..72c51489fe 100644 --- a/packages/core/database/lib/query/helpers.js +++ b/packages/core/database/lib/query/helpers.js @@ -470,7 +470,15 @@ const processPopulate = (populate, ctx) => { if (Array.isArray(populate)) { for (const key of populate) { - populateMap[key] = true; + const [root, ...rest] = key.split('.'); + + if (rest.length > 0) { + populateMap[root] = { + populate: rest, + }; + } else { + populateMap[root] = true; + } } } else { populateMap = populate; diff --git a/packages/core/database/lib/utils/content-types.js b/packages/core/database/lib/utils/content-types.js index d4f45848ce..ecd325ac7d 100644 --- a/packages/core/database/lib/utils/content-types.js +++ b/packages/core/database/lib/utils/content-types.js @@ -20,11 +20,11 @@ const transformAttribute = attribute => { } }; -// TODO: add published_at for D&P // TODO: add locale & localizations for I18N +// TODO: model logic outside DB const transformContentTypes = contentTypes => { return contentTypes.map(contentType => { - return { + const model = { ...contentType, // reuse new model def singularName: contentType.modelName, @@ -44,6 +44,8 @@ const transformContentTypes = contentTypes => { }, {}), }, }; + + return model; }); }; diff --git a/packages/core/strapi/lib/core-api/service/index.js b/packages/core/strapi/lib/core-api/service/index.js index 4c1d6ada77..50f7fcd065 100644 --- a/packages/core/strapi/lib/core-api/service/index.js +++ b/packages/core/strapi/lib/core-api/service/index.js @@ -42,7 +42,7 @@ const getLimitParam = params => { return defaultLimit; } - const limit = _.toNumber(params._limit); + const limit = _.toNumber(params.limit); // if there is max limit set and params._limit exceeds this number, return configured max limit if (maxLimit && (limit === -1 || limit > maxLimit)) { return maxLimit; @@ -58,9 +58,9 @@ const getLimitParam = params => { */ const getFetchParams = (params = {}) => { return { - // _publicationState: DP_PUB_STATE_LIVE, + publicationState: DP_PUB_STATE_LIVE, ...params, - // limit: getLimitParam(params), + limit: getLimitParam(params), }; }; diff --git a/packages/core/strapi/lib/services/entity-service.js b/packages/core/strapi/lib/services/entity-service.js index af2958cf8e..3f9502af76 100644 --- a/packages/core/strapi/lib/services/entity-service.js +++ b/packages/core/strapi/lib/services/entity-service.js @@ -1,5 +1,6 @@ 'use strict'; +const _ = require('lodash'); const { has, pick } = require('lodash/fp'); const delegate = require('delegates'); @@ -43,34 +44,59 @@ module.exports = ctx => { }; // TODO: move to Controller ? -const transformParamsToQuery = (params = {}) => { - const query = {}; +const transformParamsToQuery = (uid, params = {}) => { + const model = strapi.getModel(uid); + + const query = { + populate: [], + }; // TODO: check invalid values add defaults .... - if (params.start) { - query.offset = convertStartQueryParams(params.start); + const { start, limit, sort, filters, fields, populate, publicationState } = params; + + if (start) { + query.offset = convertStartQueryParams(start); } - if (params.limit) { - query.limit = convertLimitQueryParams(params.limit); + if (limit) { + query.limit = convertLimitQueryParams(limit); } - if (params.sort) { - query.orderBy = convertSortQueryParams(params.sort); + if (sort) { + query.orderBy = convertSortQueryParams(sort); } - if (params.filters) { - query.where = params.filters; + if (filters) { + query.where = filters; } - if (params.fields) { - query.select = params.fields; + if (fields) { + query.select = _.castArray(fields); } - if (params.populate) { + if (populate) { const { populate } = params; - query.populate = populate; + query.populate = _.castArray(populate); + } + + // TODO: move to layer above ? + if (publicationState && contentTypesUtils.hasDraftAndPublish(model)) { + const { publicationState = 'live' } = params; + + const liveClause = { + published_at: { + $notNull: true, + }, + }; + + if (publicationState === 'live') { + query.where = { + $and: [liveClause, query.where || {}], + }; + + // TODO: propagate nested publicationState filter somehow + } } return query; @@ -85,12 +111,21 @@ const createDefaultImplementation = ({ db, eventHub, entityValidator }) => ({ return options; }, + emitEvent(uid, event, entity) { + const model = strapi.getModel(uid); + + eventHub.emit(event, { + model: model.modelName, + entry: sanitizeEntity(entity, { model }), + }); + }, + async find(uid, opts) { const { kind } = strapi.getModel(uid); const { params } = await this.wrapOptions(opts, { uid, action: 'find' }); - const query = transformParamsToQuery(params); + const query = transformParamsToQuery(uid, params); // return first element and ignore filters if (kind === 'singleType') { @@ -103,26 +138,21 @@ const createDefaultImplementation = ({ db, eventHub, entityValidator }) => ({ async findPage(uid, opts) { const { params } = await this.wrapOptions(opts, { uid, action: 'findPage' }); - // TODO: transform page pageSize - const query = transformParamsToQuery(params); + const query = transformParamsToQuery(uid, params); return db.query(uid).findPage(query); }, async findWithRelationCounts(uid, opts) { - const { params, populate } = await this.wrapOptions(opts, { - uid, - action: 'findWithRelationCounts', - }); + const { params } = await this.wrapOptions(opts, { uid, action: 'findWithRelationCounts' }); - // TODO: to impl - return db.query(uid).findWithRelationCounts(params, populate); + return db.query(uid).findWithRelationCounts(params); }, async findOne(uid, entityId, opts) { const { params } = await this.wrapOptions(opts, { uid, action: 'findOne' }); - const query = transformParamsToQuery(pickSelectionParams(params)); + const query = transformParamsToQuery(uid, pickSelectionParams(params)); return db.query(uid).findOne({ ...query, where: { id: entityId } }); }, @@ -130,7 +160,7 @@ const createDefaultImplementation = ({ db, eventHub, entityValidator }) => ({ async count(uid, opts) { const { params } = await this.wrapOptions(opts, { uid, action: 'count' }); - const query = transformParamsToQuery(params); + const query = transformParamsToQuery(uid, params); return db.query(uid).count(query); }, @@ -138,36 +168,27 @@ const createDefaultImplementation = ({ db, eventHub, entityValidator }) => ({ async create(uid, opts) { const { params, data, files } = await this.wrapOptions(opts, { uid, action: 'create' }); - const modelDef = strapi.getModel(uid); - - const isDraft = contentTypesUtils.isDraft(data, modelDef); - - const validData = await entityValidator.validateEntityCreation(modelDef, data, { isDraft }); + const model = strapi.getModel(uid); + const isDraft = contentTypesUtils.isDraft(data, model); + const validData = await entityValidator.validateEntityCreation(model, data, { isDraft }); // select / populate - const query = transformParamsToQuery(pickSelectionParams(params)); + const query = transformParamsToQuery(uid, pickSelectionParams(params)); // TODO: wrap into transaction - const componentData = await createComponents(uid, validData); - const entity = await db.query(uid).create({ ...query, data: Object.assign(validData, componentData), }); - // TODO: Implement components CRUD - // TODO: implement files outside of the entity service // if (files && Object.keys(files).length > 0) { // await this.uploadFiles(entry, files, { model }); // entry = await this.findOne({ params: { id: entry.id } }, { model }); // } - eventHub.emit(ENTRY_CREATE, { - model: modelDef.modelName, - entry: sanitizeEntity(entity, { model: modelDef }), - }); + this.emitEvent(uid, ENTRY_CREATE, entity); return entity; }, @@ -175,72 +196,71 @@ const createDefaultImplementation = ({ db, eventHub, entityValidator }) => ({ async update(uid, entityId, opts) { const { params, data, files } = await this.wrapOptions(opts, { uid, action: 'update' }); - const modelDef = strapi.getModel(uid); + const model = strapi.getModel(uid); - // const existingEntry = await db.query(uid).findOne({ where: { id: entityId } }); + const existingEntry = await db.query(uid).findOne({ where: { id: entityId } }); - // const isDraft = contentTypesUtils.isDraft(existingEntry, modelDef); + const isDraft = contentTypesUtils.isDraft(existingEntry, model); - // TODO: validate - // // const validData = await entityValidator.validateEntityUpdate(modelDef, data, { - // // isDraft, - // // }); - - // select / populate - const query = transformParamsToQuery(pickSelectionParams(params)); - - // TODO: wrap in transaction - - const componentData = await updateComponents(uid, data); - - let entry = await db.query(uid).update({ - ...query, - where: { id: entityId }, - data: Object.assign(data, componentData), + const validData = await entityValidator.validateEntityUpdate(model, data, { + isDraft, }); - // TODO: implement files + const query = transformParamsToQuery(uid, pickSelectionParams(params)); + + // TODO: wrap in transaction + const componentData = await updateComponents(uid, entityId, validData); + + const entity = await db.query(uid).update({ + ...query, + where: { id: entityId }, + data: Object.assign(validData, componentData), + }); + + // TODO: implement files outside of the entity service // if (files && Object.keys(files).length > 0) { // await this.uploadFiles(entry, files, { model }); // entry = await this.findOne({ params: { id: entry.id } }, { model }); // } - eventHub.emit(ENTRY_UPDATE, { - model: modelDef.modelName, - entry: sanitizeEntity(entry, { model: modelDef }), - }); + this.emitEvent(uid, ENTRY_UPDATE, entity); - return entry; + return entity; }, async delete(uid, entityId, opts) { const { params } = await this.wrapOptions(opts, { uid, action: 'delete' }); // select / populate - const query = transformParamsToQuery(pickSelectionParams(params)); + const query = transformParamsToQuery(uid, pickSelectionParams(params)); - const entry = await db.query(uid).delete({ ...query, where: { id: entityId } }); - - const modelDef = strapi.getModel(uid); - eventHub.emit(ENTRY_DELETE, { - model: modelDef.modelName, - entry: sanitizeEntity(entry, { model: modelDef }), + const entity = await db.query(uid).findOne({ + ...query, + where: { id: entityId }, }); - return entry; + if (!entity) { + throw new Error('Entity not found'); + } + + await deleteComponents(uid, entityId); + await db.query(uid).delete({ where: { id: entity.id } }); + + this.emitEvent(uid, ENTRY_DELETE, entity); + + return entity; }, async deleteMany(uid, opts) { const { params } = await this.wrapOptions(opts, { uid, action: 'delete' }); // select / populate - const query = transformParamsToQuery(pickSelectionParams(params)); + const query = transformParamsToQuery(uid, pickSelectionParams(params)); return db.query(uid).deleteMany(query); }, // TODO: Implement search features - async search(uid, opts) { const { params, populate } = await this.wrapOptions(opts, { uid, action: 'search' }); @@ -350,6 +370,7 @@ const createComponents = async (uid, data) => { const updateOrCreateComponent = (componentUID, value) => { // update if (has('id', value)) { + // TODO: verify the compo is associated with the entity return strapi.query(componentUID).update({ where: { id: value.id }, data: value }); } @@ -357,9 +378,8 @@ const updateOrCreateComponent = (componentUID, value) => { return strapi.query(componentUID).create({ data: value }); }; -const updateComponents = async (uid, data) => { - // TODO: clear old -> done in the updateRelation - +// TODO: delete old components +const updateComponents = async (uid, entityId, data) => { const { attributes } = strapi.getModel(uid); for (const attributeName in attributes) { @@ -372,8 +392,12 @@ const updateComponents = async (uid, data) => { if (attribute.type === 'component') { const { component: componentUID, repeatable = false } = attribute; + const previousValue = await strapi.query(uid).load(entityId, attributeName); const componentValue = data[attributeName]; + // TODO: diff prev & new + + // make diff between prev ids & data ids if (componentValue === null) { continue; } @@ -423,3 +447,39 @@ const updateComponents = async (uid, data) => { } } }; + +const deleteComponents = async (uid, entityId) => { + const { attributes } = strapi.getModel(uid); + + // TODO: find components and then delete them + for (const attributeName in attributes) { + const attribute = attributes[attributeName]; + + if (attribute.type === 'component') { + const { component: componentUID } = attribute; + + // TODO: need to load before deleting the entry then delete the components then the entry + const value = await strapi.query(uid).load(entityId, attributeName); + + if (!value) { + continue; + } + + if (Array.isArray(value)) { + await Promise.all( + value.map(subValue => { + return strapi.query(componentUID).delete({ where: { id: subValue.id } }); + }) + ); + } else { + await strapi.query(componentUID).delete({ where: { id: value.id } }); + } + + continue; + } + + if (attribute.type === 'dynamiczone') { + continue; + } + } +}; diff --git a/packages/core/strapi/tests/api/basic-dp-compo-repeatable.test.e2e.js b/packages/core/strapi/tests/api/basic-dp-compo-repeatable.test.e2e.js index 7608cf0bbd..51334a1221 100644 --- a/packages/core/strapi/tests/api/basic-dp-compo-repeatable.test.e2e.js +++ b/packages/core/strapi/tests/api/basic-dp-compo-repeatable.test.e2e.js @@ -80,6 +80,9 @@ describe('Core API - Basic + compo + draftAndPublish', () => { method: 'POST', url: '/product-with-compo-and-dps', body: product, + qs: { + populate: ['compo'], + }, }); expect(res.statusCode).toBe(200); @@ -92,6 +95,9 @@ describe('Core API - Basic + compo + draftAndPublish', () => { const res = await rq({ method: 'GET', url: '/product-with-compo-and-dps', + qs: { + populate: ['compo'], + }, }); expect(res.statusCode).toBe(200); @@ -118,6 +124,9 @@ describe('Core API - Basic + compo + draftAndPublish', () => { method: 'PUT', url: `/product-with-compo-and-dps/${data.productsWithCompoAndDP[0].id}`, body: product, + qs: { + populate: ['compo'], + }, }); expect(res.statusCode).toBe(200); @@ -131,6 +140,9 @@ describe('Core API - Basic + compo + draftAndPublish', () => { const res = await rq({ method: 'DELETE', url: `/product-with-compo-and-dps/${data.productsWithCompoAndDP[0].id}`, + qs: { + populate: ['compo'], + }, }); expect(res.statusCode).toBe(200); diff --git a/packages/core/strapi/tests/api/basic-dp-compo.test.e2e.js b/packages/core/strapi/tests/api/basic-dp-compo.test.e2e.js index 337832c52e..bfe7f1f89f 100644 --- a/packages/core/strapi/tests/api/basic-dp-compo.test.e2e.js +++ b/packages/core/strapi/tests/api/basic-dp-compo.test.e2e.js @@ -77,6 +77,9 @@ describe('Core API - Basic + compo + draftAndPublish', () => { method: 'POST', url: '/product-with-compo-and-dps', body: product, + qs: { + populate: ['compo'], + }, }); expect(res.statusCode).toBe(200); @@ -89,6 +92,9 @@ describe('Core API - Basic + compo + draftAndPublish', () => { const res = await rq({ method: 'GET', url: '/product-with-compo-and-dps', + qs: { + populate: ['compo'], + }, }); expect(res.statusCode).toBe(200); @@ -113,6 +119,9 @@ describe('Core API - Basic + compo + draftAndPublish', () => { method: 'PUT', url: `/product-with-compo-and-dps/${data.productsWithCompoAndDP[0].id}`, body: product, + qs: { + populate: ['compo'], + }, }); expect(res.statusCode).toBe(200); @@ -126,6 +135,9 @@ describe('Core API - Basic + compo + draftAndPublish', () => { const res = await rq({ method: 'DELETE', url: `/product-with-compo-and-dps/${data.productsWithCompoAndDP[0].id}`, + qs: { + populate: ['compo'], + }, }); expect(res.statusCode).toBe(200); diff --git a/packages/core/strapi/tests/publication-state.test.e2e.js b/packages/core/strapi/tests/publication-state.test.e2e.js index 8f86d14e77..34eaab4f45 100644 --- a/packages/core/strapi/tests/publication-state.test.e2e.js +++ b/packages/core/strapi/tests/publication-state.test.e2e.js @@ -122,7 +122,7 @@ const lengthFor = (name, { mode = 'live' } = {}) => { const getQueryFromMode = mode => { if (['live', 'preview'].includes(mode)) { - return `?_publicationState=${mode}`; + return `?publicationState=${mode}`; } return ''; @@ -188,7 +188,10 @@ describe('Publication State', () => { beforeEach(async () => { const res = await rq({ method: 'GET', - url: `/${pluralizedModelName}?_publicationState=live`, + url: `/${pluralizedModelName}?publicationState=live`, + qs: { + populate: ['categories', 'comp.countries'], + }, }); products = res.body; }); diff --git a/packages/core/utils/lib/content-types.js b/packages/core/utils/lib/content-types.js index d3976e118d..b3e9918886 100644 --- a/packages/core/utils/lib/content-types.js +++ b/packages/core/utils/lib/content-types.js @@ -168,9 +168,42 @@ const createContentType = (model, { modelName }, { apiName, pluginName } = {}) = get() { // FIXME: to fix // return strapi.getModel(model.uid).privateAttributes; - return [] + return []; }, }); + + if (hasDraftAndPublish(model)) { + model.attributes[PUBLISHED_AT_ATTRIBUTE] = { + type: 'datetime', + configurable: false, + writable: true, + visible: false, + }; + } + + const isPrivate = !_.get(model, 'options.populateCreatorFields', false); + + model.attributes[CREATED_BY_ATTRIBUTE] = { + type: 'relation', + relation: 'oneToOne', + target: 'strapi::user', + configurable: false, + writable: false, + visible: false, + useJoinTable: false, + private: isPrivate, + }; + + model.attributes[UPDATED_BY_ATTRIBUTE] = { + type: 'relation', + relation: 'oneToOne', + target: 'strapi::user', + configurable: false, + writable: false, + visible: false, + useJoinTable: false, + private: isPrivate, + }; }; const getGlobalId = (model, modelName, prefix) => {