diff --git a/packages/strapi-admin/controllers/Admin.js b/packages/strapi-admin/controllers/Admin.js index ea5ed8ae12..329c8431ee 100644 --- a/packages/strapi-admin/controllers/Admin.js +++ b/packages/strapi-admin/controllers/Admin.js @@ -13,9 +13,7 @@ const PLUGIN_NAME_REGEX = /^[A-Za-z][A-Za-z0-9-_]+$/; * Validates a plugin name format */ const isValidPluginName = plugin => { - return ( - _.isString(plugin) && !_.isEmpty(plugin) && PLUGIN_NAME_REGEX.test(plugin) - ); + return _.isString(plugin) && !_.isEmpty(plugin) && PLUGIN_NAME_REGEX.test(plugin); }; /** @@ -48,9 +46,7 @@ module.exports = { const strapiVersion = _.get(strapi.config, 'info.strapi', null); return ctx.send({ strapiVersion }); } catch (err) { - return ctx.badRequest(null, [ - { messages: [{ id: 'The version is not available' }] }, - ]); + return ctx.badRequest(null, [{ messages: [{ id: 'The version is not available' }] }]); } }, @@ -68,9 +64,7 @@ module.exports = { return ctx.send({ layout }); } catch (err) { - return ctx.badRequest(null, [ - { messages: [{ id: 'An error occurred' }] }, - ]); + return ctx.badRequest(null, [{ messages: [{ id: 'An error occurred' }] }]); } }, @@ -179,9 +173,7 @@ module.exports = { ); } - const adminsWithSameEmail = await strapi - .query('administrator', 'admin') - .findOne({ email }); + const adminsWithSameEmail = await strapi.query('administrator', 'admin').findOne({ email }); const adminsWithSameUsername = await strapi .query('administrator', 'admin') @@ -264,18 +256,14 @@ module.exports = { }) ); } - const admin = await strapi - .query('administrator', 'admin') - .findOne(ctx.params); + const admin = await strapi.query('administrator', 'admin').findOne({ id }); // check the user exists if (!admin) return ctx.notFound('Administrator not found'); // check there are not user with requested email if (email !== admin.email) { - const adminsWithSameEmail = await strapi - .query('administrator', 'admin') - .findOne({ email }); + const adminsWithSameEmail = await strapi.query('administrator', 'admin').findOne({ email }); if (adminsWithSameEmail && adminsWithSameEmail.id !== admin.id) { return ctx.badRequest( @@ -317,9 +305,7 @@ module.exports = { user.password = await strapi.admin.services.auth.hashPassword(password); } - const data = await strapi - .query('administrator', 'admin') - .update({ id }, user); + const data = await strapi.query('administrator', 'admin').update({ id }, user); // Send 200 `ok` ctx.send(data); diff --git a/packages/strapi-connector-bookshelf/lib/queries.js b/packages/strapi-connector-bookshelf/lib/queries.js index ebbc11ec5d..945928888e 100644 --- a/packages/strapi-connector-bookshelf/lib/queries.js +++ b/packages/strapi-connector-bookshelf/lib/queries.js @@ -4,11 +4,7 @@ */ const _ = require('lodash'); -const { - convertRestQueryParams, - buildQuery, - models: modelUtils, -} = require('strapi-utils'); +const { convertRestQueryParams, buildQuery, models: modelUtils } = require('strapi-utils'); module.exports = function createQueryBuilder({ model, modelKey, strapi }) { /* Utils */ @@ -50,20 +46,8 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { * Find one entry based on params */ async function findOne(params, populate, { transacting } = {}) { - const primaryKey = params[model.primaryKey] || params.id; - - if (primaryKey) { - params = { - [model.primaryKey]: primaryKey, - }; - } - - const entry = await model.where(params).fetch({ - withRelated: populate, - transacting, - }); - - return entry ? entry.toJSON() : null; + const entries = await find({ ...params, _limit: 1 }, populate, { transacting }); + return entries[0] || null; } /** @@ -99,10 +83,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { const entry = await model.forge(data).save(null, { transacting: trx }); await createComponents(entry, values, { transacting: trx }); - return model.updateRelations( - { id: entry.id, values: relations }, - { transacting: trx } - ); + return model.updateRelations({ id: entry.id, values: relations }, { transacting: trx }); }; return wrapTransaction(runCreate, { transacting }); @@ -133,10 +114,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { await updateComponents(updatedEntry, values, { transacting: trx }); if (Object.keys(relations).length > 0) { - return model.updateRelations( - { id: entry.id, values: relations }, - { transacting: trx } - ); + return model.updateRelations({ id: entry.id, values: relations }, { transacting: trx }); } return this.findOne(params, null, { transacting: trx }); @@ -145,8 +123,8 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { return wrapTransaction(runUpdate, { transacting }); } - async function deleteOne(params, { transacting } = {}) { - const entry = await model.where(params).fetch({ transacting }); + async function deleteOne(id, { transacting } = {}) { + const entry = await model.where({ [model.primaryKey]: id }).fetch({ transacting }); if (!entry) { const err = new Error('entry.notFound'); @@ -173,13 +151,11 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { } }); - await model.updateRelations({ ...params, values }, { transacting }); + await model.updateRelations({ [model.primaryKey]: id, values }, { transacting }); const runDelete = async trx => { await deleteComponents(entry, { transacting: trx }); - await model - .where({ id: entry.id }) - .destroy({ transacting: trx, require: false }); + await model.where({ id: entry.id }).destroy({ transacting: trx, require: false }); return entry.toJSON(); }; @@ -187,14 +163,16 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { } async function deleteMany(params, { transacting } = {}) { - const primaryKey = params[model.primaryKey] || params.id; - - if (primaryKey) return deleteOne(params, { transacting }); + if (params[model.primaryKey]) { + const entries = await find({ ...params, _limit: 1 }, null, { transacting }); + if (entries.length > 0) { + return deleteOne(entries[0][model.primaryKey], { transacting }); + } + return null; + } const entries = await find(params, null, { transacting }); - return await Promise.all( - entries.map(entry => deleteOne({ id: entry.id }, { transacting })) - ); + return Promise.all(entries.map(entry => deleteOne(entry.id, { transacting }))); } function search(params, populate) { @@ -237,12 +215,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { const joinModel = model.componentsJoinModel; const { foreignKey } = joinModel; - const createComponentAndLink = async ({ - componentModel, - value, - key, - order, - }) => { + const createComponentAndLink = async ({ componentModel, value, key, order }) => { return strapi .query(componentModel.uid) .create(value, { transacting }) @@ -343,12 +316,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { const joinModel = model.componentsJoinModel; const { foreignKey } = joinModel; - const updateOrCreateComponentAndLink = async ({ - componentModel, - key, - value, - order, - }) => { + const updateOrCreateComponentAndLink = async ({ componentModel, key, value, order }) => { // check if value has an id then update else create if (_.has(value, componentModel.primaryKey)) { return strapi @@ -481,11 +449,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { return; } - async function deleteDynamicZoneOldComponents( - entry, - values, - { key, joinModel, transacting } - ) { + async function deleteDynamicZoneOldComponents(entry, values, { key, joinModel, transacting }) { const idsToKeep = values.reduce((acc, value) => { const component = value.__component; const componentModel = strapi.components[component]; @@ -506,8 +470,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { .fetchAll({ transacting }) .map(el => { const componentKey = Object.keys(strapi.components).find( - key => - strapi.components[key].collectionName === el.get('component_type') + key => strapi.components[key].collectionName === el.get('component_type') ); return { @@ -518,9 +481,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { // verify the provided ids are realted to this entity. idsToKeep.forEach(({ id, component }) => { - if ( - !allIds.find(el => el.id === id && el.component.uid === component.uid) - ) { + if (!allIds.find(el => el.id === id && el.component.uid === component.uid)) { const err = new Error( `Some of the provided components in ${key} are not related to the entity` ); @@ -530,11 +491,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { }); const idsToDelete = allIds.reduce((acc, { id, component }) => { - if ( - !idsToKeep.find( - el => el.id === id && el.component.uid === component.uid - ) - ) { + if (!idsToKeep.find(el => el.id === id && el.component.uid === component.uid)) { acc.push({ id, component, @@ -550,10 +507,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { qb.where(qb => { idsToDelete.forEach(({ id, component }) => { qb.orWhere(qb => { - qb.where('component_id', id).andWhere( - 'component_type', - component.collectionName - ); + qb.where('component_id', id).andWhere('component_type', component.collectionName); }); }); }); @@ -573,9 +527,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { componentValue, { key, joinModel, componentModel, transacting } ) { - const componentArr = Array.isArray(componentValue) - ? componentValue - : [componentValue]; + const componentArr = Array.isArray(componentValue) ? componentValue : [componentValue]; const idsToKeep = componentArr .filter(el => _.has(el, componentModel.primaryKey)) @@ -603,17 +555,12 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { const idsToDelete = _.difference(allIds, idsToKeep); if (idsToDelete.length > 0) { await joinModel - .query(qb => - qb.whereIn('component_id', idsToDelete).andWhere('field', key) - ) + .query(qb => qb.whereIn('component_id', idsToDelete).andWhere('field', key)) .destroy({ transacting, require: false }); await strapi .query(componentModel.uid) - .delete( - { [`${componentModel.primaryKey}_in`]: idsToDelete }, - { transacting } - ); + .delete({ [`${componentModel.primaryKey}_in`]: idsToDelete }, { transacting }); } } @@ -643,10 +590,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { await strapi .query(componentModel.uid) - .delete( - { [`${componentModel.primaryKey}_in`]: ids }, - { transacting } - ); + .delete({ [`${componentModel.primaryKey}_in`]: ids }, { transacting }); await joinModel .where({ @@ -674,9 +618,7 @@ module.exports = function createQueryBuilder({ model, modelKey, strapi }) { const { uid, collectionName } = strapi.components[compo]; const model = strapi.query(uid); - const toDelete = componentJoins.filter( - el => el.componentType === collectionName - ); + const toDelete = componentJoins.filter(el => el.componentType === collectionName); if (toDelete.length > 0) { await model.delete( @@ -725,33 +667,18 @@ const buildSearchQuery = (qb, model, params) => { const associations = model.associations.map(x => x.alias); const searchText = Object.keys(model._attributes) - .filter( - attribute => - attribute !== model.primaryKey && !associations.includes(attribute) - ) - .filter(attribute => - ['string', 'text'].includes(model._attributes[attribute].type) - ); + .filter(attribute => attribute !== model.primaryKey && !associations.includes(attribute)) + .filter(attribute => ['string', 'text'].includes(model._attributes[attribute].type)); const searchInt = Object.keys(model._attributes) - .filter( - attribute => - attribute !== model.primaryKey && !associations.includes(attribute) - ) + .filter(attribute => attribute !== model.primaryKey && !associations.includes(attribute)) .filter(attribute => - ['integer', 'decimal', 'float'].includes( - model._attributes[attribute].type - ) + ['integer', 'decimal', 'float'].includes(model._attributes[attribute].type) ); const searchBool = Object.keys(model._attributes) - .filter( - attribute => - attribute !== model.primaryKey && !associations.includes(attribute) - ) - .filter(attribute => - ['boolean'].includes(model._attributes[attribute].type) - ); + .filter(attribute => attribute !== model.primaryKey && !associations.includes(attribute)) + .filter(attribute => ['boolean'].includes(model._attributes[attribute].type)); if (!_.isNaN(_.toNumber(query))) { searchInt.forEach(attribute => { @@ -768,10 +695,7 @@ const buildSearchQuery = (qb, model, params) => { // Search in columns with text using index. switch (model.client) { case 'mysql': - qb.orWhereRaw( - `MATCH(${searchText.join(',')}) AGAINST(? IN BOOLEAN MODE)`, - `*${query}*` - ); + qb.orWhereRaw(`MATCH(${searchText.join(',')}) AGAINST(? IN BOOLEAN MODE)`, `*${query}*`); break; case 'pg': { const searchQuery = searchText.map(attribute => @@ -803,13 +727,8 @@ function validateRepeatableInput(value, { key, min, max, required }) { } }); - if ( - (required === true || (required !== true && value.length > 0)) && - (min && value.length < min) - ) { - const err = new Error( - `Component ${key} must contain at least ${min} items` - ); + if ((required === true || (required !== true && value.length > 0)) && min && value.length < min) { + const err = new Error(`Component ${key} must contain at least ${min} items`); err.status = 400; throw err; } @@ -835,10 +754,7 @@ function validateNonRepeatableInput(value, { key, required }) { } } -function validateDynamiczoneInput( - value, - { key, min, max, components, required } -) { +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; @@ -869,20 +785,13 @@ function validateDynamiczoneInput( } }); - if ( - (required === true || (required !== true && value.length > 0)) && - (min && value.length < min) - ) { - const err = new Error( - `Dynamiczone ${key} must contain at least ${min} items` - ); + 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` - ); + const err = new Error(`Dynamiczone ${key} must contain at most ${max} items`); err.status = 400; throw err; } diff --git a/packages/strapi-connector-mongoose/lib/queries.js b/packages/strapi-connector-mongoose/lib/queries.js index 1b47a17654..c69a11ec61 100644 --- a/packages/strapi-connector-mongoose/lib/queries.js +++ b/packages/strapi-connector-mongoose/lib/queries.js @@ -4,17 +4,12 @@ */ const _ = require('lodash'); -const { - convertRestQueryParams, - buildQuery, - models: modelUtils, -} = require('strapi-utils'); +const { convertRestQueryParams, buildQuery, models: modelUtils } = require('strapi-utils'); const { findComponentByGlobalId } = require('./utils/helpers'); const hasPK = (obj, model) => _.has(obj, model.primaryKey) || _.has(obj, 'id'); -const getPK = (obj, model) => - _.has(obj, model.primaryKey) ? obj[model.primaryKey] : obj.id; +const getPK = (obj, model) => (_.has(obj, model.primaryKey) ? obj[model.primaryKey] : obj.id); module.exports = ({ model, modelKey, strapi }) => { const assocKeys = model.associations.map(ast => ast.alias); @@ -77,9 +72,7 @@ module.exports = ({ model, modelKey, strapi }) => { validateNonRepeatableInput(componentValue, { key, ...attr }); if (componentValue === null) continue; - const componentEntry = await strapi - .query(component) - .create(componentValue); + const componentEntry = await strapi.query(component).create(componentValue); entry[key] = [ { kind: componentModel.globalId, @@ -174,9 +167,7 @@ module.exports = ({ model, modelKey, strapi }) => { }); const components = await Promise.all( - componentValue.map(value => - updateOrCreateComponent({ componentUID, value }) - ) + componentValue.map(value => updateOrCreateComponent({ componentUID, value })) ); const componentsArr = components.map(component => ({ kind: componentModel.globalId, @@ -222,14 +213,12 @@ module.exports = ({ model, modelKey, strapi }) => { const dynamiczones = await Promise.all( dynamiczoneValues.map(value => { const componentUID = value.__component; - return updateOrCreateComponent({ componentUID, value }).then( - entity => { - return { - componentUID, - entity, - }; - } - ); + return updateOrCreateComponent({ componentUID, value }).then(entity => { + return { + componentUID, + entity, + }; + }); }) ); @@ -273,9 +262,7 @@ module.exports = ({ model, modelKey, strapi }) => { // verify the provided ids are realted to this entity. idsToKeep.forEach(({ id, componentUID }) => { - if ( - !allIds.find(el => el.id === id && el.componentUID === componentUID) - ) { + if (!allIds.find(el => el.id === id && el.componentUID === componentUID)) { const err = new Error( `Some of the provided components in ${key} are not related to the entity` ); @@ -285,9 +272,7 @@ module.exports = ({ model, modelKey, strapi }) => { }); const idsToDelete = allIds.reduce((acc, { id, componentUID }) => { - if ( - !idsToKeep.find(el => el.id === id && el.componentUID === componentUID) - ) { + if (!idsToKeep.find(el => el.id === id && el.componentUID === componentUID)) { acc.push({ id, componentUID, @@ -317,14 +302,8 @@ module.exports = ({ model, modelKey, strapi }) => { } } - async function deleteOldComponents( - entry, - componentValue, - { key, componentModel } - ) { - const componentArr = Array.isArray(componentValue) - ? componentValue - : [componentValue]; + async function deleteOldComponents(entry, componentValue, { key, componentModel }) { + const componentArr = Array.isArray(componentValue) ? componentValue : [componentValue]; const idsToKeep = componentArr .filter(val => hasPK(val, componentModel)) @@ -335,7 +314,7 @@ module.exports = ({ model, modelKey, strapi }) => { .filter(el => el.ref) .map(el => el.ref._id); - // verify the provided ids are realted to this entity. + // verify the provided ids are related to this entity. idsToKeep.forEach(id => { if (allIds.findIndex(currentId => currentId.toString() === id) === -1) { const err = new Error( @@ -352,9 +331,7 @@ module.exports = ({ model, modelKey, strapi }) => { }, []); if (idsToDelete.length > 0) { - await strapi - .query(componentModel.uid) - .delete({ [`${model.primaryKey}_in`]: idsToDelete }); + await strapi.query(componentModel.uid).delete({ [`${model.primaryKey}_in`]: idsToDelete }); } } @@ -415,25 +392,12 @@ module.exports = ({ model, modelKey, strapi }) => { model, filters, populate: populateOpt, - }).then(results => - results.map(result => (result ? result.toObject() : null)) - ); + }).then(results => results.map(result => (result ? result.toObject() : null))); } async function findOne(params, populate) { - const primaryKey = getPK(params, model); - - if (primaryKey) { - params = { - [model.primaryKey]: primaryKey, - }; - } - - const entry = await model - .findOne(params) - .populate(populate || defaultPopulate); - - return entry ? entry.toObject() : null; + const entries = await find({ ...params, _limit: 1 }, populate); + return entries[0] || null; } function count(params) { @@ -463,14 +427,6 @@ module.exports = ({ model, modelKey, strapi }) => { } async function update(params, values) { - const primaryKey = getPK(params, model); - - if (primaryKey) { - params = { - [model.primaryKey]: primaryKey, - }; - } - const entry = await model.findOne(params); if (!entry) { @@ -493,17 +449,21 @@ module.exports = ({ model, modelKey, strapi }) => { } async function deleteMany(params) { - const primaryKey = getPK(params, model); - - if (primaryKey) return deleteOne(params); + if (params[model.primaryKey]) { + const entries = await find({ ...params, _limit: 1 }); + if (entries.length > 0) { + return deleteOne(entries[0][model.primaryKey]); + } + return null; + } const entries = await find(params); - return await Promise.all(entries.map(entry => deleteOne({ id: entry.id }))); + return Promise.all(entries.map(entry => deleteOne(entry[model.primaryKey]))); } - async function deleteOne(params) { + async function deleteOne(id) { const entry = await model - .findOneAndRemove({ [model.primaryKey]: getPK(params, model) }) + .findOneAndRemove({ [model.primaryKey]: id }) .populate(defaultPopulate); if (!entry) { @@ -521,21 +481,17 @@ module.exports = ({ model, modelKey, strapi }) => { } const search = - _.endsWith(association.nature, 'One') || - association.nature === 'oneToMany' + _.endsWith(association.nature, 'One') || association.nature === 'oneToMany' ? { [association.via]: entry._id } : { [association.via]: { $in: [entry._id] } }; const update = - _.endsWith(association.nature, 'One') || - association.nature === 'oneToMany' + _.endsWith(association.nature, 'One') || association.nature === 'oneToMany' ? { [association.via]: null } : { $pull: { [association.via]: entry._id } }; // Retrieve model. const model = association.plugin - ? strapi.plugins[association.plugin].models[ - association.model || association.collection - ] + ? strapi.plugins[association.plugin].models[association.model || association.collection] : strapi.models[association.model || association.collection]; return model.updateMany(search, update); @@ -557,9 +513,7 @@ module.exports = ({ model, modelKey, strapi }) => { .skip(filters.start) .limit(filters.limit) .populate(populate || defaultPopulate) - .then(results => - results.map(result => (result ? result.toObject() : null)) - ); + .then(results => results.map(result => (result ? result.toObject() : null))); } function countSearch(params) { @@ -623,13 +577,8 @@ function validateRepeatableInput(value, { key, min, max, required }) { } }); - if ( - (required === true || (required !== true && value.length > 0)) && - (min && value.length < min) - ) { - const err = new Error( - `Component ${key} must contain at least ${min} items` - ); + if ((required === true || (required !== true && value.length > 0)) && min && value.length < min) { + const err = new Error(`Component ${key} must contain at least ${min} items`); err.status = 400; throw err; } @@ -655,10 +604,7 @@ function validateNonRepeatableInput(value, { key, required }) { } } -function validateDynamiczoneInput( - value, - { key, min, max, components, required } -) { +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; @@ -689,20 +635,13 @@ function validateDynamiczoneInput( } }); - if ( - (required === true || (required !== true && value.length > 0)) && - (min && value.length < min) - ) { - const err = new Error( - `Dynamiczone ${key} must contain at least ${min} items` - ); + 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` - ); + const err = new Error(`Dynamiczone ${key} must contain at most ${max} items`); err.status = 400; throw err; } diff --git a/packages/strapi-database/lib/queries/create-query.js b/packages/strapi-database/lib/queries/create-query.js index 9e966c0200..17c41c418f 100644 --- a/packages/strapi-database/lib/queries/create-query.js +++ b/packages/strapi-database/lib/queries/create-query.js @@ -1,5 +1,7 @@ 'use strict'; +const { replaceIdByPrimaryKey } = require('../utils/primary-key'); + module.exports = function createQuery(opts) { return new Query(opts); }; @@ -35,43 +37,49 @@ class Query { } if (typeof mapping[this.orm] !== 'function') { - throw new Error( - `Custom queries must be functions received ${typeof mapping[this.orm]}` - ); + throw new Error(`Custom queries must be functions received ${typeof mapping[this.orm]}`); } return mapping[this.model.orm].call(this, { model: this.model }); } - find(...args) { - return this.connectorQuery.find(...args); + find(params = {}, ...args) { + const newParams = replaceIdByPrimaryKey(params, this.model); + return this.connectorQuery.find(newParams, ...args); } - findOne(...args) { - return this.connectorQuery.findOne(...args); + findOne(params = {}, ...args) { + const newParams = replaceIdByPrimaryKey(params, this.model); + return this.connectorQuery.findOne(newParams, ...args); } - create(...args) { - return this.connectorQuery.create(...args); + create(params = {}, ...args) { + const newParams = replaceIdByPrimaryKey(params, this.model); + return this.connectorQuery.create(newParams, ...args); } - update(...args) { - return this.connectorQuery.update(...args); + update(params = {}, ...args) { + const newParams = replaceIdByPrimaryKey(params, this.model); + return this.connectorQuery.update(newParams, ...args); } - delete(...args) { - return this.connectorQuery.delete(...args); + delete(params = {}, ...args) { + const newParams = replaceIdByPrimaryKey(params, this.model); + return this.connectorQuery.delete(newParams, ...args); } - count(...args) { - return this.connectorQuery.count(...args); + count(params = {}, ...args) { + const newParams = replaceIdByPrimaryKey(params, this.model); + return this.connectorQuery.count(newParams, ...args); } - search(...args) { - return this.connectorQuery.search(...args); + search(params = {}, ...args) { + const newParams = replaceIdByPrimaryKey(params, this.model); + return this.connectorQuery.search(newParams, ...args); } - countSearch(...args) { - return this.connectorQuery.countSearch(...args); + countSearch(params = {}, ...args) { + const newParams = replaceIdByPrimaryKey(params, this.model); + return this.connectorQuery.countSearch(newParams, ...args); } } diff --git a/packages/strapi-database/lib/utils/__tests__/primary-keys.test.js b/packages/strapi-database/lib/utils/__tests__/primary-keys.test.js new file mode 100644 index 0000000000..a6a1a2eb92 --- /dev/null +++ b/packages/strapi-database/lib/utils/__tests__/primary-keys.test.js @@ -0,0 +1,53 @@ +const { replaceIdByPrimaryKey } = require('../primary-key'); + +describe('Primary Key', () => { + describe('replaceIdByPrimaryKey', () => { + const defaultPostgresModel = { primaryKey: 'id' }; + const defaultMongooseModel = { primaryKey: '_id' }; + const customModel = { primaryKey: 'aRandomPrimaryKey' }; + + describe('Model primary key is "id"', () => { + test('Params has "id"', () => { + const result = replaceIdByPrimaryKey({ id: '123', color: 'red' }, defaultPostgresModel); + expect(result).toEqual({ id: '123', color: 'red' }); + }); + test(`Params doesn't have "id"`, () => { + const result = replaceIdByPrimaryKey({ color: 'red' }, defaultPostgresModel); + expect(result).toEqual({ color: 'red' }); + }); + }); + + describe('Model primary key is "_id"', () => { + test('Params has "_id"', () => { + const result = replaceIdByPrimaryKey({ _id: '123', color: 'red' }, defaultMongooseModel); + expect(result).toEqual({ _id: '123', color: 'red' }); + }); + test('Params has "id"', () => { + const result = replaceIdByPrimaryKey({ id: '123', color: 'red' }, defaultMongooseModel); + expect(result).toEqual({ _id: '123', color: 'red' }); + }); + test(`Params doesn't have "id" nor "_id"`, () => { + const result = replaceIdByPrimaryKey({ color: 'red' }, defaultMongooseModel); + expect(result).toEqual({ color: 'red' }); + }); + }); + + describe('Model primary key is "aRandomPrimaryKey"', () => { + test('Params has "id"', () => { + const result = replaceIdByPrimaryKey({ id: '123', color: 'red' }, customModel); + expect(result).toEqual({ aRandomPrimaryKey: '123', color: 'red' }); + }); + test('Params has "aRandomPrimaryKey"', () => { + const result = replaceIdByPrimaryKey( + { aRandomPrimaryKey: '123', color: 'red' }, + customModel + ); + expect(result).toEqual({ aRandomPrimaryKey: '123', color: 'red' }); + }); + test(`Params doesn't have "id" nor "aRandomPrimaryKey"`, () => { + const result = replaceIdByPrimaryKey({ color: 'red' }, customModel); + expect(result).toEqual({ color: 'red' }); + }); + }); + }); +}); diff --git a/packages/strapi-database/lib/utils/primary-key.js b/packages/strapi-database/lib/utils/primary-key.js new file mode 100644 index 0000000000..30e0f0b2c9 --- /dev/null +++ b/packages/strapi-database/lib/utils/primary-key.js @@ -0,0 +1,19 @@ +'use strict'; + +const _ = require('lodash'); + +/** + * If exists, rename the key "id" by the primary key name of the model ("_id" by default for mongoose). + */ +const replaceIdByPrimaryKey = (params, model) => { + const newParams = { ...params }; + if (_.has(params, 'id')) { + delete newParams.id; + newParams[model.primaryKey] = params[model.primaryKey] || params.id; + } + return newParams; +}; + +module.exports = { + replaceIdByPrimaryKey, +}; diff --git a/packages/strapi-plugin-content-manager/controllers/ContentManager.js b/packages/strapi-plugin-content-manager/controllers/ContentManager.js index 94d0c1d5d9..af66b85b76 100644 --- a/packages/strapi-plugin-content-manager/controllers/ContentManager.js +++ b/packages/strapi-plugin-content-manager/controllers/ContentManager.js @@ -45,13 +45,14 @@ module.exports = { * Returns a list of entities of a content-type matching the query parameters */ async find(ctx) { + const { model } = ctx.params; const contentManagerService = strapi.plugins['content-manager'].services.contentmanager; let entities = []; if (_.has(ctx.request.query, '_q')) { - entities = await contentManagerService.search(ctx.params, ctx.request.query); + entities = await contentManagerService.search({ model }, ctx.request.query); } else { - entities = await contentManagerService.fetchAll(ctx.params, ctx.request.query); + entities = await contentManagerService.fetchAll({ model }, ctx.request.query); } ctx.body = entities; @@ -61,9 +62,10 @@ module.exports = { * Returns an entity of a content type by id */ async findOne(ctx) { + const { model, id } = ctx.params; const contentManagerService = strapi.plugins['content-manager'].services.contentmanager; - const entry = await contentManagerService.fetch(ctx.params); + const entry = await contentManagerService.fetch({ model, id }); // Entry not found if (!entry) { @@ -77,13 +79,14 @@ module.exports = { * Returns a count of entities of a content type matching query parameters */ async count(ctx) { + const { model } = ctx.params; const contentManagerService = strapi.plugins['content-manager'].services.contentmanager; let count; if (_.has(ctx.request.query, '_q')) { - count = await contentManagerService.countSearch(ctx.params, ctx.request.query); + count = await contentManagerService.countSearch({ model }, ctx.request.query); } else { - count = await contentManagerService.count(ctx.params, ctx.request.query); + count = await contentManagerService.count({ model }, ctx.request.query); } ctx.body = { @@ -102,18 +105,13 @@ module.exports = { try { if (ctx.is('multipart')) { const { data, files } = parseMultipartBody(ctx); - ctx.body = await contentManagerService.create(data, { - files, - model, - }); + ctx.body = await contentManagerService.create(data, { files, model }); } else { // Create an entry using `queries` system - ctx.body = await contentManagerService.create(ctx.request.body, { - model, - }); + ctx.body = await contentManagerService.create(ctx.request.body, { model }); } - strapi.emit('didCreateFirstContentTypeEntry', ctx.params); + strapi.emit('didCreateFirstContentTypeEntry', { model }); } catch (error) { strapi.log.error(error); ctx.badRequest(null, [ @@ -161,17 +159,19 @@ module.exports = { * Deletes one entity of a content type matching a query */ async delete(ctx) { + const { id, model } = ctx.params; const contentManagerService = strapi.plugins['content-manager'].services.contentmanager; - ctx.body = await contentManagerService.delete(ctx.params); + ctx.body = await contentManagerService.delete({ id, model }); }, /** * Deletes multiple entities of a content type matching a query */ async deleteMany(ctx) { + const { model } = ctx.params; const contentManagerService = strapi.plugins['content-manager'].services.contentmanager; - ctx.body = await contentManagerService.deleteMany(ctx.params, ctx.request.query); + ctx.body = await contentManagerService.deleteMany({ model }, ctx.request.query); }, }; diff --git a/packages/strapi-plugin-content-manager/test/components/repeatable-required.test.e2e.js b/packages/strapi-plugin-content-manager/test/components/repeatable-required.test.e2e.js index 70b7876662..dc4cfa0a4e 100644 --- a/packages/strapi-plugin-content-manager/test/components/repeatable-required.test.e2e.js +++ b/packages/strapi-plugin-content-manager/test/components/repeatable-required.test.e2e.js @@ -6,10 +6,7 @@ let modelsUtils; let rq; describe.each([ - [ - 'CONTENT MANAGER', - '/content-manager/explorer/application::withcomponent.withcomponent', - ], + ['CONTENT MANAGER', '/content-manager/explorer/application::withcomponent.withcomponent'], ['GENERATED API', '/withcomponents'], ])('[%s] => Non repeatable and Not required component', (_, path) => { beforeAll(async () => { diff --git a/packages/strapi-plugin-upload/controllers/Upload.js b/packages/strapi-plugin-upload/controllers/Upload.js index 01f167c5f2..eaf64ed495 100644 --- a/packages/strapi-plugin-upload/controllers/Upload.js +++ b/packages/strapi-plugin-upload/controllers/Upload.js @@ -96,7 +96,8 @@ module.exports = { }, async findOne(ctx) { - const data = await strapi.plugins['upload'].services.upload.fetch(ctx.params); + const { id } = ctx.params; + const data = await strapi.plugins['upload'].services.upload.fetch({ id }); if (!data) { return ctx.notFound('file.notFound'); diff --git a/packages/strapi-plugin-users-permissions/controllers/Auth.js b/packages/strapi-plugin-users-permissions/controllers/Auth.js index 5395a65c0a..3aae9b4b44 100644 --- a/packages/strapi-plugin-users-permissions/controllers/Auth.js +++ b/packages/strapi-plugin-users-permissions/controllers/Auth.js @@ -70,9 +70,7 @@ module.exports = { } // Check if the user exists. - const user = await strapi - .query('user', 'users-permissions') - .findOne(query); + const user = await strapi.query('user', 'users-permissions').findOne(query); if (!user) { return ctx.badRequest( @@ -119,9 +117,10 @@ module.exports = { ); } - const validPassword = strapi.plugins[ - 'users-permissions' - ].services.user.validatePassword(params.password, user.password); + const validPassword = strapi.plugins['users-permissions'].services.user.validatePassword( + params.password, + user.password + ); if (!validPassword) { return ctx.badRequest( @@ -155,9 +154,10 @@ module.exports = { // Connect the user with the third-party provider. let user, error; try { - [user, error] = await strapi.plugins[ - 'users-permissions' - ].services.providers.connect(provider, ctx.query); + [user, error] = await strapi.plugins['users-permissions'].services.providers.connect( + provider, + ctx.query + ); } catch ([user, error]) { return ctx.badRequest(null, error === 'array' ? error[0] : error); } @@ -203,14 +203,12 @@ module.exports = { // Delete the current code user.resetPasswordToken = null; - user.password = await strapi.plugins[ - 'users-permissions' - ].services.user.hashPassword(params); + user.password = await strapi.plugins['users-permissions'].services.user.hashPassword({ + password: params.password, + }); // Update the user. - await strapi - .query('user', 'users-permissions') - .update({ id: user.id }, user); + await strapi.query('user', 'users-permissions').update({ id: user.id }, user); ctx.send({ jwt: strapi.plugins['users-permissions'].services.jwt.issue({ @@ -258,9 +256,7 @@ module.exports = { const [requestPath] = ctx.request.url.split('?'); const provider = - process.platform === 'win32' - ? requestPath.split('\\')[2] - : requestPath.split('/')[2]; + process.platform === 'win32' ? requestPath.split('\\')[2] : requestPath.split('/')[2]; const config = grantConfig[provider]; if (!_.get(config, 'enabled')) { @@ -268,9 +264,7 @@ module.exports = { } // Ability to pass OAuth callback dynamically grantConfig[provider].callback = - ctx.query && ctx.query.callback - ? ctx.query.callback - : grantConfig[provider].callback; + ctx.query && ctx.query.callback ? ctx.query.callback : grantConfig[provider].callback; return grant(grantConfig)(ctx, next); }, @@ -299,9 +293,7 @@ module.exports = { }); // Find the user by email. - const user = await strapi - .query('user', 'users-permissions') - .findOne({ email }); + const user = await strapi.query('user', 'users-permissions').findOne({ email }); // User not found. if (!user) { @@ -320,43 +312,43 @@ module.exports = { // Set the property code. user.resetPasswordToken = resetPasswordToken; - const settings = await pluginStore - .get({ key: 'email' }) - .then(storeEmail => { - try { - return storeEmail['reset_password'].options; - } catch (error) { - return {}; - } - }); + const settings = await pluginStore.get({ key: 'email' }).then(storeEmail => { + try { + return storeEmail['reset_password'].options; + } catch (error) { + return {}; + } + }); const advanced = await pluginStore.get({ key: 'advanced', }); - settings.message = await strapi.plugins[ - 'users-permissions' - ].services.userspermissions.template(settings.message, { - URL: advanced.email_reset_password, - USER: _.omit(user.toJSON ? user.toJSON() : user, [ - 'password', - 'resetPasswordToken', - 'role', - 'provider', - ]), - TOKEN: resetPasswordToken, - }); + settings.message = await strapi.plugins['users-permissions'].services.userspermissions.template( + settings.message, + { + URL: advanced.email_reset_password, + USER: _.omit(user.toJSON ? user.toJSON() : user, [ + 'password', + 'resetPasswordToken', + 'role', + 'provider', + ]), + TOKEN: resetPasswordToken, + } + ); - settings.object = await strapi.plugins[ - 'users-permissions' - ].services.userspermissions.template(settings.object, { - USER: _.omit(user.toJSON ? user.toJSON() : user, [ - 'password', - 'resetPasswordToken', - 'role', - 'provider', - ]), - }); + settings.object = await strapi.plugins['users-permissions'].services.userspermissions.template( + settings.object, + { + USER: _.omit(user.toJSON ? user.toJSON() : user, [ + 'password', + 'resetPasswordToken', + 'role', + 'provider', + ]), + } + ); try { // Send an email to the user. @@ -376,9 +368,7 @@ module.exports = { } // Update the user. - await strapi - .query('user', 'users-permissions') - .update({ id: user.id }, user); + await strapi.query('user', 'users-permissions').update({ id: user.id }, user); ctx.send({ ok: true }); }, @@ -432,17 +422,12 @@ module.exports = { // Throw an error if the password selected by the user // contains more than two times the symbol '$'. - if ( - strapi.plugins['users-permissions'].services.user.isHashed( - params.password - ) - ) { + if (strapi.plugins['users-permissions'].services.user.isHashed(params.password)) { return ctx.badRequest( null, formatError({ id: 'Auth.form.error.password.format', - message: - 'Your password cannot contain more than three times the symbol `$`.', + message: 'Your password cannot contain more than three times the symbol `$`.', }) ); } @@ -477,9 +462,7 @@ module.exports = { } params.role = role.id; - params.password = await strapi.plugins[ - 'users-permissions' - ].services.user.hashPassword(params); + params.password = await strapi.plugins['users-permissions'].services.user.hashPassword(params); const user = await strapi.query('user', 'users-permissions').findOne({ email: params.email, @@ -510,32 +493,25 @@ module.exports = { params.confirmed = true; } - const user = await strapi - .query('user', 'users-permissions') - .create(params); + const user = await strapi.query('user', 'users-permissions').create(params); const jwt = strapi.plugins['users-permissions'].services.jwt.issue( _.pick(user.toJSON ? user.toJSON() : user, ['id']) ); if (settings.email_confirmation) { - const settings = await pluginStore - .get({ key: 'email' }) - .then(storeEmail => { - try { - return storeEmail['email_confirmation'].options; - } catch (error) { - return {}; - } - }); + const settings = await pluginStore.get({ key: 'email' }).then(storeEmail => { + try { + return storeEmail['email_confirmation'].options; + } catch (error) { + return {}; + } + }); settings.message = await strapi.plugins[ 'users-permissions' ].services.userspermissions.template(settings.message, { - URL: new URL( - '/auth/email-confirmation', - strapi.config.url - ).toString(), + URL: new URL('/auth/email-confirmation', strapi.config.url).toString(), USER: _.omit(user.toJSON ? user.toJSON() : user, [ 'password', 'resetPasswordToken', @@ -595,9 +571,9 @@ module.exports = { async emailConfirmation(ctx) { const params = ctx.query; - const decodedToken = await strapi.plugins[ - 'users-permissions' - ].services.jwt.verify(params.confirmation); + const decodedToken = await strapi.plugins['users-permissions'].services.jwt.verify( + params.confirmation + ); await strapi.plugins['users-permissions'].services.user.edit( { id: decodedToken.id }, @@ -653,39 +629,39 @@ module.exports = { _.pick(user.toJSON ? user.toJSON() : user, ['id']) ); - const settings = await pluginStore - .get({ key: 'email' }) - .then(storeEmail => { - try { - return storeEmail['email_confirmation'].options; - } catch (err) { - return {}; - } - }); - - settings.message = await strapi.plugins[ - 'users-permissions' - ].services.userspermissions.template(settings.message, { - URL: new URL('/auth/email-confirmation', strapi.config.url).toString(), - USER: _.omit(user.toJSON ? user.toJSON() : user, [ - 'password', - 'resetPasswordToken', - 'role', - 'provider', - ]), - CODE: jwt, + const settings = await pluginStore.get({ key: 'email' }).then(storeEmail => { + try { + return storeEmail['email_confirmation'].options; + } catch (err) { + return {}; + } }); - settings.object = await strapi.plugins[ - 'users-permissions' - ].services.userspermissions.template(settings.object, { - USER: _.omit(user.toJSON ? user.toJSON() : user, [ - 'password', - 'resetPasswordToken', - 'role', - 'provider', - ]), - }); + settings.message = await strapi.plugins['users-permissions'].services.userspermissions.template( + settings.message, + { + URL: new URL('/auth/email-confirmation', strapi.config.url).toString(), + USER: _.omit(user.toJSON ? user.toJSON() : user, [ + 'password', + 'resetPasswordToken', + 'role', + 'provider', + ]), + CODE: jwt, + } + ); + + settings.object = await strapi.plugins['users-permissions'].services.userspermissions.template( + settings.object, + { + USER: _.omit(user.toJSON ? user.toJSON() : user, [ + 'password', + 'resetPasswordToken', + 'role', + 'provider', + ]), + } + ); try { await strapi.plugins['email'].services.email.send({ diff --git a/packages/strapi-plugin-users-permissions/controllers/User.js b/packages/strapi-plugin-users-permissions/controllers/User.js index 6d1e284d7c..702b311369 100644 --- a/packages/strapi-plugin-users-permissions/controllers/User.js +++ b/packages/strapi-plugin-users-permissions/controllers/User.js @@ -28,14 +28,9 @@ module.exports = { if (_.has(ctx.query, '_q')) { // use core strapi query to search for users - users = await strapi - .query('user', 'users-permissions') - .search(ctx.query, populate); + users = await strapi.query('user', 'users-permissions').search(ctx.query, populate); } else { - users = await strapi.plugins['users-permissions'].services.user.fetchAll( - ctx.query, - populate - ); + users = await strapi.plugins['users-permissions'].services.user.fetchAll(ctx.query, populate); } const data = users.map(sanitizeUser); @@ -50,9 +45,7 @@ module.exports = { const user = ctx.state.user; if (!user) { - return ctx.badRequest(null, [ - { messages: [{ id: 'No authorization header was found' }] }, - ]); + return ctx.badRequest(null, [{ messages: [{ id: 'No authorization header was found' }] }]); } const data = sanitizeUser(user); @@ -113,9 +106,7 @@ module.exports = { } if (advanced.unique_email) { - const userWithSameEmail = await strapi - .query('user', 'users-permissions') - .findOne({ email }); + const userWithSameEmail = await strapi.query('user', 'users-permissions').findOne({ email }); if (userWithSameEmail) { return ctx.badRequest( @@ -144,9 +135,7 @@ module.exports = { } try { - const data = await strapi.plugins['users-permissions'].services.user.add( - user - ); + const data = await strapi.plugins['users-permissions'].services.user.add(user); ctx.created(data); } catch (error) { @@ -183,11 +172,7 @@ module.exports = { return ctx.badRequest('username.notNull'); } - if ( - _.has(ctx.request.body, 'password') && - !password && - user.provider === 'local' - ) { + if (_.has(ctx.request.body, 'password') && !password && user.provider === 'local') { return ctx.badRequest('password.notNull'); } @@ -209,9 +194,7 @@ module.exports = { } if (_.has(ctx.request.body, 'email') && advancedConfigs.unique_email) { - const userWithSameEmail = await strapi - .query('user', 'users-permissions') - .findOne({ email }); + const userWithSameEmail = await strapi.query('user', 'users-permissions').findOne({ email }); if (userWithSameEmail && userWithSameEmail.id != id) { return ctx.badRequest( @@ -233,10 +216,7 @@ module.exports = { delete updateData.password; } - const data = await strapi.plugins['users-permissions'].services.user.edit( - { id }, - updateData - ); + const data = await strapi.plugins['users-permissions'].services.user.edit({ id }, updateData); ctx.send(data); }, @@ -247,16 +227,15 @@ module.exports = { */ async destroy(ctx) { const { id } = ctx.params; - const data = await strapi.plugins['users-permissions'].services.user.remove( - { id } - ); + const data = await strapi.plugins['users-permissions'].services.user.remove({ id }); ctx.send(data); }, async destroyAll(ctx) { - const data = await strapi.plugins[ - 'users-permissions' - ].services.user.removeAll(ctx.params, ctx.request.query); + const data = await strapi.plugins['users-permissions'].services.user.removeAll( + {}, + ctx.request.query + ); ctx.send(data); }, diff --git a/packages/strapi/lib/core-api/controller.js b/packages/strapi/lib/core-api/controller.js index cb5d507a46..c5f0496a49 100644 --- a/packages/strapi/lib/core-api/controller.js +++ b/packages/strapi/lib/core-api/controller.js @@ -81,7 +81,7 @@ const createCollectionTypeController = ({ model, service }) => { * @return {Object} */ async findOne(ctx) { - const entity = await service.findOne(ctx.params); + const entity = await service.findOne({ id: ctx.params.id }); return sanitizeEntity(entity, { model }); }, @@ -122,9 +122,9 @@ const createCollectionTypeController = ({ model, service }) => { let entity; if (ctx.is('multipart')) { const { data, files } = parseMultipartData(ctx); - entity = await service.update(ctx.params, data, { files }); + entity = await service.update({ id: ctx.params.id }, data, { files }); } else { - entity = await service.update(ctx.params, ctx.request.body); + entity = await service.update({ id: ctx.params.id }, ctx.request.body); } return sanitizeEntity(entity, { model }); @@ -136,7 +136,7 @@ const createCollectionTypeController = ({ model, service }) => { * @return {Object} */ async delete(ctx) { - const entity = await service.delete(ctx.params); + const entity = await service.delete({ id: ctx.params.id }); return sanitizeEntity(entity, { model }); }, };