Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
206 lines
5.8 KiB
Plaintext
206 lines
5.8 KiB
Plaintext
import { canAccessAdmin, formatErrors, UnauthorizedError } from 'payload';
|
|
import { getSelectMode, reduceFieldsToValues } from 'payload/shared';
|
|
import { fieldSchemasToFormState } from '../forms/fieldSchemasToFormState/index.js';
|
|
import { renderField } from '../forms/fieldSchemasToFormState/renderField.js';
|
|
import { getClientConfig } from './getClientConfig.js';
|
|
import { getClientSchemaMap } from './getClientSchemaMap.js';
|
|
import { getSchemaMap } from './getSchemaMap.js';
|
|
import { handleFormStateLocking } from './handleFormStateLocking.js';
|
|
import { handleLivePreview } from './handleLivePreview.js';
|
|
import { handlePreview } from './handlePreview.js';
|
|
export const buildFormStateHandler = async args => {
|
|
const {
|
|
req
|
|
} = args;
|
|
try {
|
|
await canAccessAdmin({
|
|
req
|
|
});
|
|
const res = await buildFormState(args);
|
|
return res;
|
|
} catch (err) {
|
|
req.payload.logger.error({
|
|
err,
|
|
msg: `There was an error building form state`
|
|
});
|
|
if (err.message === 'Could not find field schema for given path') {
|
|
return {
|
|
message: err.message
|
|
};
|
|
}
|
|
if (err.message === 'Unauthorized') {
|
|
throw new UnauthorizedError();
|
|
}
|
|
return formatErrors(err);
|
|
}
|
|
};
|
|
export const buildFormState = async args => {
|
|
const {
|
|
id: idFromArgs,
|
|
collectionSlug,
|
|
data: incomingData,
|
|
docPermissions,
|
|
docPreferences,
|
|
documentFormState,
|
|
formState,
|
|
globalSlug,
|
|
initialBlockData,
|
|
initialBlockFormState,
|
|
mockRSCs,
|
|
operation,
|
|
readOnly,
|
|
renderAllFields,
|
|
req,
|
|
req: {
|
|
i18n,
|
|
payload,
|
|
payload: {
|
|
config
|
|
}
|
|
},
|
|
returnLivePreviewURL,
|
|
returnLockStatus,
|
|
returnPreviewURL,
|
|
schemaPath = collectionSlug || globalSlug,
|
|
select,
|
|
skipClientConfigAuth,
|
|
skipValidation,
|
|
updateLastEdited
|
|
} = args;
|
|
const selectMode = select ? getSelectMode(select) : undefined;
|
|
if (!collectionSlug && !globalSlug) {
|
|
throw new Error('Either collectionSlug or globalSlug must be provided');
|
|
}
|
|
const schemaMap = getSchemaMap({
|
|
collectionSlug,
|
|
config,
|
|
globalSlug,
|
|
i18n
|
|
});
|
|
const clientSchemaMap = getClientSchemaMap({
|
|
collectionSlug,
|
|
config: getClientConfig({
|
|
config,
|
|
i18n,
|
|
importMap: req.payload.importMap,
|
|
user: skipClientConfigAuth ? true : req.user
|
|
}),
|
|
globalSlug,
|
|
i18n,
|
|
payload,
|
|
schemaMap
|
|
});
|
|
const id = collectionSlug ? idFromArgs : undefined;
|
|
const fieldOrEntityConfig = schemaMap.get(schemaPath);
|
|
if (!fieldOrEntityConfig) {
|
|
throw new Error(`Could not find "${schemaPath}" in the fieldSchemaMap`);
|
|
}
|
|
if ((!('fields' in fieldOrEntityConfig) || !fieldOrEntityConfig.fields || !fieldOrEntityConfig.fields.length) && 'type' in fieldOrEntityConfig && fieldOrEntityConfig.type !== 'blocks') {
|
|
throw new Error(`The field found in fieldSchemaMap for "${schemaPath}" does not contain any subfields.`);
|
|
}
|
|
// If there is form state but no data, deduce data from that form state, e.g. on initial load
|
|
// Otherwise, use the incoming data as the source of truth, e.g. on subsequent saves
|
|
const data = incomingData || reduceFieldsToValues(formState, true);
|
|
let documentData = undefined;
|
|
if (documentFormState) {
|
|
documentData = reduceFieldsToValues(documentFormState, true);
|
|
}
|
|
let blockData = initialBlockData;
|
|
if (initialBlockFormState) {
|
|
blockData = reduceFieldsToValues(initialBlockFormState, true);
|
|
}
|
|
/**
|
|
* When building state for sub schemas we need to adjust:
|
|
* - `fields`
|
|
* - `parentSchemaPath`
|
|
* - `parentPath`
|
|
*
|
|
* Type assertion is fine because we wrap sub schemas in an array
|
|
* so we can safely map over them within `fieldSchemasToFormState`
|
|
*/
|
|
const fields = Array.isArray(fieldOrEntityConfig) ? fieldOrEntityConfig : 'fields' in fieldOrEntityConfig ? fieldOrEntityConfig.fields : [fieldOrEntityConfig];
|
|
// Ensure data.id is present during form state requests, where the data
|
|
// is passed from the client as an argument, without the ID
|
|
if (!data.id && id) {
|
|
data.id = id;
|
|
}
|
|
const formStateResult = await fieldSchemasToFormState({
|
|
id,
|
|
clientFieldSchemaMap: clientSchemaMap,
|
|
collectionSlug,
|
|
data,
|
|
documentData,
|
|
fields,
|
|
fieldSchemaMap: schemaMap,
|
|
initialBlockData: blockData,
|
|
mockRSCs,
|
|
operation,
|
|
permissions: docPermissions?.fields || {},
|
|
preferences: docPreferences || {
|
|
fields: {}
|
|
},
|
|
previousFormState: formState,
|
|
readOnly,
|
|
renderAllFields,
|
|
renderFieldFn: renderField,
|
|
req,
|
|
schemaPath,
|
|
select,
|
|
selectMode,
|
|
skipValidation
|
|
});
|
|
// Maintain form state of auth / upload fields
|
|
if (collectionSlug && formState) {
|
|
if (payload.collections[collectionSlug]?.config?.upload && formState.file) {
|
|
formStateResult.file = formState.file;
|
|
}
|
|
}
|
|
let lockedStateResult;
|
|
if (returnLockStatus) {
|
|
lockedStateResult = await handleFormStateLocking({
|
|
id,
|
|
collectionSlug,
|
|
globalSlug,
|
|
req,
|
|
updateLastEdited
|
|
});
|
|
}
|
|
const res = {
|
|
lockedState: lockedStateResult,
|
|
state: formStateResult
|
|
};
|
|
if (returnLivePreviewURL) {
|
|
const {
|
|
livePreviewURL
|
|
} = await handleLivePreview({
|
|
collectionSlug,
|
|
config,
|
|
data,
|
|
globalSlug,
|
|
req
|
|
});
|
|
// Important: only set this when not undefined,
|
|
// Otherwise it will travel through the network as `$undefined`
|
|
if (livePreviewURL) {
|
|
res.livePreviewURL = livePreviewURL;
|
|
}
|
|
}
|
|
if (returnPreviewURL) {
|
|
const {
|
|
previewURL
|
|
} = await handlePreview({
|
|
collectionSlug,
|
|
config,
|
|
data,
|
|
globalSlug,
|
|
req
|
|
});
|
|
// Important: only set this when not undefined,
|
|
// Otherwise it will travel through the network as `$undefined`
|
|
if (previewURL) {
|
|
res.previewURL = previewURL;
|
|
}
|
|
}
|
|
return res;
|
|
};
|
|
//# sourceMappingURL=buildFormState.js.map |