mirror of
https://github.com/strapi/strapi.git
synced 2026-01-05 19:52:15 +00:00
Perf optimizations (#16117)
Co-authored-by: Marc-Roig <marc12info@gmail.com>
This commit is contained in:
parent
011967acca
commit
942e646d28
@ -22,7 +22,6 @@ const createCollectionTypeController = ({ contentType }) => {
|
||||
const sanitizedQuery = await this.sanitizeQuery(ctx);
|
||||
const { results, pagination } = await strapi.service(uid).find(sanitizedQuery);
|
||||
const sanitizedResults = await this.sanitizeOutput(results, ctx);
|
||||
|
||||
return this.transformResponse(sanitizedResults, { pagination });
|
||||
},
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ const createController = ({ contentType }) => {
|
||||
return transformResponse(data, meta, { contentType });
|
||||
},
|
||||
|
||||
sanitizeOutput(data, ctx) {
|
||||
async sanitizeOutput(data, ctx) {
|
||||
const auth = getAuthFromKoaContext(ctx);
|
||||
|
||||
return sanitize.contentAPI.output(data, contentType, { auth });
|
||||
|
||||
@ -6,9 +6,8 @@ const { curry, curryN } = require('lodash/fp');
|
||||
function pipeAsync(...methods) {
|
||||
return async (data) => {
|
||||
let res = data;
|
||||
|
||||
for (const method of methods) {
|
||||
res = await method(res);
|
||||
for (let i = 0; i < methods.length; i += 1) {
|
||||
res = await methods[i](res);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
@ -37,12 +37,16 @@ const createContentAPISanitizers = () => {
|
||||
return pipeAsync(...transforms)(data);
|
||||
};
|
||||
|
||||
const sanitizeOuput = (data, schema, { auth } = {}) => {
|
||||
const sanitizeOutput = async (data, schema, { auth } = {}) => {
|
||||
if (isArray(data)) {
|
||||
return Promise.all(data.map((entry) => sanitizeOuput(entry, schema, { auth })));
|
||||
const res = new Array(data.length);
|
||||
for (let i = 0; i < data.length; i += 1) {
|
||||
res[i] = await sanitizeOutput(data[i], schema, { auth });
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const transforms = [sanitizers.defaultSanitizeOutput(schema)];
|
||||
const transforms = [(data) => sanitizers.defaultSanitizeOutput(schema, data)];
|
||||
|
||||
if (auth) {
|
||||
transforms.push(traverseEntity(visitors.removeRestrictedRelations(auth), { schema }));
|
||||
@ -122,7 +126,7 @@ const createContentAPISanitizers = () => {
|
||||
|
||||
return {
|
||||
input: sanitizeInput,
|
||||
output: sanitizeOuput,
|
||||
output: sanitizeOutput,
|
||||
query: sanitizeQuery,
|
||||
filters: sanitizeFilters,
|
||||
sort: sanitizeSort,
|
||||
|
||||
@ -20,17 +20,20 @@ const {
|
||||
removeMorphToRelations,
|
||||
} = require('./visitors');
|
||||
|
||||
const sanitizePasswords = curry((schema, entity) => {
|
||||
const sanitizePasswords = (schema) => async (entity) => {
|
||||
return traverseEntity(removePassword, { schema }, entity);
|
||||
});
|
||||
};
|
||||
|
||||
const sanitizePrivates = curry((schema, entity) => {
|
||||
return traverseEntity(removePrivate, { schema }, entity);
|
||||
});
|
||||
|
||||
const defaultSanitizeOutput = curry((schema, entity) => {
|
||||
return pipeAsync(sanitizePrivates(schema), sanitizePasswords(schema))(entity);
|
||||
});
|
||||
const defaultSanitizeOutput = async (schema, entity) => {
|
||||
return traverseEntity(
|
||||
(...args) => {
|
||||
removePassword(...args);
|
||||
removePrivate(...args);
|
||||
},
|
||||
{ schema },
|
||||
entity
|
||||
);
|
||||
};
|
||||
|
||||
const defaultSanitizeFilters = curry((schema, filters) => {
|
||||
return pipeAsync(
|
||||
@ -140,7 +143,6 @@ const defaultSanitizePopulate = curry((schema, populate) => {
|
||||
|
||||
module.exports = {
|
||||
sanitizePasswords,
|
||||
sanitizePrivates,
|
||||
defaultSanitizeOutput,
|
||||
defaultSanitizeFilters,
|
||||
defaultSanitizeSort,
|
||||
|
||||
@ -1,6 +1,42 @@
|
||||
'use strict';
|
||||
|
||||
const { cloneDeep, isObject, isArray, isNil, curry } = require('lodash/fp');
|
||||
const { clone, isObject, isArray, isNil, curry } = require('lodash/fp');
|
||||
|
||||
const traverseMorphRelationTarget = async (visitor, path, entry) => {
|
||||
const targetSchema = strapi.getModel(entry.__type);
|
||||
|
||||
const traverseOptions = { schema: targetSchema, path };
|
||||
|
||||
return traverseEntity(visitor, traverseOptions, entry);
|
||||
};
|
||||
|
||||
const traverseRelationTarget = (schema) => async (visitor, path, entry) => {
|
||||
const traverseOptions = { schema, path };
|
||||
|
||||
return traverseEntity(visitor, traverseOptions, entry);
|
||||
};
|
||||
|
||||
const traverseMediaTarget = async (visitor, path, entry) => {
|
||||
const targetSchemaUID = 'plugin::upload.file';
|
||||
const targetSchema = strapi.getModel(targetSchemaUID);
|
||||
|
||||
const traverseOptions = { schema: targetSchema, path };
|
||||
|
||||
return traverseEntity(visitor, traverseOptions, entry);
|
||||
};
|
||||
|
||||
const traverseComponent = async (visitor, path, schema, entry) => {
|
||||
const traverseOptions = { schema, path };
|
||||
|
||||
return traverseEntity(visitor, traverseOptions, entry);
|
||||
};
|
||||
|
||||
const visitDynamicZoneEntry = async (visitor, path, entry) => {
|
||||
const targetSchema = strapi.getModel(entry.__component);
|
||||
const traverseOptions = { schema: targetSchema, path };
|
||||
|
||||
return traverseEntity(visitor, traverseOptions, entry);
|
||||
};
|
||||
|
||||
const traverseEntity = async (visitor, options, entity) => {
|
||||
const { path = { raw: null, attribute: null }, schema } = options;
|
||||
@ -11,9 +47,13 @@ const traverseEntity = async (visitor, options, entity) => {
|
||||
}
|
||||
|
||||
// Don't mutate the original entity object
|
||||
const copy = cloneDeep(entity);
|
||||
// only clone at 1st level as the next level will get clone when traversed
|
||||
const copy = clone(entity);
|
||||
const visitorUtils = createVisitorUtils({ data: copy });
|
||||
|
||||
for (const key of Object.keys(copy)) {
|
||||
const keys = Object.keys(copy);
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
const key = keys[i];
|
||||
// Retrieve the attribute definition associated to the key from the schema
|
||||
const attribute = schema.attributes[key];
|
||||
|
||||
@ -32,7 +72,6 @@ const traverseEntity = async (visitor, options, entity) => {
|
||||
|
||||
// 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);
|
||||
|
||||
@ -52,58 +91,62 @@ const traverseEntity = async (visitor, options, entity) => {
|
||||
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 method = isMorphRelation
|
||||
? traverseMorphRelationTarget
|
||||
: traverseRelationTarget(strapi.getModel(attribute.target));
|
||||
|
||||
const traverseOptions = { schema: targetSchema, path: newPath };
|
||||
if (isArray(value)) {
|
||||
const res = new Array(value.length);
|
||||
for (let i = 0; i < value.length; i += 1) {
|
||||
res[i] = await method(visitor, newPath, value[i]);
|
||||
}
|
||||
copy[key] = res;
|
||||
} else {
|
||||
copy[key] = await method(visitor, newPath, value);
|
||||
}
|
||||
|
||||
return traverseEntity(visitor, traverseOptions, entry);
|
||||
};
|
||||
|
||||
// need to update copy
|
||||
copy[key] = isArray(value)
|
||||
? await Promise.all(value.map(traverseTarget))
|
||||
: await traverseTarget(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
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 (isArray(value)) {
|
||||
const res = new Array(value.length);
|
||||
for (let i = 0; i < value.length; i += 1) {
|
||||
res[i] = await traverseMediaTarget(visitor, newPath, value[i]);
|
||||
}
|
||||
copy[key] = res;
|
||||
} else {
|
||||
copy[key] = await traverseMediaTarget(visitor, newPath, value);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isComponent) {
|
||||
const targetSchema = strapi.getModel(attribute.component);
|
||||
const traverseOptions = { schema: targetSchema, path: newPath };
|
||||
|
||||
const traverseComponent = (entry) => traverseEntity(visitor, traverseOptions, entry);
|
||||
if (isArray(value)) {
|
||||
const res = new Array(value.length);
|
||||
for (let i = 0; i < value.length; i += 1) {
|
||||
res[i] = await traverseComponent(visitor, newPath, targetSchema, value[i]);
|
||||
}
|
||||
copy[key] = res;
|
||||
} else {
|
||||
copy[key] = await traverseComponent(visitor, newPath, targetSchema, value);
|
||||
}
|
||||
|
||||
copy[key] = isArray(value)
|
||||
? await Promise.all(value.map(traverseComponent))
|
||||
: await traverseComponent(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isDynamicZone && isArray(value)) {
|
||||
const visitDynamicZoneEntry = (entry) => {
|
||||
const targetSchema = strapi.getModel(entry.__component);
|
||||
const traverseOptions = { schema: targetSchema, path: newPath };
|
||||
const res = new Array(value.length);
|
||||
for (let i = 0; i < value.length; i += 1) {
|
||||
res[i] = await visitDynamicZoneEntry(visitor, newPath, value[i]);
|
||||
}
|
||||
copy[key] = res;
|
||||
|
||||
return traverseEntity(visitor, traverseOptions, entry);
|
||||
};
|
||||
|
||||
copy[key] = await Promise.all(value.map(visitDynamicZoneEntry));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user