mirror of
https://github.com/strapi/strapi.git
synced 2025-12-26 22:54:31 +00:00
Merge pull request #11025 from strapi/v4/pagination-upload
Use new filters format in the upload plugin
This commit is contained in:
commit
412a834f98
@ -49,14 +49,14 @@ describe('Permissions Manager', () => {
|
||||
});
|
||||
|
||||
test('It should returns a valid query from the ability', () => {
|
||||
const ability = defineAbility(can => can('read', 'foo', ['bar'], { $and: [{ kai: 'doe' }] }));
|
||||
const ability = defineAbility(can => can('read', 'foo', ['bar'], { kai: 'doe' }));
|
||||
const pm = createPermissionsManager({
|
||||
ability,
|
||||
action: 'read',
|
||||
model: 'foo',
|
||||
});
|
||||
|
||||
const expected = [{ kai: 'doe' }];
|
||||
const expected = { $or: [{ kai: 'doe' }] };
|
||||
|
||||
expect(pm.getQuery()).toStrictEqual(expected);
|
||||
});
|
||||
@ -179,7 +179,7 @@ describe('Permissions Manager', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryFrom', () => {
|
||||
describe('addPermissionsQueryTo', () => {
|
||||
const ability = defineAbility(can =>
|
||||
can('read', 'article', ['title'], { $and: [{ title: 'foo' }] })
|
||||
);
|
||||
@ -189,25 +189,27 @@ describe('Permissions Manager', () => {
|
||||
model: 'article',
|
||||
});
|
||||
|
||||
const pmQuery = [{ title: 'foo' }];
|
||||
const pmQuery = { $or: [{ $and: [{ title: 'foo' }] }] };
|
||||
|
||||
test('Create query from simple object', () => {
|
||||
const query = { _limit: 100 };
|
||||
const expected = { _limit: 100, _where: pmQuery };
|
||||
const query = { limit: 100 };
|
||||
const expected = { limit: 100, filters: pmQuery };
|
||||
|
||||
const res = pm.queryFrom(query);
|
||||
const res = pm.addPermissionsQueryTo(query);
|
||||
|
||||
expect(res).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
test('Create query from complex object', () => {
|
||||
const query = { _limit: 100, _where: [{ a: 'b' }, { c: 'd' }] };
|
||||
const query = { limit: 100, filters: { $and: [{ a: 'b' }, { c: 'd' }] } };
|
||||
const expected = {
|
||||
_limit: 100,
|
||||
_where: [{ a: 'b' }, { c: 'd' }, ...pmQuery],
|
||||
limit: 100,
|
||||
filters: {
|
||||
$and: [query.filters, pmQuery],
|
||||
},
|
||||
};
|
||||
|
||||
const res = pm.queryFrom(query);
|
||||
const res = pm.addPermissionsQueryTo(query);
|
||||
|
||||
expect(res).toStrictEqual(expected);
|
||||
});
|
||||
@ -216,63 +218,81 @@ describe('Permissions Manager', () => {
|
||||
describe('buildStrapiQuery', () => {
|
||||
const tests = [
|
||||
['No transform', { foo: 'bar' }, { foo: 'bar' }],
|
||||
['Simple op', { foo: { $eq: 'bar' } }, { foo_eq: 'bar' }],
|
||||
['Nested property', { foo: { nested: 'bar' } }, { 'foo.nested': 'bar' }],
|
||||
['Simple op', { foo: { $eq: 'bar' } }, { foo: { $eq: 'bar' } }],
|
||||
['Nested property', { 'foo.nested': 'bar' }, { foo: { nested: 'bar' } }],
|
||||
[
|
||||
'Nested property + $eq',
|
||||
{ 'foo.nested': { $eq: 'bar' } },
|
||||
{ foo: { nested: { $eq: 'bar' } } },
|
||||
],
|
||||
[
|
||||
'Nested property + $elementMatch',
|
||||
{ 'foo.nested': { $elemMatch: 'bar' } },
|
||||
{ foo: { nested: 'bar' } },
|
||||
],
|
||||
[
|
||||
'Deeply nested property',
|
||||
{ foo: { nested: { again: 'bar' } } },
|
||||
{ 'foo.nested.again': 'bar' },
|
||||
{ foo: { nested: { again: 'bar' } } },
|
||||
],
|
||||
['Op with array', { foo: { $in: ['bar', 'rab'] } }, { foo_in: ['bar', 'rab'] }],
|
||||
['Removable op', { foo: { $elemMatch: { a: 'b' } } }, { 'foo.a': 'b' }],
|
||||
['Op with array', { foo: { $in: ['bar', 'rab'] } }, { foo: { $in: ['bar', 'rab'] } }],
|
||||
['Removable op', { foo: { $elemMatch: { a: 'b' } } }, { foo: { a: 'b' } }],
|
||||
[
|
||||
'Combination of removable and basic ops',
|
||||
{ foo: { $elemMatch: { a: { $in: [1, 2, 3] } } } },
|
||||
{ 'foo.a_in': [1, 2, 3] },
|
||||
{ foo: { a: { $in: [1, 2, 3] } } },
|
||||
],
|
||||
[
|
||||
'Decoupling of nested properties with/without op',
|
||||
{ foo: { $elemMatch: { a: { $in: [1, 2, 3] }, b: 'c' } } },
|
||||
{ 'foo.a_in': [1, 2, 3], 'foo.b': 'c' },
|
||||
{ foo: { a: { $in: [1, 2, 3] }, b: 'c' } },
|
||||
],
|
||||
[
|
||||
'OR op and properties decoupling',
|
||||
{ $or: [{ foo: { a: 2 } }, { foo: { b: 3 } }] },
|
||||
{ _or: [{ 'foo.a': 2 }, { 'foo.b': 3 }] },
|
||||
{ $or: [{ foo: { a: 2 } }, { foo: { b: 3 } }] },
|
||||
],
|
||||
[
|
||||
'OR op with nested properties & ops',
|
||||
{ $or: [{ foo: { a: 2 } }, { foo: { b: { $in: [1, 2, 3] } } }] },
|
||||
{ _or: [{ 'foo.a': 2 }, { 'foo.b_in': [1, 2, 3] }] },
|
||||
{ $or: [{ foo: { a: 2 } }, { foo: { b: { $in: [1, 2, 3] } } }] },
|
||||
],
|
||||
[
|
||||
'Nested OR op',
|
||||
{ $or: [{ $or: [{ a: 2 }, { a: 3 }] }] },
|
||||
{ _or: [{ _or: [{ a: 2 }, { a: 3 }] }] },
|
||||
{ $or: [{ $or: [{ a: 2 }, { a: 3 }] }] },
|
||||
],
|
||||
[
|
||||
'OR op with nested AND op',
|
||||
{ $or: [{ a: 2 }, [{ a: 3 }, { $or: [{ b: 1 }, { b: 4 }] }]] },
|
||||
{ _or: [{ a: 2 }, [{ a: 3 }, { _or: [{ b: 1 }, { b: 4 }] }]] },
|
||||
{ $or: [{ a: 2 }, [{ a: 3 }, { $or: [{ b: 1 }, { b: 4 }] }]] },
|
||||
],
|
||||
[
|
||||
'OR op with nested AND op and nested properties',
|
||||
{ _or: [{ a: 2 }, [{ a: 3 }, { b: { c: 'foo' } }]] },
|
||||
{ _or: [{ a: 2 }, [{ a: 3 }, { 'b.c': 'foo' }]] },
|
||||
{ $or: [{ a: 2 }, [{ a: 3 }, { b: { c: 'foo' } }]] },
|
||||
{ $or: [{ a: 2 }, [{ a: 3 }, { b: { c: 'foo' } }]] },
|
||||
],
|
||||
[
|
||||
'Literal nested property with removable op',
|
||||
{
|
||||
'createdBy.roles': {
|
||||
$elemMatch: {
|
||||
id: {
|
||||
$in: [1, 2, 3],
|
||||
created_by: {
|
||||
roles: {
|
||||
$elemMatch: {
|
||||
id: {
|
||||
$in: [1, 2, 3],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
'createdBy.roles.id_in': [1, 2, 3],
|
||||
created_by: {
|
||||
roles: {
|
||||
id: {
|
||||
$in: [1, 2, 3],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const { cloneDeep, isObject, set, isArray } = require('lodash/fp');
|
||||
const { cloneDeep, isPlainObject } = require('lodash/fp');
|
||||
const { subject: asSubject } = require('@casl/ability');
|
||||
const { permittedFieldsOf } = require('@casl/ability/extra');
|
||||
const {
|
||||
@ -35,22 +35,15 @@ module.exports = ({ ability, action, model }) => ({
|
||||
return buildStrapiQuery(buildCaslQuery(ability, queryAction, model));
|
||||
},
|
||||
|
||||
// FIXME:
|
||||
queryFrom(query = {}, action) {
|
||||
addPermissionsQueryTo(query = {}, action) {
|
||||
const newQuery = cloneDeep(query);
|
||||
const permissionQuery = this.getQuery(action);
|
||||
|
||||
const newQuery = cloneDeep(query);
|
||||
const { _where } = query;
|
||||
newQuery.filters = isPlainObject(query.filters)
|
||||
? { $and: [query.filters, permissionQuery] }
|
||||
: permissionQuery;
|
||||
|
||||
if (isObject(_where) && !isArray(_where)) {
|
||||
Object.assign(newQuery, { _where: [_where] });
|
||||
}
|
||||
|
||||
if (!_where) {
|
||||
Object.assign(newQuery, { _where: [] });
|
||||
}
|
||||
|
||||
return set('_where', newQuery._where.concat(permissionQuery), newQuery);
|
||||
return newQuery;
|
||||
},
|
||||
|
||||
sanitize(data, options = {}) {
|
||||
|
||||
@ -3,79 +3,63 @@
|
||||
// TODO: migration
|
||||
const _ = require('lodash');
|
||||
const { rulesToQuery } = require('@casl/ability/extra');
|
||||
const { VALID_REST_OPERATORS } = require('@strapi/utils');
|
||||
|
||||
const ops = {
|
||||
common: VALID_REST_OPERATORS.map(op => `$${op}`),
|
||||
boolean: ['$or', '$and'],
|
||||
cleanable: ['$elemMatch'],
|
||||
const operatorsMap = {
|
||||
$in: '$in',
|
||||
$nin: '$notIn',
|
||||
$exists: '$notNull',
|
||||
$gte: '$gte',
|
||||
$gt: '$gt',
|
||||
$lte: '$lte',
|
||||
$lt: '$lt',
|
||||
$eq: '$eq',
|
||||
$ne: '$ne',
|
||||
$and: '$and',
|
||||
$or: '$or',
|
||||
$not: '$not',
|
||||
};
|
||||
|
||||
const mapKey = key => {
|
||||
if (_.isString(key) && key.startsWith('$') && key in operatorsMap) {
|
||||
return operatorsMap[key];
|
||||
}
|
||||
return key;
|
||||
};
|
||||
|
||||
const buildCaslQuery = (ability, action, model) => {
|
||||
const query = rulesToQuery(ability, action, model, o => o.conditions);
|
||||
return _.get(query, '$or[0].$and', {});
|
||||
return rulesToQuery(ability, action, model, o => o.conditions);
|
||||
};
|
||||
|
||||
const buildStrapiQuery = caslQuery => {
|
||||
const transform = _.flow([flattenDeep, cleanupUnwantedProperties]);
|
||||
return transform(caslQuery);
|
||||
return unwrapDeep(caslQuery);
|
||||
};
|
||||
|
||||
const flattenDeep = condition => {
|
||||
if (_.isArray(condition)) {
|
||||
return _.map(condition, flattenDeep);
|
||||
const unwrapDeep = obj => {
|
||||
if (!_.isPlainObject(obj) && !_.isArray(obj)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (!_.isObject(condition)) {
|
||||
return condition;
|
||||
if (_.isArray(obj)) {
|
||||
return obj.map(v => unwrapDeep(v));
|
||||
}
|
||||
|
||||
const shouldIgnore = e => !!ops.common.includes(e);
|
||||
const shouldPerformTransformation = (v, k) => _.isObject(v) && !_.isArray(v) && !shouldIgnore(k);
|
||||
|
||||
const result = {};
|
||||
const set = (key, value) => (result[key] = value);
|
||||
|
||||
const getTransformParams = (prevKey, v, k) =>
|
||||
shouldIgnore(k) ? [`${prevKey}_${k.replace('$', '')}`, v] : [`${prevKey}.${k}`, v];
|
||||
|
||||
_.each(condition, (value, key) => {
|
||||
if (ops.boolean.includes(key)) {
|
||||
set(key.replace('$', '_'), _.map(value, flattenDeep));
|
||||
} else if (shouldPerformTransformation(value, key)) {
|
||||
_.each(flattenDeep(value), (v, k) => {
|
||||
set(...getTransformParams(key, v, k));
|
||||
});
|
||||
} else {
|
||||
set(key, flattenDeep(value));
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const cleanupUnwantedProperties = condition => {
|
||||
if (!_.isObject(condition)) {
|
||||
return condition;
|
||||
}
|
||||
|
||||
if (_.isArray(condition)) {
|
||||
return condition.map(cleanupUnwantedProperties);
|
||||
}
|
||||
|
||||
const shouldClean = e =>
|
||||
typeof e === 'string' ? ops.cleanable.find(o => e.includes(`.${o}`)) : undefined;
|
||||
|
||||
return _.reduce(
|
||||
condition,
|
||||
(acc, value, key) => {
|
||||
const keyToClean = shouldClean(key);
|
||||
const newKey = keyToClean ? key.split(`.${keyToClean}`).join('') : key;
|
||||
obj,
|
||||
(acc, v, k) => {
|
||||
const key = mapKey(k);
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[newKey]: _.isArray(value) ? value.map(cleanupUnwantedProperties) : value,
|
||||
};
|
||||
if (_.isPlainObject(v)) {
|
||||
if ('$elemMatch' in v) {
|
||||
v = v.$elemMatch; // removing this key
|
||||
}
|
||||
_.setWith(acc, key, unwrapDeep(v));
|
||||
} else if (_.isArray(v)) {
|
||||
// prettier-ignore
|
||||
_.setWith(acc, key, v.map(v => unwrapDeep(v)));
|
||||
} else {
|
||||
_.setWith(acc, key, v);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
@ -4,9 +4,10 @@ const { assoc, has, prop, omit } = require('lodash/fp');
|
||||
const strapiUtils = require('@strapi/utils');
|
||||
|
||||
const { sanitizeEntity } = strapiUtils;
|
||||
const { hasDraftAndPublish } = strapiUtils.contentTypes;
|
||||
const { hasDraftAndPublish, isVisibleAttribute } = strapiUtils.contentTypes;
|
||||
const { PUBLISHED_AT_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = strapiUtils.contentTypes.constants;
|
||||
const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = strapiUtils.webhook.webhookEvents;
|
||||
const { MANY_RELATIONS } = strapiUtils.relations.constants;
|
||||
|
||||
const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE);
|
||||
|
||||
@ -92,6 +93,33 @@ const getBasePopulate = (uid, populate) => {
|
||||
});
|
||||
};
|
||||
|
||||
const getCounterPopulate = (uid, populate) => {
|
||||
const basePopulate = getBasePopulate(uid, populate);
|
||||
|
||||
const model = strapi.getModel(uid);
|
||||
|
||||
return basePopulate.reduce((populate, attributeName) => {
|
||||
const attribute = model.attributes[attributeName];
|
||||
|
||||
if (MANY_RELATIONS.includes(attribute.relation) && isVisibleAttribute(model, attributeName)) {
|
||||
populate[attributeName] = { count: true };
|
||||
} else {
|
||||
populate[attributeName] = true;
|
||||
}
|
||||
|
||||
return populate;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const addCreatedByRolesPopulate = populate => {
|
||||
return {
|
||||
...populate,
|
||||
createdBy: {
|
||||
populate: ['roles'],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {import('./entity-manager').default}
|
||||
*/
|
||||
@ -118,17 +146,12 @@ module.exports = ({ strapi }) => ({
|
||||
},
|
||||
|
||||
findWithRelationCounts(opts, uid, populate) {
|
||||
const params = { ...opts, populate: getBasePopulate(uid, populate) };
|
||||
const counterPopulate = addCreatedByRolesPopulate(getCounterPopulate(uid, populate));
|
||||
const params = { ...opts, populate: counterPopulate };
|
||||
|
||||
return strapi.entityService.findWithRelationCounts(uid, params);
|
||||
},
|
||||
|
||||
count(opts, uid) {
|
||||
const params = { ...opts };
|
||||
|
||||
return strapi.entityService.count(uid, params);
|
||||
},
|
||||
|
||||
async findOne(id, uid, populate) {
|
||||
const params = { populate: getDeepPopulate(uid, populate) };
|
||||
|
||||
|
||||
@ -43,7 +43,8 @@ const createPermissionChecker = strapi => ({ userAbility, model }) => {
|
||||
const sanitizeCreateInput = data => sanitizeInput(ACTIONS.create, data);
|
||||
const sanitizeUpdateInput = entity => data => sanitizeInput(ACTIONS.update, data, entity);
|
||||
|
||||
const buildPermissionQuery = (query, action) => permissionsManager.queryFrom(query, action);
|
||||
const buildPermissionQuery = (query, action) =>
|
||||
permissionsManager.addPermissionsQueryTo(query, action);
|
||||
|
||||
const buildReadQuery = query => buildPermissionQuery(query, ACTIONS.read);
|
||||
const buildDeleteQuery = query => buildPermissionQuery(query, ACTIONS.delete);
|
||||
|
||||
@ -5,7 +5,6 @@ const {
|
||||
sanitizeEntity,
|
||||
webhook: webhookUtils,
|
||||
contentTypes: contentTypesUtils,
|
||||
relations: relationsUtils,
|
||||
} = require('@strapi/utils');
|
||||
const uploadFiles = require('../utils/upload-files');
|
||||
|
||||
@ -17,8 +16,6 @@ const {
|
||||
} = require('./components');
|
||||
const { transformParamsToQuery, pickSelectionParams } = require('./params');
|
||||
|
||||
const { MANY_RELATIONS } = relationsUtils.constants;
|
||||
|
||||
// TODO: those should be strapi events used by the webhooks not the other way arround
|
||||
const { ENTRY_CREATE, ENTRY_UPDATE, ENTRY_DELETE } = webhookUtils.webhookEvents;
|
||||
|
||||
@ -88,33 +85,11 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
||||
|
||||
// TODO: streamline the logic based on the populate option
|
||||
async findWithRelationCounts(uid, opts) {
|
||||
const model = strapi.getModel(uid);
|
||||
|
||||
const wrappedParams = await this.wrapParams(opts, { uid, action: 'findWithRelationCounts' });
|
||||
|
||||
const query = transformParamsToQuery(uid, wrappedParams);
|
||||
|
||||
const { attributes } = model;
|
||||
|
||||
const populate = (query.populate || []).reduce((populate, attributeName) => {
|
||||
const attribute = attributes[attributeName];
|
||||
|
||||
if (
|
||||
MANY_RELATIONS.includes(attribute.relation) &&
|
||||
contentTypesUtils.isVisibleAttribute(model, attributeName)
|
||||
) {
|
||||
populate[attributeName] = { count: true };
|
||||
} else {
|
||||
populate[attributeName] = true;
|
||||
}
|
||||
|
||||
return populate;
|
||||
}, {});
|
||||
|
||||
const { results, pagination } = await db.query(uid).findPage({
|
||||
...query,
|
||||
populate,
|
||||
});
|
||||
const { results, pagination } = await db.query(uid).findPage(query);
|
||||
|
||||
return {
|
||||
results,
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
|
||||
const _ = require('lodash');
|
||||
const { contentTypes: contentTypesUtils } = require('@strapi/utils');
|
||||
const validateSettings = require('../validation/settings');
|
||||
const validateUploadBody = require('../validation/upload');
|
||||
const { getService } = require('../../utils');
|
||||
const { getService } = require('../utils');
|
||||
const validateSettings = require('./validation/settings');
|
||||
const validateUploadBody = require('./validation/upload');
|
||||
|
||||
const { CREATED_BY_ATTRIBUTE } = contentTypesUtils.constants;
|
||||
|
||||
@ -35,7 +35,8 @@ module.exports = {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
|
||||
const query = pm.queryFrom(ctx.query);
|
||||
const query = pm.addPermissionsQueryTo(ctx.query);
|
||||
|
||||
const files = await getService('upload').fetchAll(query);
|
||||
|
||||
ctx.body = pm.sanitize(files, { withPrivate: false });
|
||||
@ -68,7 +69,7 @@ module.exports = {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
|
||||
const query = pm.queryFrom(ctx.query);
|
||||
const query = pm.addPermissionsQueryTo(ctx.query);
|
||||
const count = await getService('upload').count(query);
|
||||
|
||||
ctx.body = { count };
|
||||
@ -185,10 +186,29 @@ module.exports = {
|
||||
|
||||
ctx.body = pm.sanitize(uploadedFiles, { action: ACTIONS.read, withPrivate: false });
|
||||
},
|
||||
|
||||
async upload(ctx) {
|
||||
const {
|
||||
query: { id },
|
||||
request: { files: { files } = {} },
|
||||
} = ctx;
|
||||
|
||||
if (id && (_.isEmpty(files) || files.size === 0)) {
|
||||
return this.updateFileInfo(ctx);
|
||||
}
|
||||
|
||||
if (_.isEmpty(files) || files.size === 0) {
|
||||
throw strapi.errors.badRequest(null, {
|
||||
errors: [{ id: 'Upload.status.empty', message: 'Files are empty' }],
|
||||
});
|
||||
}
|
||||
|
||||
await (id ? this.replaceFile : this.uploadFiles)(ctx);
|
||||
},
|
||||
};
|
||||
|
||||
const findEntityAndCheckPermissions = async (ability, action, model, id) => {
|
||||
const file = await getService('upload').findOne({ id }, [CREATED_BY_ATTRIBUTE]);
|
||||
const file = await getService('upload').findOne(id, [CREATED_BY_ATTRIBUTE]);
|
||||
|
||||
if (_.isNil(file)) {
|
||||
throw strapi.errors.notFound();
|
||||
@ -1,9 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const { sanitizeEntity } = require('@strapi/utils');
|
||||
const validateSettings = require('../validation/settings');
|
||||
const validateUploadBody = require('../validation/upload');
|
||||
const { getService } = require('../../utils');
|
||||
const { getService } = require('../utils');
|
||||
const validateSettings = require('./validation/settings');
|
||||
const validateUploadBody = require('./validation/upload');
|
||||
|
||||
const sanitize = (data, options = {}) => {
|
||||
return sanitizeEntity(data, {
|
||||
@ -24,7 +25,7 @@ module.exports = {
|
||||
params: { id },
|
||||
} = ctx;
|
||||
|
||||
const file = await getService('upload').findOne({ id });
|
||||
const file = await getService('upload').findOne(id);
|
||||
|
||||
if (!file) {
|
||||
return ctx.notFound('file.notFound');
|
||||
@ -42,7 +43,7 @@ module.exports = {
|
||||
params: { id },
|
||||
} = ctx;
|
||||
|
||||
const file = await getService('upload').findOne({ id });
|
||||
const file = await getService('upload').findOne(id);
|
||||
|
||||
if (!file) {
|
||||
return ctx.notFound('file.notFound');
|
||||
@ -118,4 +119,35 @@ module.exports = {
|
||||
|
||||
ctx.body = sanitize(uploadedFiles);
|
||||
},
|
||||
|
||||
async upload(ctx) {
|
||||
const {
|
||||
query: { id },
|
||||
request: { files: { files } = {} },
|
||||
} = ctx;
|
||||
|
||||
if (id && (_.isEmpty(files) || files.size === 0)) {
|
||||
return this.updateFileInfo(ctx);
|
||||
}
|
||||
|
||||
if (_.isEmpty(files) || files.size === 0) {
|
||||
throw strapi.errors.badRequest(null, {
|
||||
errors: [{ id: 'Upload.status.empty', message: 'Files are empty' }],
|
||||
});
|
||||
}
|
||||
|
||||
await (id ? this.replaceFile : this.uploadFiles)(ctx);
|
||||
},
|
||||
|
||||
async search(ctx) {
|
||||
const { id } = ctx.params;
|
||||
const model = strapi.getModel('plugin::upload.file');
|
||||
const entries = await strapi.query('plugin::upload.file').findMany({
|
||||
where: {
|
||||
$or: [{ hash: { $contains: id } }, { name: { $contains: id } }],
|
||||
},
|
||||
});
|
||||
|
||||
ctx.body = sanitizeEntity(entries, { model });
|
||||
},
|
||||
};
|
||||
@ -1,7 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const upload = require('./upload');
|
||||
const adminApi = require('./admin-api');
|
||||
const contentApi = require('./content-api');
|
||||
|
||||
module.exports = {
|
||||
upload,
|
||||
'admin-api': adminApi,
|
||||
'content-api': contentApi,
|
||||
};
|
||||
|
||||
@ -1,72 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Upload.js controller
|
||||
*
|
||||
*/
|
||||
|
||||
const _ = require('lodash');
|
||||
const { sanitizeEntity } = require('@strapi/utils');
|
||||
const apiUploadController = require('./upload/api');
|
||||
const adminUploadController = require('./upload/admin');
|
||||
|
||||
const resolveController = ctx => {
|
||||
const {
|
||||
state: { isAuthenticatedAdmin },
|
||||
} = ctx;
|
||||
|
||||
return isAuthenticatedAdmin ? adminUploadController : apiUploadController;
|
||||
};
|
||||
|
||||
const resolveControllerMethod = method => ctx => {
|
||||
const controller = resolveController(ctx);
|
||||
const callbackFn = controller[method];
|
||||
|
||||
if (!_.isFunction(callbackFn)) {
|
||||
return ctx.notFound();
|
||||
}
|
||||
|
||||
return callbackFn(ctx);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
find: resolveControllerMethod('find'),
|
||||
findOne: resolveControllerMethod('findOne'),
|
||||
count: resolveControllerMethod('count'),
|
||||
destroy: resolveControllerMethod('destroy'),
|
||||
updateSettings: resolveControllerMethod('updateSettings'),
|
||||
getSettings: resolveControllerMethod('getSettings'),
|
||||
|
||||
async upload(ctx) {
|
||||
const {
|
||||
query: { id },
|
||||
request: { files: { files } = {} },
|
||||
} = ctx;
|
||||
|
||||
const controller = resolveController(ctx);
|
||||
|
||||
if (id && (_.isEmpty(files) || files.size === 0)) {
|
||||
return controller.updateFileInfo(ctx);
|
||||
}
|
||||
|
||||
if (_.isEmpty(files) || files.size === 0) {
|
||||
throw strapi.errors.badRequest(null, {
|
||||
errors: [{ id: 'Upload.status.empty', message: 'Files are empty' }],
|
||||
});
|
||||
}
|
||||
|
||||
await (id ? controller.replaceFile : controller.uploadFiles)(ctx);
|
||||
},
|
||||
|
||||
async search(ctx) {
|
||||
const { id } = ctx.params;
|
||||
const model = strapi.getModel('plugin::upload.file');
|
||||
const entries = await strapi.query('plugin::upload.file').findMany({
|
||||
where: {
|
||||
$or: [{ hash: { $contains: id } }, { name: { $contains: id } }],
|
||||
},
|
||||
});
|
||||
|
||||
ctx.body = sanitizeEntity(entries, { model });
|
||||
},
|
||||
};
|
||||
@ -6,7 +6,7 @@ module.exports = {
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/settings',
|
||||
handler: 'upload.getSettings',
|
||||
handler: 'admin-api.getSettings',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
@ -22,7 +22,7 @@ module.exports = {
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/settings',
|
||||
handler: 'upload.updateSettings',
|
||||
handler: 'admin-api.updateSettings',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
@ -38,7 +38,7 @@ module.exports = {
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/',
|
||||
handler: 'upload.upload',
|
||||
handler: 'admin-api.upload',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
@ -46,7 +46,7 @@ module.exports = {
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files/count',
|
||||
handler: 'upload.count',
|
||||
handler: 'admin-api.count',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
@ -62,7 +62,7 @@ module.exports = {
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files',
|
||||
handler: 'upload.find',
|
||||
handler: 'admin-api.find',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
@ -78,7 +78,7 @@ module.exports = {
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files/:id',
|
||||
handler: 'upload.findOne',
|
||||
handler: 'admin-api.findOne',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
@ -91,18 +91,10 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/search/:id',
|
||||
handler: 'upload.search',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/files/:id',
|
||||
handler: 'upload.destroy',
|
||||
handler: 'admin-api.destroy',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
|
||||
@ -6,27 +6,27 @@ module.exports = {
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/',
|
||||
handler: 'upload.upload',
|
||||
handler: 'content-api.upload',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files/count',
|
||||
handler: 'upload.count',
|
||||
handler: 'content-api.count',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files',
|
||||
handler: 'upload.find',
|
||||
handler: 'content-api.find',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files/:id',
|
||||
handler: 'upload.findOne',
|
||||
handler: 'content-api.findOne',
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/files/:id',
|
||||
handler: 'upload.destroy',
|
||||
handler: 'content-api.destroy',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -61,9 +61,7 @@ module.exports = {
|
||||
description: 'Delete one file',
|
||||
resolverOf: 'plugin::upload.upload.destroy',
|
||||
async resolver(obj, options, { context }) {
|
||||
const file = await getService('upload').findOne({
|
||||
id: context.params.id,
|
||||
});
|
||||
const file = await getService('upload').findOne(context.params.id);
|
||||
if (file) {
|
||||
const fileResult = await getService('upload').remove(file);
|
||||
return { file: fileResult };
|
||||
|
||||
@ -43,14 +43,6 @@ const sendMediaMetrics = data => {
|
||||
}
|
||||
};
|
||||
|
||||
const combineFilters = params => {
|
||||
// FIXME: until we support boolean operators for querying we need to make mime_ncontains use AND instead of OR
|
||||
if (_.has(params, 'mime_ncontains') && Array.isArray(params.mime_ncontains)) {
|
||||
params._where = params.mime_ncontains.map(val => ({ mime_ncontains: val }));
|
||||
delete params.mime_ncontains;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ({ strapi }) => ({
|
||||
emitEvent(event, data) {
|
||||
const modelDef = strapi.getModel('plugin::upload.file');
|
||||
@ -189,7 +181,7 @@ module.exports = ({ strapi }) => ({
|
||||
},
|
||||
|
||||
async updateFileInfo(id, { name, alternativeText, caption }, { user } = {}) {
|
||||
const dbFile = await this.findOne({ id });
|
||||
const dbFile = await this.findOne(id);
|
||||
|
||||
if (!dbFile) {
|
||||
throw strapi.errors.notFound('file not found');
|
||||
@ -201,7 +193,7 @@ module.exports = ({ strapi }) => ({
|
||||
caption: _.isNil(caption) ? dbFile.caption : caption,
|
||||
};
|
||||
|
||||
return this.update({ id }, newInfos, { user });
|
||||
return this.update(id, newInfos, { user });
|
||||
},
|
||||
|
||||
async replace(id, { data, file }, { user } = {}) {
|
||||
@ -211,7 +203,7 @@ module.exports = ({ strapi }) => ({
|
||||
'image-manipulation'
|
||||
);
|
||||
|
||||
const dbFile = await this.findOne({ id });
|
||||
const dbFile = await this.findOne(id);
|
||||
|
||||
if (!dbFile) {
|
||||
throw strapi.errors.notFound('file not found');
|
||||
@ -274,20 +266,17 @@ module.exports = ({ strapi }) => ({
|
||||
height,
|
||||
});
|
||||
|
||||
return this.update({ id }, fileData, { user });
|
||||
return this.update(id, fileData, { user });
|
||||
},
|
||||
|
||||
async update(params, values, { user } = {}) {
|
||||
async update(id, values, { user } = {}) {
|
||||
const fileValues = { ...values };
|
||||
if (user) {
|
||||
fileValues[UPDATED_BY_ATTRIBUTE] = user.id;
|
||||
}
|
||||
sendMediaMetrics(fileValues);
|
||||
|
||||
//
|
||||
const res = await strapi
|
||||
.query('plugin::upload.file')
|
||||
.update({ where: params, data: fileValues });
|
||||
const res = await strapi.entityService.update('plugin::upload.file', id, { data: fileValues });
|
||||
|
||||
this.emitEvent(MEDIA_UPDATE, res);
|
||||
|
||||
@ -309,18 +298,16 @@ module.exports = ({ strapi }) => ({
|
||||
return res;
|
||||
},
|
||||
|
||||
findOne(params, populate) {
|
||||
return strapi.query('plugin::upload.file').findOne({ where: params, populate });
|
||||
findOne(id, populate) {
|
||||
return strapi.entityService.findOne('plugin::upload.file', id, { populate });
|
||||
},
|
||||
|
||||
fetchAll(params) {
|
||||
combineFilters(params);
|
||||
return strapi.query('plugin::upload.file').findMany({ ...params });
|
||||
fetchAll(query) {
|
||||
return strapi.entityService.findMany('plugin::upload.file', query);
|
||||
},
|
||||
|
||||
count(params) {
|
||||
combineFilters(params);
|
||||
return strapi.query('plugin::upload.file').count({ ...params });
|
||||
count(query) {
|
||||
return strapi.entityService.count('plugin::upload.file', query);
|
||||
},
|
||||
|
||||
async remove(file) {
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
const _ = require('lodash');
|
||||
const parseType = require('./parse-type');
|
||||
|
||||
const QUERY_OPERATORS = ['_where', '_or', '_and'];
|
||||
|
||||
@ -29,6 +30,10 @@ const validateOrder = order => {
|
||||
}
|
||||
};
|
||||
|
||||
const convertCountQueryParams = countQuery => {
|
||||
return parseType({ type: 'boolean', value: countQuery });
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort query parser
|
||||
* @param {string} sortQuery - ex: id:asc,price:desc
|
||||
@ -161,7 +166,7 @@ const convertNestedPopulate = subPopulate => {
|
||||
}
|
||||
|
||||
// TODO: We will need to consider a way to add limitation / pagination
|
||||
const { sort, filters, fields, populate } = subPopulate;
|
||||
const { sort, filters, fields, populate, count } = subPopulate;
|
||||
|
||||
const query = {};
|
||||
|
||||
@ -181,6 +186,10 @@ const convertNestedPopulate = subPopulate => {
|
||||
query.populate = convertPopulateQueryParams(populate);
|
||||
}
|
||||
|
||||
if (count) {
|
||||
query.count = convertCountQueryParams(count);
|
||||
}
|
||||
|
||||
return query;
|
||||
};
|
||||
|
||||
@ -206,23 +215,6 @@ const convertFieldsQueryParams = (fields, depth = 0) => {
|
||||
// NOTE: We could validate the parameters are on existing / non private attributes
|
||||
const convertFiltersQueryParams = filters => filters;
|
||||
|
||||
// TODO: migrate
|
||||
const VALID_REST_OPERATORS = [
|
||||
'eq',
|
||||
'ne',
|
||||
'in',
|
||||
'nin',
|
||||
'contains',
|
||||
'ncontains',
|
||||
'containss',
|
||||
'ncontainss',
|
||||
'lt',
|
||||
'lte',
|
||||
'gt',
|
||||
'gte',
|
||||
'null',
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
convertSortQueryParams,
|
||||
convertStartQueryParams,
|
||||
@ -230,6 +222,5 @@ module.exports = {
|
||||
convertPopulateQueryParams,
|
||||
convertFiltersQueryParams,
|
||||
convertFieldsQueryParams,
|
||||
VALID_REST_OPERATORS,
|
||||
QUERY_OPERATORS,
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* Export shared utilities
|
||||
*/
|
||||
const { buildQuery, hasDeepFilters } = require('./build-query');
|
||||
const { VALID_REST_OPERATORS, QUERY_OPERATORS } = require('./convert-query-params');
|
||||
const { QUERY_OPERATORS } = require('./convert-query-params');
|
||||
const parseMultipartData = require('./parse-multipart');
|
||||
const sanitizeEntity = require('./sanitize-entity');
|
||||
const parseType = require('./parse-type');
|
||||
@ -37,7 +37,6 @@ module.exports = {
|
||||
formatYupErrors,
|
||||
policy,
|
||||
templateConfiguration,
|
||||
VALID_REST_OPERATORS,
|
||||
QUERY_OPERATORS,
|
||||
buildQuery,
|
||||
hasDeepFilters,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user