mirror of
				https://github.com/strapi/strapi.git
				synced 2025-10-31 09:56:44 +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);
 | 
