Declare a new load method in the entity service (#10930)

This commit is contained in:
Jean-Sébastien Herbaux 2021-09-10 10:24:33 +02:00 committed by GitHub
parent a0cdf693bb
commit 8e6de48dc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 98 deletions

View File

@ -6,7 +6,6 @@ const {
webhook: webhookUtils,
contentTypes: contentTypesUtils,
relations: relationsUtils,
pagination: paginationUtils,
} = require('@strapi/utils');
const uploadFiles = require('../utils/upload-files');
@ -16,21 +15,30 @@ const {
updateComponents,
deleteComponents,
} = require('./components');
const { transformParamsToQuery, pickSelectionParams } = require('./params');
const {
chainParamsTransformations,
transformCommonParams,
transformPaginationParams,
transformPublicationStateParams,
pickSelectionParams,
} = require('./params');
const transformParamsToQuery = (uid, params) => {
return chainParamsTransformations(params, [
// _q, _where, filters, etc...
transformCommonParams,
// page, pageSize, start, limit
transformPaginationParams,
// publicationState
transformPublicationStateParams(uid),
]);
};
const { MANY_RELATIONS } = relationsUtils.constants;
// TODO: those should be strapi events used by the webhooks not the other way arround
const { ENTRY_CREATE, ENTRY_UPDATE, ENTRY_DELETE } = webhookUtils.webhookEvents;
const paginateAndTransformToQuery = (uid, opts = {}) => {
// Paginate the opts
const paginatedOpts = paginationUtils.withDefaultPagination(opts);
// Transform the opts into a query & return it
return transformParamsToQuery(uid, paginatedOpts);
};
module.exports = ctx => {
const implementation = createDefaultImplementation(ctx);
@ -76,7 +84,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
const { params } = await this.wrapOptions(opts, { uid, action: 'find' });
const query = paginateAndTransformToQuery(uid, params);
const query = transformParamsToQuery(uid, params);
if (kind === 'singleType') {
return db.query(uid).findOne(query);
@ -88,7 +96,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
async findPage(uid, opts) {
const { params } = await this.wrapOptions(opts, { uid, action: 'findPage' });
const query = paginateAndTransformToQuery(uid, params);
const query = transformParamsToQuery(uid, params);
return db.query(uid).findPage(query);
},
@ -140,7 +148,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
async count(uid, opts) {
const { params } = await this.wrapOptions(opts, { uid, action: 'count' });
const query = paginateAndTransformToQuery(uid, params);
const query = transformParamsToQuery(uid, params);
return db.query(uid).count(query);
},
@ -248,4 +256,17 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
return db.query(uid).deleteMany(query);
},
load(uid, entity, field, params) {
const { attributes } = strapi.getModel(uid);
const attribute = attributes[field];
const loadParams =
attribute.type === 'relation'
? transformParamsToQuery(attribute.target, params)
: chainParamsTransformations(params, [transformCommonParams, transformPaginationParams]);
return db.query(uid).load(entity, field, loadParams);
},
});

View File

