mirror of
https://github.com/strapi/strapi.git
synced 2026-01-06 20:24:18 +00:00
feat: type entity manager service
This commit is contained in:
parent
a1c8cbb8a8
commit
153a3d8aec
@ -1,5 +1,6 @@
|
||||
import { omit } from 'lodash/fp';
|
||||
import { mapAsync, errors, contentTypes, sanitize } from '@strapi/utils';
|
||||
import type { LoadedStrapi as Strapi, Common, EntityService } from '@strapi/types';
|
||||
import { getService } from '../utils';
|
||||
import {
|
||||
getDeepPopulate,
|
||||
@ -18,7 +19,11 @@ const { PUBLISHED_AT_ATTRIBUTE } = contentTypes.constants;
|
||||
|
||||
const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE);
|
||||
|
||||
const emitEvent = async (uid: any, event: any, entity: any) => {
|
||||
// Types reused from entity service
|
||||
type Entity = EntityService.Result<Common.UID.ContentType>;
|
||||
type Body = EntityService.Params.Data.Input<Common.UID.ContentType>;
|
||||
|
||||
const emitEvent = async (uid: Common.UID.ContentType, event: string, entity: Entity) => {
|
||||
const modelDef = strapi.getModel(uid);
|
||||
const sanitizedEntity = await sanitize.sanitizers.defaultSanitizeOutput(modelDef, entity);
|
||||
|
||||
@ -28,7 +33,7 @@ const emitEvent = async (uid: any, event: any, entity: any) => {
|
||||
});
|
||||
};
|
||||
|
||||
const buildDeepPopulate = (uid: string) => {
|
||||
const buildDeepPopulate = (uid: Common.UID.ContentType) => {
|
||||
// User can configure to populate relations, so downstream services can use them.
|
||||
// They will be transformed into counts later if this is set to true.
|
||||
|
||||
@ -44,14 +49,14 @@ const buildDeepPopulate = (uid: string) => {
|
||||
/**
|
||||
* @type {import('./entity-manager').default}
|
||||
*/
|
||||
export default ({ strapi }: any) => ({
|
||||
export default ({ strapi }: { strapi: Strapi }) => ({
|
||||
/**
|
||||
* Extend this function from other plugins to add custom mapping of entity
|
||||
* responses
|
||||
* @param {Object} entity
|
||||
* @returns
|
||||
*/
|
||||
mapEntity<T = any>(entity: T): T {
|
||||
mapEntity<T = unknown>(entity: T): T {
|
||||
return entity;
|
||||
},
|
||||
|
||||
@ -61,9 +66,9 @@ export default ({ strapi }: any) => ({
|
||||
* @param {Array|Object|null} entities
|
||||
* @param {string} uid
|
||||
*/
|
||||
async mapEntitiesResponse(entities: any, uid: any) {
|
||||
async mapEntitiesResponse(entities: any, uid: Common.UID.ContentType) {
|
||||
if (entities?.results) {
|
||||
const mappedResults = await mapAsync(entities.results, (entity: any) =>
|
||||
const mappedResults = await mapAsync(entities.results, (entity: Entity) =>
|
||||
// @ts-expect-error mapEntity can be extended
|
||||
this.mapEntity(entity, uid)
|
||||
);
|
||||
@ -74,29 +79,35 @@ export default ({ strapi }: any) => ({
|
||||
return this.mapEntity(entities, uid);
|
||||
},
|
||||
|
||||
async find(opts: any, uid: any) {
|
||||
const params = { ...opts, populate: getDeepPopulate(uid) };
|
||||
async find(
|
||||
opts: Parameters<typeof strapi.entityService.findMany>[1],
|
||||
uid: Common.UID.ContentType
|
||||
) {
|
||||
const params = { ...opts, populate: getDeepPopulate(uid) } as typeof opts;
|
||||
const entities = await strapi.entityService.findMany(uid, params);
|
||||
return this.mapEntitiesResponse(entities, uid);
|
||||
},
|
||||
|
||||
async findPage(opts: any, uid: any) {
|
||||
async findPage(
|
||||
opts: Parameters<typeof strapi.entityService.findPage>[1],
|
||||
uid: Common.UID.ContentType
|
||||
) {
|
||||
const entities = await strapi.entityService.findPage(uid, opts);
|
||||
return this.mapEntitiesResponse(entities, uid);
|
||||
},
|
||||
|
||||
async findOne(id: any, uid: any, opts = {}) {
|
||||
async findOne(id: Entity['id'], uid: Common.UID.ContentType, opts = {}) {
|
||||
return (
|
||||
strapi.entityService
|
||||
.findOne(uid, id, opts)
|
||||
// @ts-expect-error mapEntity can be extended
|
||||
.then((entity: any) => this.mapEntity(entity, uid))
|
||||
.then((entity: Entity) => this.mapEntity(entity, uid))
|
||||
);
|
||||
},
|
||||
|
||||
async create(body: any, uid: any) {
|
||||
async create(body: Body, uid: Common.UID.ContentType) {
|
||||
const modelDef = strapi.getModel(uid);
|
||||
const publishData = { ...body };
|
||||
const publishData = { ...body } as any;
|
||||
const populate = await buildDeepPopulate(uid);
|
||||
|
||||
if (hasDraftAndPublish(modelDef)) {
|
||||
@ -108,7 +119,7 @@ export default ({ strapi }: any) => ({
|
||||
const entity = await strapi.entityService
|
||||
.create(uid, params)
|
||||
// @ts-expect-error mapEntity can be extended
|
||||
.then((entity: any) => this.mapEntity(entity, uid));
|
||||
.then((entity: Entity) => this.mapEntity(entity, uid));
|
||||
|
||||
if (isWebhooksPopulateRelationsEnabled()) {
|
||||
return getDeepRelationsCount(entity, uid);
|
||||
@ -117,7 +128,7 @@ export default ({ strapi }: any) => ({
|
||||
return entity;
|
||||
},
|
||||
|
||||
async update(entity: any, body: any, uid: any) {
|
||||
async update(entity: Entity, body: Partial<Body>, uid: Common.UID.ContentType) {
|
||||
const publishData = omitPublishedAtField(body);
|
||||
const populate = await buildDeepPopulate(uid);
|
||||
const params = { data: publishData, populate };
|
||||
@ -125,7 +136,7 @@ export default ({ strapi }: any) => ({
|
||||
const updatedEntity = await strapi.entityService
|
||||
.update(uid, entity.id, params)
|
||||
// @ts-expect-error mapEntity can be extended
|
||||
.then((entity: any) => this.mapEntity(entity, uid));
|
||||
.then((entity: Entity) => this.mapEntity(entity, uid));
|
||||
|
||||
if (isWebhooksPopulateRelationsEnabled()) {
|
||||
return getDeepRelationsCount(updatedEntity, uid);
|
||||
@ -133,7 +144,7 @@ export default ({ strapi }: any) => ({
|
||||
|
||||
return updatedEntity;
|
||||
},
|
||||
async clone(entity: any, body: any, uid: any) {
|
||||
async clone(entity: Entity, body: Partial<Body>, uid: Common.UID.ContentType) {
|
||||
const modelDef = strapi.getModel(uid);
|
||||
const populate = await buildDeepPopulate(uid);
|
||||
const publishData = { ...body };
|
||||
@ -156,7 +167,7 @@ export default ({ strapi }: any) => ({
|
||||
|
||||
return clonedEntity;
|
||||
},
|
||||
async delete(entity: any, uid: any) {
|
||||
async delete(entity: Entity, uid: Common.UID.ContentType) {
|
||||
const populate = await buildDeepPopulate(uid);
|
||||
const deletedEntity = await strapi.entityService.delete(uid, entity.id, { populate });
|
||||
|
||||
@ -169,11 +180,14 @@ export default ({ strapi }: any) => ({
|
||||
},
|
||||
|
||||
// FIXME: handle relations
|
||||
deleteMany(opts: any, uid: any) {
|
||||
deleteMany(
|
||||
opts: Parameters<typeof strapi.entityService.deleteMany>[1],
|
||||
uid: Common.UID.ContentType
|
||||
) {
|
||||
return strapi.entityService.deleteMany(uid, opts);
|
||||
},
|
||||
|
||||
async publish(entity: any, uid: any, body = {}) {
|
||||
async publish(entity: Entity, uid: Common.UID.ContentType, body = {}) {
|
||||
if (entity[PUBLISHED_AT_ATTRIBUTE]) {
|
||||
throw new ApplicationError('already.published');
|
||||
}
|
||||
@ -183,6 +197,7 @@ export default ({ strapi }: any) => ({
|
||||
strapi.getModel(uid),
|
||||
entity,
|
||||
undefined,
|
||||
// @ts-expect-error - FIXME: entity here is unnecessary
|
||||
entity
|
||||
);
|
||||
|
||||
@ -193,7 +208,7 @@ export default ({ strapi }: any) => ({
|
||||
|
||||
const updatedEntity = await strapi.entityService.update(uid, entity.id, params);
|
||||
|
||||
await emitEvent(uid, ENTRY_PUBLISH, updatedEntity);
|
||||
await emitEvent(uid, ENTRY_PUBLISH, updatedEntity!);
|
||||
|
||||
// @ts-expect-error mapEntity can be extended
|
||||
const mappedEntity = await this.mapEntity(updatedEntity, uid);
|
||||
@ -206,18 +221,19 @@ export default ({ strapi }: any) => ({
|
||||
return mappedEntity;
|
||||
},
|
||||
|
||||
async publishMany(entities: any, uid: any) {
|
||||
async publishMany(entities: Entity[], uid: Common.UID.ContentType) {
|
||||
if (!entities.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Validate entities before publishing, throw if invalid
|
||||
await Promise.all(
|
||||
entities.map((entity: any) => {
|
||||
entities.map((entity: Entity) => {
|
||||
return strapi.entityValidator.validateEntityCreation(
|
||||
strapi.getModel(uid),
|
||||
entity,
|
||||
undefined,
|
||||
// @ts-expect-error - FIXME: entity here is unnecessary
|
||||
entity
|
||||
);
|
||||
})
|
||||
@ -225,8 +241,8 @@ export default ({ strapi }: any) => ({
|
||||
|
||||
// Only publish entities without a published_at date
|
||||
const entitiesToPublish = entities
|
||||
.filter((entity: any) => !entity[PUBLISHED_AT_ATTRIBUTE])
|
||||
.map((entity: any) => entity.id);
|
||||
.filter((entity: Entity) => !entity[PUBLISHED_AT_ATTRIBUTE])
|
||||
.map((entity: Entity) => entity.id);
|
||||
|
||||
const filters = { id: { $in: entitiesToPublish } };
|
||||
const data = { [PUBLISHED_AT_ATTRIBUTE]: new Date() };
|
||||
@ -241,22 +257,22 @@ export default ({ strapi }: any) => ({
|
||||
const publishedEntities = await strapi.entityService.findMany(uid, { filters, populate });
|
||||
// Emit the publish event for all updated entities
|
||||
await Promise.all(
|
||||
publishedEntities.map((entity: any) => emitEvent(uid, ENTRY_PUBLISH, entity))
|
||||
publishedEntities!.map((entity: Entity) => emitEvent(uid, ENTRY_PUBLISH, entity))
|
||||
);
|
||||
|
||||
// Return the number of published entities
|
||||
return publishedEntitiesCount;
|
||||
},
|
||||
|
||||
async unpublishMany(entities: any, uid: any) {
|
||||
async unpublishMany(entities: Entity[], uid: Common.UID.ContentType) {
|
||||
if (!entities.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Only unpublish entities with a published_at date
|
||||
const entitiesToUnpublish = entities
|
||||
.filter((entity: any) => entity[PUBLISHED_AT_ATTRIBUTE])
|
||||
.map((entity: any) => entity.id);
|
||||
.filter((entity: Entity) => entity[PUBLISHED_AT_ATTRIBUTE])
|
||||
.map((entity: Entity) => entity.id);
|
||||
|
||||
const filters = { id: { $in: entitiesToUnpublish } };
|
||||
const data = { [PUBLISHED_AT_ATTRIBUTE]: null };
|
||||
@ -271,14 +287,14 @@ export default ({ strapi }: any) => ({
|
||||
const unpublishedEntities = await strapi.entityService.findMany(uid, { filters, populate });
|
||||
// Emit the unpublish event for all updated entities
|
||||
await Promise.all(
|
||||
unpublishedEntities.map((entity: any) => emitEvent(uid, ENTRY_UNPUBLISH, entity))
|
||||
unpublishedEntities!.map((entity: Entity) => emitEvent(uid, ENTRY_UNPUBLISH, entity))
|
||||
);
|
||||
|
||||
// Return the number of unpublished entities
|
||||
return unpublishedEntitiesCount;
|
||||
},
|
||||
|
||||
async unpublish(entity: any, uid: any, body = {}) {
|
||||
async unpublish(entity: Entity, uid: Common.UID.ContentType, body = {}) {
|
||||
if (!entity[PUBLISHED_AT_ATTRIBUTE]) {
|
||||
throw new ApplicationError('already.draft');
|
||||
}
|
||||
@ -290,7 +306,7 @@ export default ({ strapi }: any) => ({
|
||||
|
||||
const updatedEntity = await strapi.entityService.update(uid, entity.id, params);
|
||||
|
||||
await emitEvent(uid, ENTRY_UNPUBLISH, updatedEntity);
|
||||
await emitEvent(uid, ENTRY_UNPUBLISH, updatedEntity!);
|
||||
|
||||
// @ts-expect-error mapEntity can be extended
|
||||
const mappedEntity = await this.mapEntity(updatedEntity, uid);
|
||||
@ -303,7 +319,7 @@ export default ({ strapi }: any) => ({
|
||||
return mappedEntity;
|
||||
},
|
||||
|
||||
async countDraftRelations(id: string, uid: string) {
|
||||
async countDraftRelations(id: Entity['id'], uid: Common.UID.ContentType) {
|
||||
const { populate, hasRelations } = getDeepPopulateDraftCount(uid);
|
||||
|
||||
if (!hasRelations) {
|
||||
@ -315,7 +331,11 @@ export default ({ strapi }: any) => ({
|
||||
return sumDraftCounts(entity, uid);
|
||||
},
|
||||
|
||||
async countManyEntriesDraftRelations(ids: number[], uid: string, locale = 'en') {
|
||||
async countManyEntriesDraftRelations(
|
||||
ids: number[],
|
||||
uid: Common.UID.ContentType,
|
||||
locale: string = 'en'
|
||||
) {
|
||||
const { populate, hasRelations } = getDeepPopulateDraftCount(uid);
|
||||
|
||||
if (!hasRelations) {
|
||||
@ -328,8 +348,8 @@ export default ({ strapi }: any) => ({
|
||||
locale,
|
||||
});
|
||||
|
||||
const totalNumberDraftRelations = entities.reduce(
|
||||
(count: any, entity: any) => sumDraftCounts(entity, uid) + count,
|
||||
const totalNumberDraftRelations: number = entities!.reduce(
|
||||
(count: number, entity: Entity) => sumDraftCounts(entity, uid) + count,
|
||||
0
|
||||
);
|
||||
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
import { isNil } from 'lodash/fp';
|
||||
import { getDeepPopulate, getQueryPopulate } from './utils/populate';
|
||||
import type { Common } from '@strapi/types';
|
||||
import { type Populate, getDeepPopulate, getQueryPopulate } from './utils/populate';
|
||||
|
||||
/**
|
||||
* Builder to create a Strapi populate object.
|
||||
*
|
||||
* @param {string} uid - Content type UID
|
||||
* @param uid - Content type UID
|
||||
*
|
||||
* @example
|
||||
* const populate = await populateBuilder('api::article.article').countRelations().build();
|
||||
* // populate = { article: { populate: { count: true } } }
|
||||
*
|
||||
*/
|
||||
const populateBuilder = (uid: string) => {
|
||||
let getInitialPopulate = async () => {};
|
||||
const populateBuilder = (uid: Common.UID.Schema) => {
|
||||
let getInitialPopulate = async (): Promise<undefined | Populate> => {
|
||||
return undefined;
|
||||
};
|
||||
const deepPopulateOptions = {
|
||||
countMany: false,
|
||||
countOne: false,
|
||||
@ -22,22 +25,20 @@ const populateBuilder = (uid: string) => {
|
||||
const builder = {
|
||||
/**
|
||||
* Populates all attribute fields present in a query.
|
||||
* @param {Object} query - Query object
|
||||
* @returns {typeof builder} - Builder
|
||||
* @param query - Strapi query object
|
||||
*/
|
||||
populateFromQuery(query: any) {
|
||||
populateFromQuery(query: object) {
|
||||
getInitialPopulate = async () => getQueryPopulate(uid, query);
|
||||
return builder;
|
||||
},
|
||||
/**
|
||||
* Populate relations as count if condition is true.
|
||||
* @param {Boolean} condition
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [options.toMany] - Populate XtoMany relations as count if true.
|
||||
* @param {Boolean} [options.toOne] - Populate XtoOne relations as count if true.
|
||||
* @returns {typeof builder} - Builder
|
||||
* @param condition
|
||||
* @param [options]
|
||||
* @param [options.toMany] - Populate XtoMany relations as count if true.
|
||||
* @param [options.toOne] - Populate XtoOne relations as count if true.
|
||||
*/
|
||||
countRelationsIf(condition: any, { toMany, toOne } = { toMany: true, toOne: true }) {
|
||||
countRelationsIf(condition: boolean, { toMany, toOne } = { toMany: true, toOne: true }) {
|
||||
if (condition) {
|
||||
return this.countRelations({ toMany, toOne });
|
||||
}
|
||||
@ -45,10 +46,9 @@ const populateBuilder = (uid: string) => {
|
||||
},
|
||||
/**
|
||||
* Populate relations as count.
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean } [options.toMany] - Populate XtoMany relations as count if true.
|
||||
* @param {Boolean} [options.toOne] - Populate XtoOne relations as count if true.
|
||||
* @returns {typeof builder} - Builder
|
||||
* @param [options]
|
||||
* @param [options.toMany] - Populate XtoMany relations as count if true.
|
||||
* @param [options.toOne] - Populate XtoOne relations as count if true.
|
||||
*/
|
||||
countRelations({ toMany, toOne } = { toMany: true, toOne: true }) {
|
||||
if (!isNil(toMany)) {
|
||||
@ -61,8 +61,7 @@ const populateBuilder = (uid: string) => {
|
||||
},
|
||||
/**
|
||||
* Populate relations deeply, up to a certain level.
|
||||
* @param {Number} [level=Infinity] - Max level of nested populate.
|
||||
* @returns {typeof builder} - Builder
|
||||
* @param [level=Infinity] - Max level of nested populate.
|
||||
*/
|
||||
populateDeep(level = Infinity) {
|
||||
deepPopulateOptions.maxLevel = level;
|
||||
@ -70,7 +69,7 @@ const populateBuilder = (uid: string) => {
|
||||
},
|
||||
/**
|
||||
* Construct the populate object based on the builder options.
|
||||
* @returns {Object} - Populate object
|
||||
* @returns Populate object
|
||||
*/
|
||||
async build() {
|
||||
const initialPopulate = await getInitialPopulate();
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { merge, isEmpty, set, propEq } from 'lodash/fp';
|
||||
import strapiUtils from '@strapi/utils';
|
||||
import { Common, Attribute, EntityService } from '@strapi/types';
|
||||
|
||||
const { hasDraftAndPublish, isVisibleAttribute } = strapiUtils.contentTypes;
|
||||
const { isAnyToMany } = strapiUtils.relations;
|
||||
@ -12,22 +13,30 @@ const isRelation = propEq('type', 'relation');
|
||||
const isComponent = propEq('type', 'component');
|
||||
const isDynamicZone = propEq('type', 'dynamiczone');
|
||||
|
||||
// TODO: Import from @strapi/types when it's available there
|
||||
type Model = Parameters<typeof isVisibleAttribute>[0];
|
||||
export type Populate = EntityService.Params.Populate.Any<Common.UID.Schema>;
|
||||
|
||||
type PopulateOptions = {
|
||||
initialPopulate?: Populate;
|
||||
countMany?: boolean;
|
||||
countOne?: boolean;
|
||||
maxLevel?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Populate the model for relation
|
||||
* @param {Object} attribute - Attribute containing a relation
|
||||
* @param {String} attribute.relation - type of relation
|
||||
* @param attribute - Attribute containing a relation
|
||||
* @param attribute.relation - type of relation
|
||||
* @param model - Model of the populated entity
|
||||
* @param attributeName
|
||||
* @param {Object} options - Options to apply while populating
|
||||
* @param {Boolean} options.countMany
|
||||
* @param {Boolean} options.countOne
|
||||
* @returns {true|{count: true}}
|
||||
* @param options - Options to apply while populating
|
||||
*/
|
||||
function getPopulateForRelation(
|
||||
attribute: any,
|
||||
model: any,
|
||||
attributeName: any,
|
||||
{ countMany, countOne, initialPopulate }: any
|
||||
attribute: Attribute.Any,
|
||||
model: Model,
|
||||
attributeName: string,
|
||||
{ countMany, countOne, initialPopulate }: PopulateOptions
|
||||
) {
|
||||
const isManyRelation = isAnyToMany(attribute);
|
||||
|
||||
@ -49,19 +58,18 @@ function getPopulateForRelation(
|
||||
|
||||
/**
|
||||
* Populate the model for Dynamic Zone components
|
||||
* @param {Object} attribute - Attribute containing the components
|
||||
* @param {String[]} attribute.components - IDs of components
|
||||
* @param {Object} options - Options to apply while populating
|
||||
* @param {Boolean} options.countMany
|
||||
* @param {Boolean} options.countOne
|
||||
* @param {Number} options.maxLevel
|
||||
* @param {Number} level
|
||||
* @returns {{populate: Object}}
|
||||
* @param attribute - Attribute containing the components
|
||||
* @param attribute.components - IDs of components
|
||||
* @param options - Options to apply while populating
|
||||
*/
|
||||
function getPopulateForDZ(attribute: any, options: any, level: any) {
|
||||
function getPopulateForDZ(
|
||||
attribute: Attribute.DynamicZone,
|
||||
options: PopulateOptions,
|
||||
level: number
|
||||
) {
|
||||
// Use fragments to populate the dynamic zone components
|
||||
const populatedComponents = (attribute.components || []).reduce(
|
||||
(acc: any, componentUID: any) => ({
|
||||
(acc: any, componentUID: Common.UID.Component) => ({
|
||||
...acc,
|
||||
[componentUID]: {
|
||||
populate: getDeepPopulate(componentUID, options, level + 1),
|
||||
@ -75,21 +83,26 @@ function getPopulateForDZ(attribute: any, options: any, level: any) {
|
||||
|
||||
/**
|
||||
* Get the populated value based on the type of the attribute
|
||||
* @param {String} attributeName - Name of the attribute
|
||||
* @param {Object} model - Model of the populated entity
|
||||
* @param {Object} model.attributes
|
||||
* @param {Object} options - Options to apply while populating
|
||||
* @param {Boolean} options.countMany
|
||||
* @param {Boolean} options.countOne
|
||||
* @param {Number} options.maxLevel
|
||||
* @param {Number} level
|
||||
* @returns {Object}
|
||||
* @param attributeName - Name of the attribute
|
||||
* @param model - Model of the populated entity
|
||||
* @param model.attributes
|
||||
* @param options - Options to apply while populating
|
||||
* @param options.countMany
|
||||
* @param options.countOne
|
||||
* @param options.maxLevel
|
||||
* @param level
|
||||
*/
|
||||
function getPopulateFor(attributeName: any, model: any, options: any, level: any): any {
|
||||
function getPopulateFor(
|
||||
attributeName: string,
|
||||
model: any,
|
||||
options: PopulateOptions,
|
||||
level: number
|
||||
): { [key: string]: boolean | object } {
|
||||
const attribute = model.attributes[attributeName];
|
||||
|
||||
switch (attribute.type) {
|
||||
case 'relation':
|
||||
// @ts-expect-error - TODO: support populate count typing
|
||||
return {
|
||||
[attributeName]: getPopulateForRelation(attribute, model, attributeName, options),
|
||||
};
|
||||
@ -114,17 +127,18 @@ function getPopulateFor(attributeName: any, model: any, options: any, level: any
|
||||
|
||||
/**
|
||||
* Deeply populate a model based on UID
|
||||
* @param {String} uid - Unique identifier of the model
|
||||
* @param {Object} [options] - Options to apply while populating
|
||||
* @param {Boolean} [options.countMany=false]
|
||||
* @param {Boolean} [options.countOne=false]
|
||||
* @param {Number} [options.maxLevel=Infinity]
|
||||
* @param {Number} [level=1] - Current level of nested call
|
||||
* @returns {Object}
|
||||
* @param uid - Unique identifier of the model
|
||||
* @param options - Options to apply while populating
|
||||
* @param level - Current level of nested call
|
||||
*/
|
||||
const getDeepPopulate = (
|
||||
uid: any,
|
||||
{ initialPopulate = {}, countMany = false, countOne = false, maxLevel = Infinity }: any = {},
|
||||
uid: Common.UID.Schema,
|
||||
{
|
||||
initialPopulate = {} as any,
|
||||
countMany = false,
|
||||
countOne = false,
|
||||
maxLevel = Infinity,
|
||||
}: PopulateOptions = {},
|
||||
level = 1
|
||||
) => {
|
||||
if (level > maxLevel) {
|
||||
@ -134,13 +148,19 @@ const getDeepPopulate = (
|
||||
const model = strapi.getModel(uid);
|
||||
|
||||
return Object.keys(model.attributes).reduce(
|
||||
(populateAcc, attributeName) =>
|
||||
(populateAcc, attributeName: string) =>
|
||||
merge(
|
||||
populateAcc,
|
||||
getPopulateFor(
|
||||
attributeName,
|
||||
model,
|
||||
{ initialPopulate: initialPopulate?.[attributeName], countMany, countOne, maxLevel },
|
||||
{
|
||||
// @ts-expect-error - improve types
|
||||
initialPopulate: initialPopulate?.[attributeName],
|
||||
countMany,
|
||||
countOne,
|
||||
maxLevel,
|
||||
},
|
||||
level
|
||||
)
|
||||
),
|
||||
@ -152,12 +172,12 @@ const getDeepPopulate = (
|
||||
* getDeepPopulateDraftCount works recursively on the attributes of a model
|
||||
* creating a populated object to count all the unpublished relations within the model
|
||||
* These relations can be direct to this content type or contained within components/dynamic zones
|
||||
* @param {String} uid of the model
|
||||
* @returns {Object} result
|
||||
* @returns {Object} result.populate
|
||||
* @returns {Boolean} result.hasRelations
|
||||
* @param uid of the model
|
||||
* @returns result
|
||||
* @returns result.populate
|
||||
* @returns result.hasRelations
|
||||
*/
|
||||
const getDeepPopulateDraftCount = (uid: any) => {
|
||||
const getDeepPopulateDraftCount = (uid: Common.UID.Schema) => {
|
||||
const model = strapi.getModel(uid);
|
||||
let hasRelations = false;
|
||||
|
||||
@ -213,13 +233,9 @@ const getDeepPopulateDraftCount = (uid: any) => {
|
||||
|
||||
/**
|
||||
* Create a Strapi populate object which populates all attribute fields of a Strapi query.
|
||||
*
|
||||
* @param {string} uid
|
||||
* @param {Object} query
|
||||
* @returns {Object} populate object
|
||||
*/
|
||||
const getQueryPopulate = async (uid: any, query: any): Promise<any> => {
|
||||
let populateQuery = {};
|
||||
const getQueryPopulate = async (uid: Common.UID.Schema, query: object): Promise<Populate> => {
|
||||
let populateQuery: Populate = {};
|
||||
|
||||
await strapiUtils.traverse.traverseQueryFilters(
|
||||
/**
|
||||
@ -239,6 +255,7 @@ const getQueryPopulate = async (uid: any, query: any): Promise<any> => {
|
||||
// Populate all relations, components and media
|
||||
if (isRelation(attribute) || isMedia(attribute) || isComponent(attribute)) {
|
||||
const populatePath = path.attribute.replace(/\./g, '.populate.');
|
||||
// @ts-expect-error - lodash doesn't resolve the Populate type correctly
|
||||
populateQuery = set(populatePath, {}, populateQuery);
|
||||
}
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user