UNPKG

@websolutespa/payload-plugin-bowl

Version:

Bowl PayloadCms plugin of the BOM Repository

457 lines (455 loc) 17.8 kB
import { isNonEmptyArray } from '@websolutespa/bom-core'; import { HttpStatus, isDataField, parseQueryDepth, parseQueryInt } from '@websolutespa/payload-utils'; import { bulkWrite, getRequestCollection, ResponseBadRequest, ResponseError, ResponseSuccess, ResponseUnprocessable } from '@websolutespa/payload-utils/server'; import * as Papa from 'papaparse'; import { addDataAndFileToRequest } from 'payload'; import { options } from '../../options'; import { decorateRichText_ } from '../decorators/rich-text'; import { decorateSchema_ } from '../decorators/schema'; import { findByIDHandler } from '../utils/findByIDHandler'; import { findHandler } from '../utils/findHandler'; import { ImportMode } from './types'; import { setMixerContext } from './utils'; export function parseDepth(depth, defaultDepth = 1) { if (depth !== undefined) { return parseInt(String(depth), 10); } else { return defaultDepth; } } export async function getCollectionItems(req, slug, depth) { const { query = {}, payload, user } = req; const { locale, where, sort, draft } = query; depth = depth !== undefined ? depth : parseQueryDepth(query.depth); query.depth = String(depth); const limit = parseQueryInt(query.limit, 10000); /* console.log('getCollectionItems', 'slug', slug, 'locale', locale, 'where', where, 'sort', sort, 'draft', draft, 'depth', depth, 'user', user?.email ); */ try { const response = await payload.find({ collection: slug, draft: draft === 'true', locale: locale, fallbackLocale: options.defaultLocale, where: where, sort: sort, depth, limit, pagination: false, req, user, overrideAccess: false }); // console.log('getCollectionItems', slug, user?.role); const items = response.docs ? response.docs : []; // console.log('getCollectionItems.items.length', items.length); return items; } catch (error) { // 403 is expected, it means the collection is private if (error.status !== 403) { throw error; } return []; } } export async function getCollectionItem(req, slug, id, depth) { const { query = {}, payload, user } = req; const { locale, draft } = query; depth = depth !== undefined ? depth : parseQueryDepth(query.depth); query.depth = String(depth); // console.log('getCollectionItem', 'slug', slug, 'id', id, 'locale', locale, 'draft', draft, 'depth', depth, 'user', user?.email); const item = await payload.findByID({ collection: slug, id, draft: draft === 'true', locale: locale, fallbackLocale: options.defaultLocale, depth, req, user, overrideAccess: false }); return item; } export async function getGlobalItems(req, slug, depth) { const { query = {}, payload, user } = req; const { locale, draft } = query; depth = depth !== undefined ? depth : parseQueryDepth(query.depth); query.depth = String(depth); // console.log('getGlobalItems', 'slug', slug, 'locale', locale, 'draft', draft, 'depth', depth, 'user', user?.email); try { const response = await payload.findGlobal({ slug, depth, locale: locale, fallbackLocale: options.defaultLocale, showHiddenFields: false, draft: draft === 'true', req, user, overrideAccess: false }); const items = response.items ? response.items : []; return items; } catch (error) { // 403 is expected, it means the collection is private if (error.status !== 403) { throw error; } return []; } } /** * Rest api collection index get handler. */ export const collectionIndexGet = (slug)=>({ path: '/', method: 'get', handler: async (req)=>{ try { const { query } = req; if (!query) { return findHandler(req); } // console.log('collectionIndexGet', ...Object.entries(query)); const { locale, market, pagination } = query; if (typeof market === 'string' && typeof locale === 'string') { // const context = await setMixerContext(req, market, locale); // console.log('collectionIndexGet.context', context.market, context.locale, context.routes.length, context.categories.length); if (pagination === 'true') { return findHandler(req); } const items = await getCollectionItems(req, slug); return ResponseSuccess(items); } else if (pagination === 'false') { const items = await getCollectionItems(req, slug); return ResponseSuccess(items); } return findHandler(req); } catch (error) { console.error(`CollectionService.collectionIndexGet.${slug}.error`, error); return ResponseError(error); } } }); /** * Rest api collection detail get handler. */ export const collectionDetailGet = (slug)=>({ path: '/:id', method: 'get', handler: async (req)=>{ // console.log('collectionDetailGet', slug, ...Object.entries(req.query)); try { const { query } = req; if (!query) { return findByIDHandler(req); } // console.log('collectionDetailGet', ...Object.entries(query)); const { market, locale } = query; if (!(typeof market === 'string' && typeof locale === 'string')) { return findByIDHandler(req); } // const context = await setMixerContext(req, market, locale); // console.log('collectionDetailGet.context', context.market, context.locale, context.routes.length, context.categories.length); return findByIDHandler(req); } catch (error) { console.error(`CollectionService.collectionDetailGet.${slug}.error`, error); return ResponseError(error); } } }); /** * Rest api collection bulk patch handler. */ export const collectionBulkPatch = (slug)=>({ path: '/bulk', method: 'patch', handler: async (req)=>{ /* if (!hasRole(req.user, roles.Admin, roles.Editor)) { return next(); } */ await addDataAndFileToRequest(req); const { payload, data } = req; if (!isNonEmptyArray(data)) { return ResponseBadRequest(); } const records = data; try { const bulkWriteResult = await bulkWrite(payload, slug, records); if (bulkWriteResult === undefined) { return ResponseUnprocessable(); } // console.log('CollectionService.collectionBulkPatch.success', bulkWriteResult); const items = await getCollectionItems(req, slug); return ResponseSuccess(items); } catch (error) { console.error(`CollectionService.collectionBulkPatch.${slug}.error`, error); return ResponseError(error); } } }); /** * Rest api collection update patch handler. * known issue: collectionUpdatePatch() does not work, needs to be fixed */ export const collectionUpdatePatch = (slug)=>({ path: '/update', method: 'patch', handler: async (req)=>{ const collection = getRequestCollection(req); /* if (!hasRole(req.user, roles.Admin, roles.Editor)) { return next(); } */ const { payload } = req; const adapter = payload.db; const model = adapter.collections[slug]; if (!model) { return ResponseUnprocessable(); } await addDataAndFileToRequest(req); if (!isNonEmptyArray(req.data)) { return ResponseUnprocessable(); } try { const records = req.data; const keyMap = {}; records.forEach((x)=>Object.keys(x).forEach((k)=>keyMap[k] = k)); const keys = Object.keys(keyMap); const fields = collection.config.fields.filter((x)=>isDataField(x)); const filteredKeys = fields.map((x)=>x.name).filter((name)=>keys.includes(name)); const $set = {}; filteredKeys.forEach((key)=>{ if (key !== 'id') { $set[key] = { $switch: { // branches: records.filter(x => x.hasOwnProperty('order')).map(x => ( branches: records.map((x)=>({ case: { $eq: [ '$_id', x.id ] }, then: x[key] })) } }; } }); // const sourceItems = await getCollectionItems(req, slug); const where = { _id: { $in: records.map((x)=>x.id) } }; const update = [ { $set } ]; // console.log('where', where); // console.log('update', update); const response = await model.updateMany(where, update, { multi: true }); // console.log('response', response); /* response { acknowledged: true, modifiedCount: 33, upsertedId: null, upsertedCount: 0, matchedCount: 33 } */ /* const promises = records.map(item => model.findByIdAndUpdate({ _id: item.id }, item, { new: true }).lean()); const responses = await Promise.all(promises); */ const items = await getCollectionItems(req, slug); return ResponseSuccess(items); } catch (error) { console.error(`CollectionService.collectionUpdatePatch.${slug}.error`, error); return ResponseError(error); } } }); /** * Rest api collection export get handler. */ export const collectionExportGet = (slug)=>({ path: '/export', method: 'get', handler: async (req)=>{ /* if (!hasRole(req.user, roles.Admin, roles.Editor)) { return next(); } */ try { const items = await getCollectionItems(req, slug); const csv = Papa.unparse(items); return new Response(csv, { headers: new Headers({ 'Content-Type': 'text/csv', 'Content-Disposition': `attachment; filename="${slug}.csv"` }), status: 200 }); } catch (error) { console.error(`CollectionService.collectionExportGet.${slug}.error`, error); return ResponseError(error); } } }); /** * Rest api collection import post handler. */ export const collectionImportPost = (slug)=>({ path: '/import', method: 'post', handler: async (req)=>{ /* if (!hasRole(req.user, roles.Admin, roles.Editor)) { return next(); } */ const { query, payload, user } = req; // console.log('collectionImportPost', 'query', query, 'user', user); const { locale, where, sort, depth, draft, page, limit, pagination, mode } = query; const importMode = mode || ImportMode.Append; const defaultLocale = payload.config.localization ? payload.config.localization.defaultLocale : 'en'; // console.log('locale', locale, 'defaultLocale', defaultLocale); try { await addDataAndFileToRequest(req); if (!isNonEmptyArray(req.data?.items)) { return ResponseUnprocessable(); } const deleteItems = async ()=>{ return await payload.delete({ collection: slug, where: { id: { exists: true } }, id: undefined, depth: 0, locale: locale, fallbackLocale: defaultLocale, user, showHiddenFields: true, overrideAccess: true }); }; const insertItem = async (item)=>{ return await payload.create({ collection: slug, data: item, locale: locale, fallbackLocale: defaultLocale, user, showHiddenFields: false, overrideAccess: true }); }; const updateItem = async (item)=>{ return await payload.update({ collection: slug, data: item, id: item.id, depth: 0, locale: locale, fallbackLocale: defaultLocale, user, showHiddenFields: true, overrideAccess: true }); }; const items = req.data.items; if (items && items.length) { try { if (importMode === ImportMode.Replace) { await deleteItems(); } for (const item of items){ if (importMode === ImportMode.Update) { await updateItem(item); } else { await insertItem(item); } } return ResponseSuccess(items); } catch (error) { console.error(`CollectionService.collectionImportPost.${slug}.error`, error); return Response.json({ errors: [ { message: error.message || error.name || error.statusText || 'An error occurred' } ] }, { status: error.status || 500 }); } } else { return Response.json({ message: 'Bad Request' }, { status: HttpStatus.BAD_REQUEST }); } } catch (error) { console.error(`CollectionService.collectionImportPost.${slug}.error`, error); return ResponseError(error); } } }); /** * Decorate Mixer context. */ export const beforeOperationHook = async ({ // collection, // context, // operation, args, req })=>{ if (typeof req.query.market === 'string' && typeof req.query.locale === 'string' && !req.context.mixer) { // console.log('beforeReadHook.setMixerContext', req.query.market, req.query.locale); req.context.mixer = true; await setMixerContext(req, req.query.market, req.query.locale); } return args; }; /** * Decorate record with static collection data. */ export const afterCollectionReadHook = (collectionConfig)=>async ({ doc, req, context, findMany })=>{ const { query = {} } = req; // console.log('afterCollectionReadHook.req', req); // console.log('afterCollectionReadHook.query', query); const { locale, market } = query; // console.log('afterCollectionReadHook', locale, market, depth); if (typeof locale === 'string' && typeof market === 'string') { const withSchema = await decorateSchema_(doc, collectionConfig.slug); const withRichText = await decorateRichText_(withSchema, collectionConfig.fields, context, req.payload.config); return withRichText; } else { return doc; } }; /** * !!! removed from v3 * Enforce unique field values during duplication. export const beforeDuplicateCollectionHook: BeforeDuplicate = async ({ data, collection, }) => { data = { ...data, }; collection.fields.forEach(field => { if (isDataField(field) && field.unique) { data[field.name] = `${data[field.name]}-${uuid()}`; } }); return data; }; */ //# sourceMappingURL=collection.service.js.map