Support creation

This commit is contained in:
Alexandre Bodin 2019-11-26 09:53:18 +01:00
parent a34f117a4a
commit f82b05dc94
2 changed files with 176 additions and 72 deletions

View File

@ -364,57 +364,60 @@ const createOnFetchPopulateFn = ({
// TODO: handle Dynamic zone // TODO: handle Dynamic zone
componentAttributes.forEach(name => { componentAttributes.forEach(name => {
const attr = definition.attributes[name]; const attr = definition.attributes[name];
const { type } = attr;
const component = strapi.components[attr.component]; if (type === 'component') {
const component = strapi.components[attr.component];
const assocs = (component.associations || []).filter( const assocs = (component.associations || []).filter(
assoc => assoc.autoPopulate === true assoc => assoc.autoPopulate === true
); );
let subpopulates = []; let subpopulates = [];
assocs.forEach(assoc => { assocs.forEach(assoc => {
if (isPolymorphic({ assoc })) { if (isPolymorphic({ assoc })) {
if ( if (
assoc.nature === 'oneToManyMorph' || assoc.nature === 'oneToManyMorph' ||
assoc.nature === 'manyToManyMorph' assoc.nature === 'manyToManyMorph'
) { ) {
subpopulates.push({
path: assoc.alias,
match: {
[`${assoc.via}.${assoc.filter}`]: assoc.alias,
[`${assoc.via}.kind`]: definition.globalId,
},
options: {
sort: '-createdAt',
},
select: undefined,
model: undefined,
_docs: {},
});
} else {
subpopulates.push({ path: `${assoc.alias}.ref`, _docs: {} });
}
} else {
subpopulates.push({ subpopulates.push({
path: assoc.alias, path: assoc.alias,
match: {
[`${assoc.via}.${assoc.filter}`]: assoc.alias,
[`${assoc.via}.kind`]: definition.globalId,
},
options: {
sort: '-createdAt',
},
select: undefined,
model: undefined,
_docs: {}, _docs: {},
}); });
} else {
subpopulates.push({ path: `${assoc.alias}.ref`, _docs: {} });
} }
});
if (
this._mongooseOptions.populate &&
this._mongooseOptions.populate[name]
) {
this._mongooseOptions.populate[name].path = `${name}.ref`;
this._mongooseOptions.populate[name].populate = subpopulates;
} else { } else {
subpopulates.push({ _.set(this._mongooseOptions, ['populate', name], {
path: assoc.alias, path: `${name}.ref`,
populate: subpopulates,
_docs: {}, _docs: {},
}); });
} }
});
if (
this._mongooseOptions.populate &&
this._mongooseOptions.populate[name]
) {
this._mongooseOptions.populate[name].path = `${name}.ref`;
this._mongooseOptions.populate[name].populate = subpopulates;
} else {
_.set(this._mongooseOptions, ['populate', name], {
path: `${name}.ref`,
populate: subpopulates,
_docs: {},
});
} }
}); });

View File

@ -16,9 +16,10 @@ module.exports = ({ model, modelKey, strapi }) => {
_.has(obj, model.primaryKey) ? obj[model.primaryKey] : obj.id; _.has(obj, model.primaryKey) ? obj[model.primaryKey] : obj.id;
const assocKeys = model.associations.map(ast => ast.alias); const assocKeys = model.associations.map(ast => ast.alias);
const componentKeys = Object.keys(model.attributes).filter(key => { const componentKeys = Object.keys(model.attributes).filter(key =>
return model.attributes[key].type === 'component'; ['component', 'dynamiczone'].includes(model.attributes[key].type)
}); );
const excludedKeys = assocKeys.concat(componentKeys); const excludedKeys = assocKeys.concat(componentKeys);
const defaultPopulate = model.associations const defaultPopulate = model.associations
@ -38,49 +39,96 @@ module.exports = ({ model, modelKey, strapi }) => {
for (let key of componentKeys) { for (let key of componentKeys) {
const attr = model.attributes[key]; const attr = model.attributes[key];
const { component, required = false, repeatable = false } = attr; const { type } = attr;
const componentModel = strapi.components[component]; if (type === 'component') {
const { component, required = false, repeatable = false } = attr;
if (required === true && !_.has(values, key)) { const componentModel = strapi.components[component];
const err = new Error(`Component ${key} is required`);
err.status = 400; if (required === true && !_.has(values, key)) {
throw err; const err = new Error(`Component ${key} is required`);
err.status = 400;
throw err;
}
if (!_.has(values, key)) continue;
const componentValue = values[key];
if (repeatable === true) {
validateRepeatableInput(componentValue, { key, ...attr });
const components = await Promise.all(
componentValue.map(value => {
return strapi.query(component).create(value);
})
);
const componentsArr = components.map(componentEntry => ({
kind: componentModel.globalId,
ref: componentEntry,
}));
entry[key] = componentsArr;
await entry.save();
} else {
validateNonRepeatableInput(componentValue, { key, ...attr });
if (componentValue === null) continue;
const componentEntry = await strapi
.query(component)
.create(componentValue);
entry[key] = [
{
kind: componentModel.globalId,
ref: componentEntry,
},
];
await entry.save();
}
} }
if (!_.has(values, key)) continue; if (type === 'dynamiczone') {
const { required = false } = attr;
const componentValue = values[key]; if (required === true && !_.has(values, key)) {
const err = new Error(`Dynamiczone ${key} is required`);
err.status = 400;
throw err;
}
if (repeatable === true) { if (!_.has(values, key)) continue;
validateRepeatableInput(componentValue, { key, ...attr });
const components = await Promise.all( const dynamiczoneValues = values[key];
componentValue.map(value => {
return strapi.query(component).create(value); validateDynamiczoneInput(dynamiczoneValues, { key, ...attr });
const dynamiczones = await Promise.all(
dynamiczoneValues.map(value => {
const component = value.__component;
return strapi
.query(component)
.create(value)
.then(entity => {
return {
__component: value.__component,
entity,
};
});
}) })
); );
const componentsArr = components.map(componentEntry => ({ const componentsArr = dynamiczones.map(({ __component, entity }) => {
kind: componentModel.globalId, const componentModel = strapi.components[__component];
ref: componentEntry,
})); return {
kind: componentModel.globalId,
ref: entity,
};
});
entry[key] = componentsArr; entry[key] = componentsArr;
await entry.save(); await entry.save();
} else {
validateNonRepeatableInput(componentValue, { key, ...attr });
if (componentValue === null) continue;
const componentEntry = await strapi
.query(component)
.create(componentValue);
entry[key] = [
{
kind: componentModel.globalId,
ref: componentEntry,
},
];
await entry.save();
} }
} }
} }
@ -454,3 +502,56 @@ function validateNonRepeatableInput(value, { key, required }) {
throw err; throw err;
} }
} }
function validateDynamiczoneInput(
value,
{ key, min, max, components, required }
) {
if (!Array.isArray(value)) {
const err = new Error(`Dynamiczone ${key} is invalid. Expected an array`);
err.status = 400;
throw err;
}
value.forEach(val => {
if (typeof val !== 'object' || Array.isArray(val) || val === null) {
const err = new Error(
`Dynamiczone ${key} has invalid items. Expected each items to be objects`
);
err.status = 400;
throw err;
}
if (!_.has(val, '__component')) {
const err = new Error(
`Dynamiczone ${key} has invalid items. Expected each items to have a valid __component key`
);
err.status = 400;
throw err;
} else if (!components.includes(val.__component)) {
const err = new Error(
`Dynamiczone ${key} has invalid items. Each item must have a __component key that is present in the attribute definition`
);
err.status = 400;
throw err;
}
});
if (
(required === true || (required !== true && value.length > 0)) &&
(min && value.length < min)
) {
const err = new Error(
`Dynamiczone ${key} must contain at least ${min} items`
);
err.status = 400;
throw err;
}
if (max && value.length > max) {
const err = new Error(
`Dynamiczone ${key} must contain at most ${max} items`
);
err.status = 400;
throw err;
}
}