Ben Irvin 801e3db415 add traverse query
fix single type

fix query

sanitize pagination count params

add comments

Cleanup the params/filters sanitize helpers

sanitize association resolver

Sanitize sort

fix graphql single type

fix graphql types

fix addFindQuery

Sanitize fields

Update sanitize sort to handle all the different formats

Update fields sanitize to handle regular strings & wildcard

Fix non scalar recursion

Add a traverse factory

Add visitor to remove dz & morph relations

Replace the old traverse utils (sort, filters) by one created using the traverse factory

add sanitize populate

await args

fix async and duplicate sanitization

sanitize u&p params

Add traverse fields

Fix traverse & sanitize fields

add traverse fields to nested populate

sanitize admin api filter queries

Co-authored-by: Jean-Sébastien Herbaux <Convly@users.noreply.github.com>

sanitize sort params in admin API

todo

make token fields unsearchable with _q

sanitize delete mutation

Update packages/core/admin/server/services/permission/permissions-manager/sanitize.js

Co-authored-by: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>

fix errors on queries without ctx

rename findParams to sanitizedParams

Sanitize queries everywhere in the content manager admin controllers

sanitize single type update and delete

Ignore non attribute keys in the sanitize sort

Fix the sanitize query sort for nested string sort

Fix permission check for the admin

typo

sanitize upload

sanitize admin media library

sanitize admin users

Add missing await

Co-authored-by: Jean-Sébastien Herbaux <Convly@users.noreply.github.com>

set U&P users fields to searchable:false

add token support to createContentAPIRequest

add searchable:false to getstarted U&P schema

remove comment

sanitize component resolver

remove await

add searchable false to the file's folder path

Fix admin query when the permission query is set to null

add basic tests for filtering private params

add tests for fields

add pagination tests

Fix admin user fields not being sanitized

Fix convert query params for the morph fragment on undefined value

Traverse dynamic zone on nested populate

Handle nested sort, filters & fields in populate queries + handle populate fragment for morphTo relations

Sanitize 'on' subpopulate

Co-authored-by: Jean-Sébastien Herbaux <Convly@users.noreply.github.com>

don't throw error on invalid attributes

check models for snake case column name instead of assuming they are operators

Add first batch of api tests for params sanitize

Fix sort traversal: handle object arrays

Put back removePassword for fields,sort,filters

Add schemas and fixtures for sanitize api tests

Add tests for relations (sanitize api tests)

Move constant to domain scope

Rename sanitize params to sanitize query

Fix typo

Cleanup fixtures file

Fix variable name conflict

Update packages/core/admin/server/services/permission/permissions-manager/sanitize.js

Co-authored-by: Alexandre BODIN <alexandrebodin@users.noreply.github.com>

Update comment for array filters

Rename sanitize test

Test implicit & explicit array operator for filter

Remove unused code
2023-03-15 14:59:19 +01:00

378 lines
10 KiB
JavaScript

