mirror of
https://github.com/strapi/strapi.git
synced 2025-07-26 02:20:32 +00:00
118 lines
3.4 KiB
JavaScript
118 lines
3.4 KiB
JavaScript
'use strict';
|
|
|
|
const { cloneDeep, isObject, isArray, isNil, curry } = require('lodash/fp');
|
|
|
|
const traverseEntity = async (visitor, options, entity) => {
|
|
const { path = null, schema } = options;
|
|
|
|
// End recursion
|
|
if (!isObject(entity) || isNil(schema)) {
|
|
return entity;
|
|
}
|
|
|
|
// Don't mutate the original entity object
|
|
const copy = cloneDeep(entity);
|
|
|
|
for (const key of Object.keys(copy)) {
|
|
// Retrieve the attribute definition associated to the key from the schema
|
|
const attribute = schema.attributes[key];
|
|
|
|
// If the attribute doesn't exist within the schema, ignore it
|
|
if (isNil(attribute)) {
|
|
continue;
|
|
}
|
|
|
|
const newPath = path ? `${path}.${key}` : key;
|
|
|
|
// Visit the current attribute
|
|
const visitorOptions = { data: copy, schema, key, value: copy[key], attribute, path: newPath };
|
|
const visitorUtils = createVisitorUtils({ data: copy });
|
|
|
|
await visitor(visitorOptions, visitorUtils);
|
|
|
|
// Extract the value for the current key (after calling the visitor)
|
|
const value = copy[key];
|
|
|
|
// Ignore Nil values
|
|
if (isNil(value)) {
|
|
continue;
|
|
}
|
|
|
|
const isRelation = attribute.type === 'relation';
|
|
const isComponent = attribute.type === 'component';
|
|
const isDynamicZone = attribute.type === 'dynamiczone';
|
|
const isMedia = attribute.type === 'media';
|
|
|
|
if (isRelation) {
|
|
const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');
|
|
|
|
const traverseTarget = (entry) => {
|
|
// Handle polymorphic relationships
|
|
const targetSchemaUID = isMorphRelation ? entry.__type : attribute.target;
|
|
const targetSchema = strapi.getModel(targetSchemaUID);
|
|
|
|
const traverseOptions = { schema: targetSchema, path: newPath };
|
|
|
|
return traverseEntity(visitor, traverseOptions, entry);
|
|
};
|
|
|
|
// need to update copy
|
|
copy[key] = isArray(value)
|
|
? await Promise.all(value.map(traverseTarget))
|
|
: await traverseTarget(value);
|
|
}
|
|
|
|
if (isMedia) {
|
|
const traverseTarget = (entry) => {
|
|
const targetSchemaUID = 'plugin::upload.file';
|
|
const targetSchema = strapi.getModel(targetSchemaUID);
|
|
|
|
const traverseOptions = { schema: targetSchema, path: newPath };
|
|
|
|
return traverseEntity(visitor, traverseOptions, entry);
|
|
};
|
|
|
|
// need to update copy
|
|
copy[key] = isArray(value)
|
|
? await Promise.all(value.map(traverseTarget))
|
|
: await traverseTarget(value);
|
|
}
|
|
|
|
if (isComponent) {
|
|
const targetSchema = strapi.getModel(attribute.component);
|
|
const traverseOptions = { schema: targetSchema, path: newPath };
|
|
|
|
const traverseComponent = (entry) => traverseEntity(visitor, traverseOptions, entry);
|
|
|
|
copy[key] = isArray(value)
|
|
? await Promise.all(value.map(traverseComponent))
|
|
: await traverseComponent(value);
|
|
}
|
|
|
|
if (isDynamicZone && isArray(value)) {
|
|
const visitDynamicZoneEntry = (entry) => {
|
|
const targetSchema = strapi.getModel(entry.__component);
|
|
const traverseOptions = { schema: targetSchema, path: newPath };
|
|
|
|
return traverseEntity(visitor, traverseOptions, entry);
|
|
};
|
|
|
|
copy[key] = await Promise.all(value.map(visitDynamicZoneEntry));
|
|
}
|
|
}
|
|
|
|
return copy;
|
|
};
|
|
|
|
const createVisitorUtils = ({ data }) => ({
|
|
remove(key) {
|
|
delete data[key];
|
|
},
|
|
|
|
set(key, value) {
|
|
data[key] = value;
|
|
},
|
|
});
|
|
|
|
module.exports = curry(traverseEntity);
|