@ -1,6 +1,6 @@
'use strict';
const { pick } = require('lodash/fp');
const { pick, pipe, isNil } = require('lodash/fp');
const {
convertSortQueryParams,
@ -15,47 +15,28 @@ const { contentTypes: contentTypesUtils } = require('@strapi/utils');
const { PUBLISHED_AT_ATTRIBUTE } = contentTypesUtils.constants;
// TODO: check invalid values / add defaults ....
const transformParamsToQuery = (uid, params = {}) => {
const model = strapi.getModel(uid);
// TODO: to remove once the front is migrated
const convertOldQuery = params => {
const obj = {};
const query = {};
Object.keys(params).forEach(key => {
if (key.startsWith('_')) {
obj[key.slice(1)] = params[key];
} else {
obj[key] = params[key];
}
});
const {
start,
page,
pageSize,
limit,
sort,
filters,
fields,
populate,
publicationState,
_q,
_where,
...rest
} = params;
return obj;
};
const transformCommonParams = (params = {}) => {
const { _q, sort, filters, _where, fields, populate, ...query } = params;
if (_q) {
query._q = _q;
}
if (page) {
query.page = Number(page);
}
if (pageSize) {
query.pageSize = Number(pageSize);
}
if (start) {
query.offset = convertStartQueryParams(start);
}
if (limit) {
query.limit = convertLimitQueryParams(limit);
}
if (sort) {
query.orderBy = convertSortQueryParams(sort);
}
@ -78,8 +59,50 @@ const transformParamsToQuery = (uid, params = {}) => {
query.populate = convertPopulateQueryParams(populate);
}
// TODO: move to convert-query-params ?
if (publicationState && contentTypesUtils.hasDraftAndPublish(model)) {
return { ...convertOldQuery(query), ...query };
};
const transformPaginationParams = (params = {}) => {
const { page, pageSize, start, limit, ...query } = params;
const isPagePagination = !isNil(page) || !isNil(pageSize);
const isOffsetPagination = !isNil(start) || !isNil(limit);
if (isPagePagination && isOffsetPagination) {
throw new Error(
'Invalid pagination attributes. You cannot use page and offset pagination in the same query'
);
}
if (page) {
query.page = Number(page);
}
if (pageSize) {
query.pageSize = Number(pageSize);
}
if (start) {
query.offset = convertStartQueryParams(start);
}
if (limit) {
query.limit = convertLimitQueryParams(limit);
}
return { ...convertOldQuery(query), ...query };
};
const transformPublicationStateParams = uid => (params = {}) => {
const contentType = strapi.getModel(uid);
if (!contentType) {
return params;
}
const { publicationState, ...query } = params;
if (publicationState && contentTypesUtils.hasDraftAndPublish(contentType)) {
const { publicationState = 'live' } = params;
const liveClause = {
@ -97,32 +120,19 @@ const transformParamsToQuery = (uid, params = {}) => {
}
}
const finalQuery = {
...convertOldQuery(rest),
...query,
};
return finalQuery;
return { ...convertOldQuery(query), ...query };
};
// TODO: to remove once the front is migrated
const convertOldQuery = params => {
const obj = {};
Object.keys(params).forEach(key => {
if (key.startsWith('_')) {
obj[key.slice(1)] = params[key];
} else {
obj[key] = params[key];
}
});
return obj;
const chainParamsTransformations = (params, transformFunctions = []) => {
return pipe(...transformFunctions)(params);
};
const pickSelectionParams = pick(['fields', 'populate']);
module.exports = {
transformParamsToQuery,
transformCommonParams,
transformPublicationStateParams,
transformPaginationParams,
chainParamsTransformations,
pickSelectionParams,
};

View File

@ -1,15 +1,11 @@
'use strict';
const { omit } = require('lodash/fp');
module.exports = ({ strapi }) => {
const { isMorphRelation, isMedia } = strapi.plugin('graphql').service('utils').attributes;
const { transformArgs } = strapi.plugin('graphql').service('builders').utils;
return {
buildAssociationResolver: ({ contentTypeUID, attributeName }) => {
const { entityManager } = strapi.db;
const contentType = strapi.getModel(contentTypeUID);
const attribute = contentType.attributes[attributeName];
@ -33,20 +29,11 @@ module.exports = ({ strapi }) => {
usePagination: true,
});
// Since we're using the entity-manager & not the entity-service to load the
// association, we need to apply some transformation to the transformed args object
const entityManagerArgs = {
...omit(['start', 'filters'], transformedArgs),
where: transformedArgs.filters,
offset: transformedArgs.start,
};
// todo[v4]: should we move the .load to the entity service so we can use the same args everywhere?
const data = await entityManager.load(
const data = await strapi.entityService.load(
contentTypeUID,
parent,
attributeName,
entityManagerArgs
transformedArgs
);
// If this a polymorphic association, it returns the raw data

View File

@ -1,7 +1,5 @@
'use strict';
const { omit } = require('lodash/fp');
module.exports = ({ strapi }) => ({
buildComponentResolver: ({ contentTypeUID, attributeName }) => {
const { transformArgs } = strapi.plugin('graphql').service('builders').utils;
@ -10,16 +8,7 @@ module.exports = ({ strapi }) => ({
const contentType = strapi.contentTypes[contentTypeUID];
const transformedArgs = transformArgs(args, { contentType, usePagination: true });
// Since we're using the entity-manager & not the entity-service to load the
// association, we need to apply some transformation to the transformed args object
const entityManagerArgs = {
...omit(['start', 'filters'], transformedArgs),
where: transformedArgs.filters,
offset: transformedArgs.start,
};
// todo[v4]: should we move the .load to the entity service so we can use the same args everywhere?
return strapi.db.entityManager.load(contentTypeUID, parent, attributeName, entityManagerArgs);
return strapi.entityService.load(contentTypeUID, parent, attributeName, transformedArgs);
};
},
});

View File

@ -2,6 +2,6 @@
module.exports = ({ strapi }) => ({
buildDynamicZoneResolver: ({ contentTypeUID, attributeName }) => async parent => {
return strapi.db.entityManager.load(contentTypeUID, parent, attributeName);
return strapi.entityService.load(contentTypeUID, parent, attributeName);
},
});