mirror of
https://github.com/strapi/strapi.git
synced 2025-12-26 14:44:31 +00:00
Fix ability to update unique constraints in mongoose (#5612)
* Sync indexes on model mount with mongoose Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu> * Use Mongoose.Model.syncIndexes callback arg to subscribe to Model's indexes messages. Get rid off errors-pron-promises. Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu>
This commit is contained in:
parent
9137233bea
commit
4a8de29100
@ -15,8 +15,7 @@ const isPolymorphicAssoc = assoc => {
|
||||
module.exports = ({ models, target }, ctx) => {
|
||||
const { instance } = ctx;
|
||||
|
||||
// Parse every authenticated model.
|
||||
Object.keys(models).map(model => {
|
||||
function mountModel(model) {
|
||||
const definition = models[model];
|
||||
definition.orm = 'mongoose';
|
||||
definition.associations = [];
|
||||
@ -34,20 +33,13 @@ module.exports = ({ models, target }, ctx) => {
|
||||
|
||||
const scalarAttributes = Object.keys(definition.attributes).filter(key => {
|
||||
const { type } = definition.attributes[key];
|
||||
return (
|
||||
type !== undefined &&
|
||||
type !== null &&
|
||||
type !== 'component' &&
|
||||
type !== 'dynamiczone'
|
||||
);
|
||||
return type !== undefined && type !== null && type !== 'component' && type !== 'dynamiczone';
|
||||
});
|
||||
|
||||
const relationalAttributes = Object.keys(definition.attributes).filter(
|
||||
key => {
|
||||
const { type } = definition.attributes[key];
|
||||
return type === undefined;
|
||||
}
|
||||
);
|
||||
const relationalAttributes = Object.keys(definition.attributes).filter(key => {
|
||||
const { type } = definition.attributes[key];
|
||||
return type === undefined;
|
||||
});
|
||||
|
||||
// handle component and dynamic zone attrs
|
||||
if (componentAttributes.length > 0) {
|
||||
@ -103,12 +95,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
save: 'beforeSave',
|
||||
};
|
||||
|
||||
const findLifecycles = [
|
||||
'find',
|
||||
'findOne',
|
||||
'findOneAndUpdate',
|
||||
'findOneAndRemove',
|
||||
];
|
||||
const findLifecycles = ['find', 'findOne', 'findOneAndUpdate', 'findOneAndRemove'];
|
||||
|
||||
/*
|
||||
Override populate path for polymorphic association.
|
||||
@ -116,9 +103,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
instead of Upload.find().populate('related.item')
|
||||
*/
|
||||
|
||||
const morphAssociations = definition.associations.filter(
|
||||
isPolymorphicAssoc
|
||||
);
|
||||
const morphAssociations = definition.associations.filter(isPolymorphicAssoc);
|
||||
|
||||
const populateFn = createOnFetchPopulateFn({
|
||||
componentAttributes,
|
||||
@ -189,10 +174,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
// Use provided timestamps if the elemnets in the array are string else use default.
|
||||
const timestampsOption = _.get(definition, 'options.timestamps', true);
|
||||
if (_.isArray(timestampsOption)) {
|
||||
const [
|
||||
createAtCol = 'createdAt',
|
||||
updatedAtCol = 'updatedAt',
|
||||
] = timestampsOption;
|
||||
const [createAtCol = 'createdAt', updatedAtCol = 'updatedAt'] = timestampsOption;
|
||||
|
||||
schema.set('timestamps', {
|
||||
createdAt: createAtCol,
|
||||
@ -217,16 +199,12 @@ module.exports = ({ models, target }, ctx) => {
|
||||
type: 'timestamp',
|
||||
};
|
||||
}
|
||||
schema.set(
|
||||
'minimize',
|
||||
_.get(definition, 'options.minimize', false) === true
|
||||
);
|
||||
schema.set('minimize', _.get(definition, 'options.minimize', false) === true);
|
||||
|
||||
const refToStrapiRef = obj => {
|
||||
const ref = obj.ref;
|
||||
|
||||
let plainData =
|
||||
ref && typeof ref.toJSON === 'function' ? ref.toJSON() : ref;
|
||||
let plainData = ref && typeof ref.toJSON === 'function' ? ref.toJSON() : ref;
|
||||
|
||||
if (typeof plainData !== 'object') return ref;
|
||||
|
||||
@ -255,17 +233,15 @@ module.exports = ({ models, target }, ctx) => {
|
||||
// Reformat data by bypassing the many-to-many relationship.
|
||||
switch (association.nature) {
|
||||
case 'oneMorphToOne':
|
||||
returned[association.alias] = refToStrapiRef(
|
||||
returned[association.alias][0]
|
||||
);
|
||||
returned[association.alias] = refToStrapiRef(returned[association.alias][0]);
|
||||
|
||||
break;
|
||||
|
||||
case 'manyMorphToMany':
|
||||
case 'manyMorphToOne':
|
||||
returned[association.alias] = returned[
|
||||
association.alias
|
||||
].map(obj => refToStrapiRef(obj));
|
||||
returned[association.alias] = returned[association.alias].map(obj =>
|
||||
refToStrapiRef(obj)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
@ -281,9 +257,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
const components = returned[name].map(el => el.ref);
|
||||
// Reformat data by bypassing the many-to-many relationship.
|
||||
returned[name] =
|
||||
attribute.repeatable === true
|
||||
? components
|
||||
: _.first(components) || null;
|
||||
attribute.repeatable === true ? components : _.first(components) || null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,24 +276,21 @@ module.exports = ({ models, target }, ctx) => {
|
||||
};
|
||||
|
||||
// Instantiate model.
|
||||
const Model = instance.model(
|
||||
definition.globalId,
|
||||
schema,
|
||||
definition.collectionName
|
||||
);
|
||||
const Model = instance.model(definition.globalId, schema, definition.collectionName);
|
||||
|
||||
Model.on('index', error => {
|
||||
if (error) {
|
||||
if (error.code === 11000) {
|
||||
strapi.log.error(
|
||||
`Unique constraint fails, make sure to update your data and restart to apply the unique constraint.\n\t- ${error.message}`
|
||||
);
|
||||
} else {
|
||||
strapi.log.error(
|
||||
`An index error happened, it wasn't applied.\n\t- ${error.message}`
|
||||
);
|
||||
// Ensure indexes are synced with the model, prevent duplicate index errors
|
||||
Model.syncIndexes(null, () => {
|
||||
Model.on('index', error => {
|
||||
if (error) {
|
||||
if (error.code === 11000) {
|
||||
strapi.log.error(
|
||||
`Unique constraint fails, make sure to update your data and restart to apply the unique constraint.\n\t- ${error.message}`
|
||||
);
|
||||
} else {
|
||||
strapi.log.error(`An index error happened, it wasn't applied.\n\t- ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Expose ORM functions through the `target` object.
|
||||
@ -328,14 +299,13 @@ module.exports = ({ models, target }, ctx) => {
|
||||
// Push attributes to be aware of model schema.
|
||||
target[model]._attributes = definition.attributes;
|
||||
target[model].updateRelations = relations.update;
|
||||
});
|
||||
}
|
||||
|
||||
// Parse every authenticated model.
|
||||
Object.keys(models).map(mountModel);
|
||||
};
|
||||
|
||||
const createOnFetchPopulateFn = ({
|
||||
morphAssociations,
|
||||
componentAttributes,
|
||||
definition,
|
||||
}) => {
|
||||
const createOnFetchPopulateFn = ({ morphAssociations, componentAttributes, definition }) => {
|
||||
return function() {
|
||||
const populatedPaths = this.getPopulatedPaths();
|
||||
|
||||
@ -378,16 +348,10 @@ const createOnFetchPopulateFn = ({
|
||||
|
||||
const buildRelation = ({ definition, model, instance, attribute, name }) => {
|
||||
const { nature, verbose } =
|
||||
utilsModels.getNature(attribute, name, undefined, model.toLowerCase()) ||
|
||||
{};
|
||||
utilsModels.getNature(attribute, name, undefined, model.toLowerCase()) || {};
|
||||
|
||||
// Build associations key
|
||||
utilsModels.defineAssociations(
|
||||
model.toLowerCase(),
|
||||
definition,
|
||||
attribute,
|
||||
name
|
||||
);
|
||||
utilsModels.defineAssociations(model.toLowerCase(), definition, attribute, name);
|
||||
|
||||
switch (verbose) {
|
||||
case 'hasOne': {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user