'use strict';
const fs = require('fs');
const path = require('path');
const get = require('lodash/get');
// Helpers.
const { createTestBuilder } = require('../../../../../test/helpers/builder');
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
const builder = createTestBuilder();
const data = { dogs: [] };
let strapi;
let rq;
const dogModel = {
displayName: 'Dog',
singularName: 'dog',
pluralName: 'dogs',
kind: 'collectionType',
attributes: {
profilePicture: {
type: 'media',
},
},
};
const todoListModel = {
displayName: 'TodoList',
singularName: 'todolist',
pluralName: 'todolists',
kind: 'collectionType',
attributes: {
title: {
type: 'string',
},
todo: {
displayName: 'todo',
type: 'component',
repeatable: true,
component: 'default.todo',
},
},
};
const todoComponent = {
displayName: 'Todo',
attributes: {
docs: {
allowedTypes: ['images', 'files', 'videos', 'audios'],
type: 'media',
multiple: true,
},
task: {
type: 'string',
},
},
};
describe('Upload plugin', () => {
beforeAll(async () => {
await builder
.addContentType(dogModel)
.addComponent(todoComponent)
.addContentType(todoListModel)
.build();
strapi = await createStrapiInstance();
rq = createContentAPIRequest({ strapi });
});
afterAll(async () => {
await strapi.destroy();
await builder.cleanup();
});
describe('Create', () => {
test('Simple image upload', async () => {
const res = await rq({
method: 'POST',
url: '/upload',
formData: {
files: fs.createReadStream(path.join(__dirname, '../utils/rec.jpg')),
},
});
expect(res.statusCode).toBe(200);
expect(Array.isArray(res.body)).toBe(true);
expect(res.body.length).toBe(1);
expect(res.body[0]).toEqual(
expect.objectContaining({
id: expect.anything(),
name: 'rec.jpg',
ext: '.jpg',
mime: 'image/jpeg',
hash: expect.any(String),
size: expect.any(Number),
width: expect.any(Number),
height: expect.any(Number),
url: expect.any(String),
provider: 'local',
})
);
});
test('Rejects when no files are provided', async () => {
const res = await rq({ method: 'POST', url: '/upload', formData: {} });
expect(res.statusCode).toBe(400);
});
test('Generates a thumbnail on large enough files', async () => {
const res = await rq({
method: 'POST',
url: '/upload',
formData: {
files: fs.createReadStream(path.join(__dirname, '../utils/thumbnail_target.png')),
},
});
expect(res.statusCode).toBe(200);
expect(Array.isArray(res.body)).toBe(true);
expect(res.body.length).toBe(1);
expect(res.body[0]).toEqual(
expect.objectContaining({
id: expect.anything(),
name: 'thumbnail_target.png',
ext: '.png',
mime: 'image/png',
hash: expect.any(String),
size: expect.any(Number),
width: expect.any(Number),
height: expect.any(Number),
url: expect.any(String),
provider: 'local',
formats: {
thumbnail: {
name: 'thumbnail_thumbnail_target.png',
hash: expect.any(String),
ext: '.png',
mime: 'image/png',
size: expect.any(Number),
width: expect.any(Number),
height: expect.any(Number),
url: expect.any(String),
path: null,
},
},
})
);
});
});
describe('Read', () => {
test('Get files', async () => {
const getRes = await rq({ method: 'GET', url: '/upload/files' });
expect(getRes.statusCode).toBe(200);
expect(getRes.body).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: expect.anything(),
url: expect.any(String),
}),
])
);
});
test('Get one file', async () => {
const dogEntity = await strapi.entityService.create('api::dog.dog', {
data: {},
files: {
profilePicture: {
path: path.join(__dirname, '../utils/rec.jpg'),
name: 'rec',
type: 'jpg',
size: 0,
},
},
populate: 'profilePicture',
});
const getRes = await rq({
method: 'GET',
url: `/upload/files/${dogEntity.profilePicture.id}`,
});
expect(getRes.statusCode).toBe(200);
expect(getRes.body).toEqual(
expect.objectContaining({
id: expect.anything(),
url: expect.any(String),
})
);
await strapi.entityService.delete('api::dog.dog', dogEntity.id);
await strapi.entityService.delete('plugin::upload.file', dogEntity.profilePicture.id);
});
});
describe('Create an entity with a file', () => {
test('With an image', async () => {
const res = await rq({
method: 'POST',
url: '/dogs?populate=*',
formData: {
data: '{}',
'files.profilePicture': fs.createReadStream(path.join(__dirname, '../utils/rec.jpg')),
},
});
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject({
data: {
attributes: {
profilePicture: {
data: {
id: expect.anything(),
attributes: {
provider: 'local',
},
},
},
},
id: expect.anything(),
},
});
data.dogs.push(res.body);
});
test('With a pdf', async () => {
const res = await rq({
method: 'POST',
url: '/dogs?populate=*',
formData: {
data: '{}',
'files.profilePicture': fs.createReadStream(path.join(__dirname, '../utils/rec.pdf')),
},
});
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject({
data: {
attributes: {
profilePicture: {
data: {
id: expect.anything(),
attributes: {
provider: 'local',
},
},
},
},
id: expect.anything(),
},
});
data.dogs.push(res.body);
});
test('File should have related field', async () => {
const fileId = get(data, 'dogs[0].data.attributes.profilePicture.data.id');
expect(fileId).toBeDefined();
const getRes = await rq({
method: 'GET',
url: `/upload/files/${fileId}`,
qs: { populate: '*' },
});
expect(getRes.statusCode).toBe(200);
expect(getRes.body).toEqual(
expect.objectContaining({
id: fileId,
related: [expect.any(Object)],
})
);
});
});
describe('Create an entity with a component with a file', () => {
test('With an image', async () => {
const res = await rq({
method: 'POST',
url: '/todolists',
formData: {
data: '{"title":"Test todolist title","todo":[{"task":"First todo"},{"task":"Second todo"}]}',
'files.todo.0.docs': fs.createReadStream(path.join(__dirname, '../utils/rec.jpg')),
},
});
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject({
data: {
attributes: {
title: 'Test todolist title',
},
id: expect.anything(),
},
});
const newlyCreatedTodolist = await rq({
method: 'GET',
url: `/todolists/${res.body.data.id}`,
qs: {
populate: ['todo', 'todo.docs'],
},
});
expect(newlyCreatedTodolist.body).toBeDefined();
expect(newlyCreatedTodolist.body).toMatchObject({
data: {
attributes: {
title: 'Test todolist title',
todo: [
{
id: expect.anything(),
task: 'First todo',
docs: {
data: [
{
id: expect.anything(),
attributes: {
mime: 'image/jpeg',
name: 'rec.jpg',
},
},
],
},
},
expect.any(Object),
],
},
},
});
});
});
// see https://github.com/strapi/strapi/issues/14125
describe('File relations are correctly removed', () => {
test('Update an entity with a file correctly removes the relation between the entity and its old file', async () => {
const res = await rq({
method: 'PUT',
url: `/dogs/${data.dogs[0].data.id}?populate=*`,
formData: {
data: '{}',
'files.profilePicture': fs.createReadStream(path.join(__dirname, '../utils/strapi.jpg')),
},
});
expect(res.statusCode).toBe(200);
expect(res.body.data.attributes.profilePicture.data.id).not.toBe(
data.dogs[0].data.attributes.profilePicture.data.id
);
data.dogs[0] = res.body;
});
test('Update a file with an entity correctly removes the relation between the entity and its old file', async () => {
const fileId = data.dogs[1].data.attributes.profilePicture.data.id;
await strapi.entityService.update('plugin::upload.file', fileId, {
data: {
related: [
{
id: data.dogs[0].data.id,
__type: 'api::dog.dog',
__pivot: { field: 'profilePicture' },
},
],
},
});
const res = await rq({
method: 'GET',
url: `/dogs/${data.dogs[0].data.id}?populate=*`,
});
expect(res.body.data.attributes.profilePicture.data.id).toBe(fileId);
data.dogs[0] = res.body;
});
});
});