memoizes the Populate function
This commit is contained in:
@@ -9,10 +9,8 @@ const accessPromise = async ({
|
||||
req,
|
||||
id,
|
||||
relationshipPopulations,
|
||||
depth,
|
||||
currentDepth,
|
||||
hook,
|
||||
payload,
|
||||
populate,
|
||||
}) => {
|
||||
const resultingData = data;
|
||||
|
||||
@@ -30,11 +28,7 @@ const accessPromise = async ({
|
||||
relationshipPopulations.push(relationshipPopulationPromise({
|
||||
data,
|
||||
field,
|
||||
depth,
|
||||
currentDepth,
|
||||
req,
|
||||
overrideAccess,
|
||||
payload,
|
||||
populate,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const memoize = require('fast-memoize');
|
||||
const { ValidationError } = require('../errors');
|
||||
const sanitizeFallbackLocale = require('../localization/sanitizeFallbackLocale');
|
||||
const traverseFields = require('./traverseFields');
|
||||
const executeAccess = require('../auth/executeAccess');
|
||||
|
||||
async function performFieldOperations(entityConfig, args) {
|
||||
const {
|
||||
@@ -35,12 +37,72 @@ async function performFieldOperations(entityConfig, args) {
|
||||
// Maintain a top-level list of promises
|
||||
// so that all async field access / validations / hooks
|
||||
// can run in parallel
|
||||
|
||||
const validationPromises = [];
|
||||
const accessPromises = [];
|
||||
const relationshipPopulations = [];
|
||||
const hookPromises = [];
|
||||
const errors = [];
|
||||
|
||||
// ////////////////////////////////////////////////////////////////
|
||||
// Create memoized Populate function — with the same input (ID),
|
||||
// it will return the same result without re-querying.
|
||||
// Note - this function is recreated each time a request comes in
|
||||
// ////////////////////////////////////////////////////////////////
|
||||
|
||||
const populate = memoize(async (
|
||||
data,
|
||||
dataReference,
|
||||
field,
|
||||
index,
|
||||
) => {
|
||||
const dataToUpdate = dataReference;
|
||||
|
||||
const relation = Array.isArray(field.relationTo) ? data.relationTo : field.relationTo;
|
||||
const relatedCollection = this.collections[relation];
|
||||
|
||||
if (relatedCollection) {
|
||||
const accessResult = !overrideAccess ? await executeAccess({ req, disableErrors: true, id }, relatedCollection.config.access.read) : true;
|
||||
|
||||
let populatedRelationship = null;
|
||||
|
||||
if (accessResult && (depth && currentDepth <= depth)) {
|
||||
let idString = Array.isArray(field.relationTo) ? data.value : data;
|
||||
|
||||
if (typeof idString !== 'string') {
|
||||
idString = idString.toString();
|
||||
}
|
||||
|
||||
populatedRelationship = await this.operations.collections.findByID({
|
||||
req,
|
||||
collection: relatedCollection,
|
||||
id: idString,
|
||||
currentDepth: currentDepth + 1,
|
||||
disableErrors: true,
|
||||
depth,
|
||||
});
|
||||
}
|
||||
|
||||
// If access control fails, update value to null
|
||||
// If populatedRelationship comes back, update value
|
||||
if (!accessResult || populatedRelationship) {
|
||||
if (typeof index === 'number') {
|
||||
if (Array.isArray(field.relationTo)) {
|
||||
dataToUpdate[field.name][index].value = populatedRelationship;
|
||||
} else {
|
||||
dataToUpdate[field.name][index] = populatedRelationship;
|
||||
}
|
||||
} else if (Array.isArray(field.relationTo)) {
|
||||
dataToUpdate[field.name].value = populatedRelationship;
|
||||
} else {
|
||||
dataToUpdate[field.name] = populatedRelationship;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
serializer: (populateArgs) => JSON.stringify(populateArgs[0]),
|
||||
});
|
||||
|
||||
// //////////////////////////////////////////
|
||||
// Entry point for field validation
|
||||
// //////////////////////////////////////////
|
||||
@@ -69,6 +131,7 @@ async function performFieldOperations(entityConfig, args) {
|
||||
validationPromises,
|
||||
errors,
|
||||
payload: this,
|
||||
populate,
|
||||
});
|
||||
|
||||
await Promise.all(hookPromises);
|
||||
|
||||
@@ -1,70 +1,7 @@
|
||||
const executeAccess = require('../auth/executeAccess');
|
||||
|
||||
const populate = async ({
|
||||
depth,
|
||||
currentDepth,
|
||||
req,
|
||||
overrideAccess,
|
||||
dataReference,
|
||||
data,
|
||||
field,
|
||||
index,
|
||||
id,
|
||||
payload,
|
||||
}) => {
|
||||
const dataToUpdate = dataReference;
|
||||
|
||||
const relation = Array.isArray(field.relationTo) ? data.relationTo : field.relationTo;
|
||||
const relatedCollection = payload.collections[relation];
|
||||
|
||||
if (relatedCollection) {
|
||||
const accessResult = !overrideAccess ? await executeAccess({ req, disableErrors: true, id }, relatedCollection.config.access.read) : true;
|
||||
|
||||
let populatedRelationship = null;
|
||||
|
||||
if (accessResult && (depth && currentDepth <= depth)) {
|
||||
let idString = Array.isArray(field.relationTo) ? data.value : data;
|
||||
|
||||
if (typeof idString !== 'string') {
|
||||
idString = idString.toString();
|
||||
}
|
||||
|
||||
populatedRelationship = await payload.operations.collections.findByID({
|
||||
req,
|
||||
collection: relatedCollection,
|
||||
id: idString,
|
||||
currentDepth: currentDepth + 1,
|
||||
disableErrors: true,
|
||||
depth,
|
||||
});
|
||||
}
|
||||
|
||||
// If access control fails, update value to null
|
||||
// If populatedRelationship comes back, update value
|
||||
if (!accessResult || populatedRelationship) {
|
||||
if (typeof index === 'number') {
|
||||
if (Array.isArray(field.relationTo)) {
|
||||
dataToUpdate[field.name][index].value = populatedRelationship;
|
||||
} else {
|
||||
dataToUpdate[field.name][index] = populatedRelationship;
|
||||
}
|
||||
} else if (Array.isArray(field.relationTo)) {
|
||||
dataToUpdate[field.name].value = populatedRelationship;
|
||||
} else {
|
||||
dataToUpdate[field.name] = populatedRelationship;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const relationshipPopulationPromise = ({
|
||||
data,
|
||||
field,
|
||||
depth,
|
||||
currentDepth,
|
||||
req,
|
||||
overrideAccess,
|
||||
payload,
|
||||
populate,
|
||||
}) => async () => {
|
||||
const resultingData = data;
|
||||
|
||||
@@ -74,17 +11,12 @@ const relationshipPopulationPromise = ({
|
||||
data[field.name].forEach((relatedDoc, index) => {
|
||||
const rowPromise = async () => {
|
||||
if (relatedDoc) {
|
||||
await populate({
|
||||
depth,
|
||||
currentDepth,
|
||||
req,
|
||||
overrideAccess,
|
||||
data: relatedDoc,
|
||||
dataReference: resultingData,
|
||||
await populate(
|
||||
relatedDoc,
|
||||
resultingData,
|
||||
field,
|
||||
index,
|
||||
payload,
|
||||
});
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -93,16 +25,11 @@ const relationshipPopulationPromise = ({
|
||||
|
||||
await Promise.all(rowPromises);
|
||||
} else if (data[field.name]) {
|
||||
await populate({
|
||||
depth,
|
||||
currentDepth,
|
||||
req,
|
||||
overrideAccess,
|
||||
dataReference: resultingData,
|
||||
data: data[field.name],
|
||||
await populate(
|
||||
data[field.name],
|
||||
resultingData,
|
||||
field,
|
||||
payload,
|
||||
});
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ const traverseFields = (args) => {
|
||||
validationPromises,
|
||||
errors,
|
||||
payload,
|
||||
populate,
|
||||
} = args;
|
||||
|
||||
fields.forEach((field) => {
|
||||
@@ -60,6 +61,7 @@ const traverseFields = (args) => {
|
||||
}
|
||||
|
||||
accessPromises.push(accessPromise({
|
||||
populate,
|
||||
data,
|
||||
originalDoc,
|
||||
field,
|
||||
@@ -68,10 +70,7 @@ const traverseFields = (args) => {
|
||||
req,
|
||||
id,
|
||||
relationshipPopulations,
|
||||
depth,
|
||||
currentDepth,
|
||||
hook,
|
||||
payload,
|
||||
}));
|
||||
|
||||
hookPromises.push(hookPromise({
|
||||
|
||||
Reference in New Issue
Block a user