UNPKG

payload-plugin-scheduler

Version:
117 lines (116 loc) 5.41 kB
import { getPublishDateFieldNameFromFields } from '../../util.js'; /** * Wrapper around the default `RelationshipField` that ensures "related" documents are published before the primary document */ export const SafeRelationship = (props)=>{ const validate = async (value, options)=>{ // first run user validate fn if (props.validate) { const validateRes = await props.validate(value, options); if (validateRes !== true) { return validateRes; } } const { data: rawData, req } = options; const data = rawData; const payload = req.payload; const { config } = payload; const currentCollection = options.collectionSlug ? config.collections.find((collection)=>collection.slug === options.collectionSlug) : undefined; const currentPublishDateFieldName = currentCollection ? getPublishDateFieldNameFromFields(currentCollection.fields) : getPublishDateFieldNameFromFields([]); // abort if the field is empty if (!value || Array.isArray(value) && value.length === 0) { return true; } // abort if the current document is an unscheduled draft if (!currentPublishDateFieldName || data?._status === 'draft' && !data?.[currentPublishDateFieldName]) { return true; } // cast relationTo to an array const relationsTo = Array.isArray(props.relationTo) ? props.relationTo : [ props.relationTo ]; // cast value to an array const values = Array.isArray(value) ? value : [ value ]; // build a list of collections we need to check draft status for // format: { [collectionSlug]: <titleFieldKey> } so we can generate a pretty error message later const relatedDraftCollections = {}; relationsTo.forEach((name)=>{ const collection = config.collections.find((collection)=>collection.slug === name); const useAsTitle = collection?.admin.useAsTitle; const publishDateFieldName = getPublishDateFieldNameFromFields(collection?.fields || []); if (collection?.versions?.drafts && useAsTitle && publishDateFieldName) { relatedDraftCollections[collection.slug] = { publishDateFieldName, titleFieldName: useAsTitle }; } }); // compile an array of related draft documents to check const relatedDocs = await values.reduce(async (accPromise, v)=>{ const acc = await accPromise; // naively assume that we're dealing with a simple (not polymorphic) relationship // https://payloadcms.com/docs/fields/relationship#how-the-data-is-saved let collection = props.relationTo; let id = v; // handle polymorphic relationships if (typeof v === 'object') { collection = v.relationTo; id = v.value; } // ignore related docs w/o drafts if (!relatedDraftCollections[collection]) { return acc; } try { const doc = await payload.findByID({ id, collection: collection, req }); // Only add the document if it's in draft status if (doc && doc._status === 'draft') { acc.push({ ...doc, collection }); } } catch (error) { payload.logger.error(error, `[SafeRelationship] ${collection}:${id}`); } return acc; }, Promise.resolve([])); const invalidRelatedDocs = []; // store a reference to the publish date for the current document let publishDate; if (data?._status === 'published' || !data?._status) { // for published docs, or docs w/o drafts, we assume they are currently being published publishDate = new Date(); } else if (data?._status === 'draft' && data?.[currentPublishDateFieldName]) { // for scheduled documents, use the set publish_date publishDate = new Date(data[currentPublishDateFieldName]); } // loop over the related documents and find any stragglers relatedDocs.forEach((rd)=>{ const relatedCollection = relatedDraftCollections[rd.collection]; const docPubDate = rd[relatedCollection.publishDateFieldName] ? new Date(rd[relatedCollection.publishDateFieldName]) : null // this check is necessary otherwise new Date(null) => 1/1/70 ; if (!docPubDate || docPubDate >= publishDate) { invalidRelatedDocs.push({ collection: rd.collection, title: rd[relatedCollection.titleFieldName] }); } }); if (invalidRelatedDocs.length > 0) { return `The following docs must be published before this one: ${invalidRelatedDocs.map(({ collection, title })=>`${title} (${collection})`).join(', ')}`; } return true; }; return { type: 'relationship', ...props, validate }; }; //# sourceMappingURL=index.js.map