mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 15:13:21 +00:00
add getNumberOfDraftRelations route
This commit is contained in:
parent
541f4b6a40
commit
0415f5d677
@ -239,4 +239,28 @@ module.exports = {
|
||||
|
||||
ctx.body = { count };
|
||||
},
|
||||
|
||||
async getNumberOfDraftRelations(ctx) {
|
||||
const { userAbility } = ctx.state;
|
||||
const { model, id } = ctx.params;
|
||||
|
||||
const entityManager = getService('entity-manager');
|
||||
const permissionChecker = getService('permission-checker').create({ userAbility, model });
|
||||
|
||||
if (permissionChecker.cannot.read()) {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
|
||||
const entity = await entityManager.findOneWithCreatorRolesAndCount(id, model);
|
||||
|
||||
if (!entity) {
|
||||
return ctx.notFound();
|
||||
}
|
||||
|
||||
if (permissionChecker.cannot.read(entity)) {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
|
||||
return entityManager.getNumberOfDraftRelations(id, model);
|
||||
},
|
||||
};
|
||||
|
||||
@ -174,4 +174,28 @@ module.exports = {
|
||||
|
||||
ctx.body = await permissionChecker.sanitizeOutput(unpublishedEntity);
|
||||
},
|
||||
|
||||
async getNumberOfDraftRelations(ctx) {
|
||||
const { userAbility } = ctx.state;
|
||||
const { model, id } = ctx.params;
|
||||
|
||||
const entityManager = getService('entity-manager');
|
||||
const permissionChecker = getService('permission-checker').create({ userAbility, model });
|
||||
|
||||
if (permissionChecker.cannot.read()) {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
|
||||
const entity = await entityManager.findOneWithCreatorRolesAndCount(id, model);
|
||||
|
||||
if (!entity) {
|
||||
return ctx.notFound();
|
||||
}
|
||||
|
||||
if (permissionChecker.cannot.read(entity)) {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
|
||||
return entityManager.getNumberOfDraftRelations(id, model);
|
||||
},
|
||||
};
|
||||
|
||||
@ -182,6 +182,21 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/single-types/:model/:id/actions/numberOfDraftRelations',
|
||||
handler: 'single-types.getNumberOfDraftRelations',
|
||||
config: {
|
||||
middlewares: [routing],
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
config: { actions: ['plugin::content-manager.explorer.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/single-types/:model/:id/:targetField',
|
||||
@ -334,5 +349,20 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/collection-types/:model/:id/actions/numberOfDraftRelations',
|
||||
handler: 'collection-types.getNumberOfDraftRelations',
|
||||
config: {
|
||||
middlewares: [routing],
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
config: { actions: ['plugin::content-manager.explorer.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const { assoc, has, prop, omit, merge } = require('lodash/fp');
|
||||
const { assoc, has, prop, omit } = require('lodash/fp');
|
||||
const strapiUtils = require('@strapi/utils');
|
||||
const { ApplicationError } = require('@strapi/utils').errors;
|
||||
const { getDeepPopulate, getDeepPopulateDraftCount } = require('./utils/populate');
|
||||
const { sumDraftCounts } = require('./utils/draft');
|
||||
|
||||
const { hasDraftAndPublish, isVisibleAttribute } = strapiUtils.contentTypes;
|
||||
const { isAnyToMany } = strapiUtils.relations;
|
||||
const { hasDraftAndPublish } = strapiUtils.contentTypes;
|
||||
const { PUBLISHED_AT_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = strapiUtils.contentTypes.constants;
|
||||
const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = strapiUtils.webhook.webhookEvents;
|
||||
|
||||
@ -39,66 +40,6 @@ const findCreatorRoles = (entity) => {
|
||||
return [];
|
||||
};
|
||||
|
||||
const getDeepPopulate = (
|
||||
uid,
|
||||
populate,
|
||||
{ onlyMany = false, countMany = false, maxLevel = Infinity } = {},
|
||||
level = 1
|
||||
) => {
|
||||
if (populate) {
|
||||
return populate;
|
||||
}
|
||||
|
||||
if (level > maxLevel) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const model = strapi.getModel(uid);
|
||||
|
||||
return Object.keys(model.attributes).reduce((populateAcc, attributeName) => {
|
||||
const attribute = model.attributes[attributeName];
|
||||
|
||||
if (attribute.type === 'relation') {
|
||||
const isManyRelation = isAnyToMany(attribute);
|
||||
// always populate createdBy, updatedBy, localizations etc.
|
||||
if (!isVisibleAttribute(model, attributeName)) {
|
||||
populateAcc[attributeName] = true;
|
||||
} else if (!onlyMany || isManyRelation) {
|
||||
// Only populate one level of relations
|
||||
populateAcc[attributeName] = countMany && isManyRelation ? { count: true } : true;
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute.type === 'component') {
|
||||
populateAcc[attributeName] = {
|
||||
populate: getDeepPopulate(
|
||||
attribute.component,
|
||||
null,
|
||||
{ onlyMany, countMany, maxLevel },
|
||||
level + 1
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (attribute.type === 'media') {
|
||||
populateAcc[attributeName] = { populate: 'folder' };
|
||||
}
|
||||
|
||||
if (attribute.type === 'dynamiczone') {
|
||||
populateAcc[attributeName] = {
|
||||
populate: (attribute.components || []).reduce((acc, componentUID) => {
|
||||
return merge(
|
||||
acc,
|
||||
getDeepPopulate(componentUID, null, { onlyMany, countMany, maxLevel }, level + 1)
|
||||
);
|
||||
}, {}),
|
||||
};
|
||||
}
|
||||
|
||||
return populateAcc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const addCreatedByRolesPopulate = (populate) => {
|
||||
return {
|
||||
...populate,
|
||||
@ -240,4 +181,16 @@ module.exports = ({ strapi }) => ({
|
||||
|
||||
return strapi.entityService.update(uid, entity.id, params);
|
||||
}),
|
||||
|
||||
async getNumberOfDraftRelations(id, uid) {
|
||||
const { populate, hasRelations } = getDeepPopulateDraftCount(uid);
|
||||
|
||||
if (!hasRelations) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const entity = await strapi.entityService.findOne(uid, id, { populate });
|
||||
|
||||
return sumDraftCounts(entity, uid);
|
||||
},
|
||||
});
|
||||
|
||||
46
packages/core/content-manager/server/services/utils/draft.js
Normal file
46
packages/core/content-manager/server/services/utils/draft.js
Normal file
@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
const { castArray } = require('lodash/fp');
|
||||
const strapiUtils = require('@strapi/utils');
|
||||
|
||||
const { hasDraftAndPublish, isVisibleAttribute } = strapiUtils.contentTypes;
|
||||
|
||||
const sumDraftCounts = (entity, uid) => {
|
||||
const model = strapi.getModel(uid);
|
||||
|
||||
return Object.keys(model.attributes).reduce((sum, attributeName) => {
|
||||
const attribute = model.attributes[attributeName];
|
||||
const value = entity[attributeName];
|
||||
if (!value) {
|
||||
return sum;
|
||||
}
|
||||
|
||||
switch (attribute.type) {
|
||||
case 'relation': {
|
||||
const childModel = strapi.getModel(attribute.target);
|
||||
if (hasDraftAndPublish(childModel) && isVisibleAttribute(model, attributeName)) {
|
||||
return sum + value.count;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
case 'component': {
|
||||
const compoSum = castArray(value).reduce((acc, componentValue) => {
|
||||
return acc + sumDraftCounts(componentValue, attribute.component);
|
||||
}, 0);
|
||||
return sum + compoSum;
|
||||
}
|
||||
case 'dynamiczone': {
|
||||
const dzSum = value.reduce((acc, componentValue) => {
|
||||
return acc + sumDraftCounts(componentValue, componentValue.__component);
|
||||
}, 0);
|
||||
return sum + dzSum;
|
||||
}
|
||||
default:
|
||||
return sum;
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
sumDraftCounts,
|
||||
};
|
||||
127
packages/core/content-manager/server/services/utils/populate.js
Normal file
127
packages/core/content-manager/server/services/utils/populate.js
Normal file
@ -0,0 +1,127 @@
|
||||
'use strict';
|
||||
|
||||
const { merge, isEmpty } = require('lodash/fp');
|
||||
const strapiUtils = require('@strapi/utils');
|
||||
|
||||
const { hasDraftAndPublish, isVisibleAttribute } = strapiUtils.contentTypes;
|
||||
const { isAnyToMany } = strapiUtils.relations;
|
||||
const { PUBLISHED_AT_ATTRIBUTE } = strapiUtils.contentTypes.constants;
|
||||
|
||||
const getDeepPopulate = (
|
||||
uid,
|
||||
populate,
|
||||
{ onlyMany = false, countMany = false, maxLevel = Infinity } = {},
|
||||
level = 1
|
||||
) => {
|
||||
if (populate) {
|
||||
return populate;
|
||||
}
|
||||
|
||||
if (level > maxLevel) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const model = strapi.getModel(uid);
|
||||
|
||||
return Object.keys(model.attributes).reduce((populateAcc, attributeName) => {
|
||||
const attribute = model.attributes[attributeName];
|
||||
|
||||
if (attribute.type === 'relation') {
|
||||
const isManyRelation = isAnyToMany(attribute);
|
||||
// always populate createdBy, updatedBy, localizations etc.
|
||||
if (!isVisibleAttribute(model, attributeName)) {
|
||||
populateAcc[attributeName] = true;
|
||||
} else if (!onlyMany || isManyRelation) {
|
||||
// Only populate one level of relations
|
||||
populateAcc[attributeName] = countMany && isManyRelation ? { count: true } : true;
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute.type === 'component') {
|
||||
populateAcc[attributeName] = {
|
||||
populate: getDeepPopulate(
|
||||
attribute.component,
|
||||
null,
|
||||
{ onlyMany, countMany, maxLevel },
|
||||
level + 1
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (attribute.type === 'media') {
|
||||
populateAcc[attributeName] = { populate: 'folder' };
|
||||
}
|
||||
|
||||
if (attribute.type === 'dynamiczone') {
|
||||
populateAcc[attributeName] = {
|
||||
populate: (attribute.components || []).reduce((acc, componentUID) => {
|
||||
return merge(
|
||||
acc,
|
||||
getDeepPopulate(componentUID, null, { onlyMany, countMany, maxLevel }, level + 1)
|
||||
);
|
||||
}, {}),
|
||||
};
|
||||
}
|
||||
|
||||
return populateAcc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const getDeepPopulateDraftCount = (uid) => {
|
||||
const model = strapi.getModel(uid);
|
||||
let hasRelations = false;
|
||||
|
||||
const populate = Object.keys(model.attributes).reduce((populateAcc, attributeName) => {
|
||||
const attribute = model.attributes[attributeName];
|
||||
|
||||
switch (attribute.type) {
|
||||
case 'relation': {
|
||||
const childModel = strapi.getModel(attribute.target);
|
||||
if (hasDraftAndPublish(childModel) && isVisibleAttribute(model, attributeName)) {
|
||||
populateAcc[attributeName] = {
|
||||
count: true,
|
||||
filters: { [PUBLISHED_AT_ATTRIBUTE]: { $null: true } },
|
||||
};
|
||||
hasRelations = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'component': {
|
||||
const { populate, hasRelations: childHasRelations } = getDeepPopulateDraftCount(
|
||||
attribute.component
|
||||
);
|
||||
if (childHasRelations) {
|
||||
populateAcc[attributeName] = { populate };
|
||||
hasRelations = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'dynamiczone': {
|
||||
const dzPopulate = (attribute.components || []).reduce((acc, componentUID) => {
|
||||
const { populate, hasRelations: childHasRelations } =
|
||||
getDeepPopulateDraftCount(componentUID);
|
||||
if (childHasRelations) {
|
||||
hasRelations = true;
|
||||
return merge(acc, populate);
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
if (!isEmpty(dzPopulate)) {
|
||||
populateAcc[attributeName] = { populate: dzPopulate };
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return populateAcc;
|
||||
}, {});
|
||||
|
||||
return { populate, hasRelations };
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getDeepPopulate,
|
||||
getDeepPopulateDraftCount,
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user