UNPKG

@r1tsu/payload

Version:

335 lines (334 loc) 13.6 kB
import merge from 'deepmerge'; import { fieldAffectsData, tabHasName } from '../../config/types.js'; import { beforeDuplicate } from './beforeDuplicate.js'; import { getExistingRowDoc } from './getExistingRowDoc.js'; import { traverseFields } from './traverseFields.js'; // This function is responsible for the following actions, in order: // - Run condition // - Execute field hooks // - Validate data // - Transform data for storage // - beforeDuplicate hooks (if duplicate) // - Unflatten locales export const promise = async ({ id, collection, context, data, doc, docWithLocales, duplicate, errors, field, global, mergeLocaleActions, operation, path, req, siblingData, siblingDoc, siblingDocWithLocales, skipValidation })=>{ const passesCondition = field.admin?.condition ? Boolean(field.admin.condition(data, siblingData, { user: req.user })) : true; let skipValidationFromHere = skipValidation || !passesCondition; const { localization } = req.payload.config; const defaultLocale = localization ? localization?.defaultLocale : 'en'; const operationLocale = req.locale || defaultLocale; if (fieldAffectsData(field)) { // skip validation if the field is localized and the incoming data is null if (field.localized && operationLocale !== defaultLocale) { if ([ 'array', 'blocks' ].includes(field.type) && siblingData[field.name] === null) { skipValidationFromHere = true; } } // Execute hooks if (field.hooks?.beforeChange) { await field.hooks.beforeChange.reduce(async (priorHook, currentHook)=>{ await priorHook; const hookedValue = await currentHook({ collection, context, data, field, global, operation, originalDoc: doc, previousSiblingDoc: siblingDoc, previousValue: siblingDoc[field.name], req, siblingData, value: siblingData[field.name] }); if (hookedValue !== undefined) { siblingData[field.name] = hookedValue; } }, Promise.resolve()); } // Validate if (!skipValidationFromHere && field.validate) { const valueToValidate = siblingData[field.name]; let jsonError; if (field.type === 'json' && typeof siblingData[field.name] === 'string') { try { JSON.parse(siblingData[field.name]); } catch (e) { jsonError = e; } } const validationResult = await field.validate(valueToValidate, { ...field, id, data: merge(doc, data, { arrayMerge: (_, source)=>source }), jsonError, operation, preferences: { fields: {} }, req, siblingData: merge(siblingDoc, siblingData, { arrayMerge: (_, source)=>source }) }); if (typeof validationResult === 'string') { errors.push({ field: `${path}${field.name}`, message: validationResult }); } } const beforeDuplicateArgs = { collection, context, data, field, global: undefined, req, siblingData, value: siblingData[field.name] }; // Push merge locale action if applicable if (localization && field.localized) { mergeLocaleActions.push(async ()=>{ const localeData = await localization.localeCodes.reduce(async (localizedValuesPromise, locale)=>{ const localizedValues = await localizedValuesPromise; let fieldValue = locale === req.locale ? siblingData[field.name] : siblingDocWithLocales?.[field.name]?.[locale]; if (duplicate && field.hooks?.beforeDuplicate?.length) { beforeDuplicateArgs.value = fieldValue; fieldValue = await beforeDuplicate(beforeDuplicateArgs); } // const result = await localizedValues // update locale value if it's not undefined if (typeof fieldValue !== 'undefined') { return { ...localizedValues, [locale]: fieldValue }; } return localizedValuesPromise; }, Promise.resolve({})); // If there are locales with data, set the data if (Object.keys(localeData).length > 0) { siblingData[field.name] = localeData; } }); } else if (duplicate && field.hooks?.beforeDuplicate?.length) { mergeLocaleActions.push(async ()=>{ siblingData[field.name] = await beforeDuplicate(beforeDuplicateArgs); }); } } switch(field.type){ case 'point': { // Transform point data for storage if (Array.isArray(siblingData[field.name]) && siblingData[field.name][0] !== null && siblingData[field.name][1] !== null) { siblingData[field.name] = { type: 'Point', coordinates: [ parseFloat(siblingData[field.name][0]), parseFloat(siblingData[field.name][1]) ] }; } break; } case 'group': { if (typeof siblingData[field.name] !== 'object') siblingData[field.name] = {}; if (typeof siblingDoc[field.name] !== 'object') siblingDoc[field.name] = {}; if (typeof siblingDocWithLocales[field.name] !== 'object') siblingDocWithLocales[field.name] = {}; await traverseFields({ id, collection, context, data, doc, docWithLocales, duplicate, errors, fields: field.fields, global, mergeLocaleActions, operation, path: `${path}${field.name}.`, req, siblingData: siblingData[field.name], siblingDoc: siblingDoc[field.name], siblingDocWithLocales: siblingDocWithLocales[field.name], skipValidation: skipValidationFromHere }); break; } case 'array': { const rows = siblingData[field.name]; if (Array.isArray(rows)) { const promises = []; rows.forEach((row, i)=>{ promises.push(traverseFields({ id, collection, context, data, doc, docWithLocales, duplicate, errors, fields: field.fields, global, mergeLocaleActions, operation, path: `${path}${field.name}.${i}.`, req, siblingData: row, siblingDoc: getExistingRowDoc(row, siblingDoc[field.name]), siblingDocWithLocales: getExistingRowDoc(row, siblingDocWithLocales[field.name]), skipValidation: skipValidationFromHere })); }); await Promise.all(promises); } break; } case 'blocks': { const rows = siblingData[field.name]; if (Array.isArray(rows)) { const promises = []; rows.forEach((row, i)=>{ const rowSiblingDoc = getExistingRowDoc(row, siblingDoc[field.name]); const rowSiblingDocWithLocales = getExistingRowDoc(row, siblingDocWithLocales[field.name]); const blockTypeToMatch = row.blockType || rowSiblingDoc.blockType; const block = field.blocks.find((blockType)=>blockType.slug === blockTypeToMatch); if (block) { promises.push(traverseFields({ id, collection, context, data, doc, docWithLocales, duplicate, errors, fields: block.fields, global, mergeLocaleActions, operation, path: `${path}${field.name}.${i}.`, req, siblingData: row, siblingDoc: rowSiblingDoc, siblingDocWithLocales: rowSiblingDocWithLocales, skipValidation: skipValidationFromHere })); } }); await Promise.all(promises); } break; } case 'row': case 'collapsible': { await traverseFields({ id, collection, context, data, doc, docWithLocales, duplicate, errors, fields: field.fields, global, mergeLocaleActions, operation, path, req, siblingData, siblingDoc, siblingDocWithLocales, skipValidation: skipValidationFromHere }); break; } case 'tab': { let tabPath = path; let tabSiblingData = siblingData; let tabSiblingDoc = siblingDoc; let tabSiblingDocWithLocales = siblingDocWithLocales; if (tabHasName(field)) { tabPath = `${path}${field.name}.`; if (typeof siblingData[field.name] !== 'object') siblingData[field.name] = {}; if (typeof siblingDoc[field.name] !== 'object') siblingDoc[field.name] = {}; if (typeof siblingDocWithLocales[field.name] !== 'object') siblingDocWithLocales[field.name] = {}; tabSiblingData = siblingData[field.name]; tabSiblingDoc = siblingDoc[field.name]; tabSiblingDocWithLocales = siblingDocWithLocales[field.name]; } await traverseFields({ id, collection, context, data, doc, docWithLocales, duplicate, errors, fields: field.fields, global, mergeLocaleActions, operation, path: tabPath, req, siblingData: tabSiblingData, siblingDoc: tabSiblingDoc, siblingDocWithLocales: tabSiblingDocWithLocales, skipValidation: skipValidationFromHere }); break; } case 'tabs': { await traverseFields({ id, collection, context, data, doc, docWithLocales, duplicate, errors, fields: field.tabs.map((tab)=>({ ...tab, type: 'tab' })), global, mergeLocaleActions, operation, path, req, siblingData, siblingDoc, siblingDocWithLocales, skipValidation: skipValidationFromHere }); break; } default: { break; } } }; //# sourceMappingURL=promise.js.map