@r1tsu/payload
Version:
434 lines (433 loc) • 17.4 kB
JavaScript
/* eslint-disable no-param-reassign */ import { fieldAffectsData, tabHasName } from '../../config/types.js';
import getValueWithDefault from '../../getDefaultValue.js';
import { relationshipPopulationPromise } from './relationshipPopulationPromise.js';
import { traverseFields } from './traverseFields.js';
// This function is responsible for the following actions, in order:
// - Remove hidden fields from response
// - Flatten locales into requested locale
// - Sanitize outgoing data (point field, etc.)
// - Execute field hooks
// - Execute read access control
// - Populate relationships
export const promise = async ({ collection, context, currentDepth, depth, doc, fallbackLocale, field, fieldPromises, findMany, flattenLocales, global, locale, overrideAccess, populationPromises, req, showHiddenFields, siblingDoc, triggerAccessControl = true, triggerHooks = true })=>{
if (fieldAffectsData(field) && field.hidden && typeof siblingDoc[field.name] !== 'undefined' && !showHiddenFields) {
delete siblingDoc[field.name];
}
const shouldHoistLocalizedValue = flattenLocales && fieldAffectsData(field) && typeof siblingDoc[field.name] === 'object' && siblingDoc[field.name] !== null && field.localized && locale !== 'all' && req.payload.config.localization;
if (shouldHoistLocalizedValue) {
// replace actual value with localized value before sanitizing
// { [locale]: fields } -> fields
const value = siblingDoc[field.name][locale];
let hoistedValue = value;
if (fallbackLocale && fallbackLocale !== locale) {
const fallbackValue = siblingDoc[field.name][fallbackLocale];
const isNullOrUndefined = typeof value === 'undefined' || value === null;
if (fallbackValue) {
switch(field.type){
case 'text':
case 'textarea':
{
if (value === '' || isNullOrUndefined) {
hoistedValue = fallbackValue;
}
break;
}
default:
{
if (isNullOrUndefined) {
hoistedValue = fallbackValue;
}
break;
}
}
}
}
siblingDoc[field.name] = hoistedValue;
}
// Sanitize outgoing field value
switch(field.type){
case 'group':
{
// Fill groups with empty objects so fields with hooks within groups can populate
// themselves virtually as necessary
if (typeof siblingDoc[field.name] === 'undefined') {
siblingDoc[field.name] = {};
}
break;
}
case 'tabs':
{
field.tabs.forEach((tab)=>{
if (tabHasName(tab) && (typeof siblingDoc[tab.name] === 'undefined' || siblingDoc[tab.name] === null)) {
siblingDoc[tab.name] = {};
}
});
break;
}
case 'richText':
{
const editor = field?.editor;
// This is run here AND in the GraphQL Resolver
if (editor?.populationPromises) {
editor.populationPromises({
context,
currentDepth,
depth,
field,
fieldPromises,
findMany,
flattenLocales,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc
});
}
break;
}
case 'point':
{
const pointDoc = siblingDoc[field.name];
if (Array.isArray(pointDoc?.coordinates) && pointDoc.coordinates.length === 2) {
siblingDoc[field.name] = pointDoc.coordinates;
} else {
siblingDoc[field.name] = undefined;
}
break;
}
default:
{
break;
}
}
if (fieldAffectsData(field)) {
// Execute hooks
if (triggerHooks && field.hooks?.afterRead) {
await field.hooks.afterRead.reduce(async (priorHook, currentHook)=>{
await priorHook;
const shouldRunHookOnAllLocales = field.localized && (locale === 'all' || !flattenLocales) && typeof siblingDoc[field.name] === 'object';
if (shouldRunHookOnAllLocales) {
const hookPromises = Object.entries(siblingDoc[field.name]).map(([locale, value])=>(async ()=>{
const hookedValue = await currentHook({
collection,
context,
data: doc,
field,
global,
operation: 'read',
originalDoc: doc,
req,
siblingData: siblingDoc,
value
});
if (hookedValue !== undefined) {
siblingDoc[field.name][locale] = hookedValue;
}
})());
await Promise.all(hookPromises);
} else {
const hookedValue = await currentHook({
collection,
context,
data: doc,
field,
findMany,
global,
operation: 'read',
originalDoc: doc,
overrideAccess,
req,
siblingData: siblingDoc,
value: siblingDoc[field.name]
});
if (hookedValue !== undefined) {
siblingDoc[field.name] = hookedValue;
}
}
}, Promise.resolve());
}
// Execute access control
let allowDefaultValue = true;
if (triggerAccessControl && field.access && field.access.read) {
const result = overrideAccess ? true : await field.access.read({
id: doc.id,
data: doc,
doc,
req,
siblingData: siblingDoc
});
if (!result) {
allowDefaultValue = false;
delete siblingDoc[field.name];
}
}
// Set defaultValue on the field for globals being returned without being first created
// or collection documents created prior to having a default
if (allowDefaultValue && typeof siblingDoc[field.name] === 'undefined' && typeof field.defaultValue !== 'undefined') {
siblingDoc[field.name] = await getValueWithDefault({
defaultValue: field.defaultValue,
locale,
req,
value: siblingDoc[field.name]
});
}
if (field.type === 'relationship' || field.type === 'upload') {
populationPromises.push(relationshipPopulationPromise({
currentDepth,
depth,
fallbackLocale,
field,
locale,
overrideAccess,
req,
showHiddenFields,
siblingDoc
}));
}
}
switch(field.type){
case 'group':
{
let groupDoc = siblingDoc[field.name];
if (typeof siblingDoc[field.name] !== 'object') groupDoc = {};
traverseFields({
collection,
context,
currentDepth,
depth,
doc,
fallbackLocale,
fieldPromises,
fields: field.fields,
findMany,
flattenLocales,
global,
locale,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc: groupDoc,
triggerAccessControl,
triggerHooks
});
break;
}
case 'array':
{
const rows = siblingDoc[field.name];
if (Array.isArray(rows)) {
rows.forEach((row)=>{
traverseFields({
collection,
context,
currentDepth,
depth,
doc,
fallbackLocale,
fieldPromises,
fields: field.fields,
findMany,
flattenLocales,
global,
locale,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc: row || {},
triggerAccessControl,
triggerHooks
});
});
} else if (!shouldHoistLocalizedValue && typeof rows === 'object' && rows !== null) {
Object.values(rows).forEach((localeRows)=>{
if (Array.isArray(localeRows)) {
localeRows.forEach((row)=>{
traverseFields({
collection,
context,
currentDepth,
depth,
doc,
fallbackLocale,
fieldPromises,
fields: field.fields,
findMany,
flattenLocales,
global,
locale,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc: row || {},
triggerAccessControl,
triggerHooks
});
});
}
});
} else {
siblingDoc[field.name] = [];
}
break;
}
case 'blocks':
{
const rows = siblingDoc[field.name];
if (Array.isArray(rows)) {
rows.forEach((row)=>{
const block = field.blocks.find((blockType)=>blockType.slug === row.blockType);
if (block) {
traverseFields({
collection,
context,
currentDepth,
depth,
doc,
fallbackLocale,
fieldPromises,
fields: block.fields,
findMany,
flattenLocales,
global,
locale,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc: row || {},
triggerAccessControl,
triggerHooks
});
}
});
} else if (!shouldHoistLocalizedValue && typeof rows === 'object' && rows !== null) {
Object.values(rows).forEach((localeRows)=>{
if (Array.isArray(localeRows)) {
localeRows.forEach((row)=>{
const block = field.blocks.find((blockType)=>blockType.slug === row.blockType);
if (block) {
traverseFields({
collection,
context,
currentDepth,
depth,
doc,
fallbackLocale,
fieldPromises,
fields: block.fields,
findMany,
flattenLocales,
global,
locale,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc: row || {},
triggerAccessControl,
triggerHooks
});
}
});
}
});
} else {
siblingDoc[field.name] = [];
}
break;
}
case 'row':
case 'collapsible':
{
traverseFields({
collection,
context,
currentDepth,
depth,
doc,
fallbackLocale,
fieldPromises,
fields: field.fields,
findMany,
flattenLocales,
global,
locale,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc,
triggerAccessControl,
triggerHooks
});
break;
}
case 'tab':
{
let tabDoc = siblingDoc;
if (tabHasName(field)) {
tabDoc = siblingDoc[field.name];
if (typeof siblingDoc[field.name] !== 'object') tabDoc = {};
}
traverseFields({
collection,
context,
currentDepth,
depth,
doc,
fallbackLocale,
fieldPromises,
fields: field.fields,
findMany,
flattenLocales,
global,
locale,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc: tabDoc,
triggerAccessControl,
triggerHooks
});
break;
}
case 'tabs':
{
traverseFields({
collection,
context,
currentDepth,
depth,
doc,
fallbackLocale,
fieldPromises,
fields: field.tabs.map((tab)=>({
...tab,
type: 'tab'
})),
findMany,
flattenLocales,
global,
locale,
overrideAccess,
populationPromises,
req,
showHiddenFields,
siblingDoc,
triggerAccessControl,
triggerHooks
});
break;
}
default:
{
break;
}
}
};
//# sourceMappingURL=promise.js.map