restructure upload controllers + add create and get folders

This commit is contained in:
Pierre Noël 2022-03-22 18:19:46 +01:00
parent 0f8956db72
commit 94308aa2bc
24 changed files with 692 additions and 255 deletions

View File

@ -40,7 +40,8 @@
"react-redux": "7.2.3",
"react-router": "^5.2.0",
"react-router-dom": "5.2.0",
"sharp": "0.30.1"
"sharp": "0.30.1",
"uuid": "8.3.2"
},
"engines": {
"node": ">=12.22.0 <=16.x.x",

View File

@ -0,0 +1,14 @@
'use strict';
const ACTIONS = {
read: 'plugin::upload.read',
readSettings: 'plugin::upload.settings.read',
create: 'plugin::upload.assets.create',
update: 'plugin::upload.assets.update',
download: 'plugin::upload.assets.download',
copyLink: 'plugin::upload.assets.copy-link',
};
module.exports = {
ACTIONS,
};

View File

@ -89,5 +89,16 @@ module.exports = {
relation: 'morphToMany',
configurable: false,
},
folder: {
type: 'relation',
relation: 'manyToOne',
target: 'plugin::upload.folder',
inversedBy: 'files',
},
path: {
type: 'string',
min: 1,
required: true,
},
},
};

View File

@ -0,0 +1,7 @@
'use strict';
const schema = require('./schema');
module.exports = {
schema,
};

View File

@ -0,0 +1,54 @@
'use strict';
module.exports = {
collectionName: 'folders',
info: {
singularName: 'folder',
pluralName: 'folders',
displayName: 'Folder',
},
options: {},
pluginOptions: {
'content-manager': {
visible: false,
},
'content-type-builder': {
visible: false,
},
},
attributes: {
name: {
type: 'string',
min: 1,
required: true,
},
uid: {
type: 'string',
unique: true,
required: true,
},
parent: {
type: 'relation',
relation: 'manyToOne',
target: 'plugin::upload.folder',
inversedBy: 'children',
},
children: {
type: 'relation',
relation: 'oneToMany',
target: 'plugin::upload.folder',
mappedBy: 'parent',
},
files: {
type: 'relation',
relation: 'oneToMany',
target: 'plugin::upload.file',
mappedBy: 'folder',
},
path: {
type: 'string',
min: 1,
required: true,
},
},
};

View File

@ -1,7 +1,9 @@
'use strict';
const file = require('./file');
const folder = require('./folder');
module.exports = {
file,
folder,
};

View File

@ -1,207 +0,0 @@
'use strict';
const _ = require('lodash');
const { contentTypes: contentTypesUtils } = require('@strapi/utils');
const { ApplicationError, NotFoundError, ForbiddenError } = require('@strapi/utils').errors;
const { getService } = require('../utils');
const validateSettings = require('./validation/settings');
const validateUploadBody = require('./validation/upload');
const { CREATED_BY_ATTRIBUTE } = contentTypesUtils.constants;
const ACTIONS = {
read: 'plugin::upload.read',
readSettings: 'plugin::upload.settings.read',
create: 'plugin::upload.assets.create',
update: 'plugin::upload.assets.update',
download: 'plugin::upload.assets.download',
copyLink: 'plugin::upload.assets.copy-link',
};
const fileModel = 'plugin::upload.file';
module.exports = {
async find(ctx) {
const {
state: { userAbility },
} = ctx;
const pm = strapi.admin.services.permission.createPermissionsManager({
ability: userAbility,
action: ACTIONS.read,
model: fileModel,
});
if (!pm.isAllowed) {
return ctx.forbidden();
}
const query = pm.addPermissionsQueryTo(ctx.query);
const { results, pagination } = await getService('upload').findPage(query);
const sanitizedResults = await pm.sanitizeOutput(results);
return { results: sanitizedResults, pagination };
},
async findOne(ctx) {
const {
state: { userAbility },
params: { id },
} = ctx;
const { pm, file } = await findEntityAndCheckPermissions(
userAbility,
ACTIONS.read,
fileModel,
id
);
ctx.body = await pm.sanitizeOutput(file);
},
async destroy(ctx) {
const { id } = ctx.params;
const { userAbility } = ctx.state;
const { pm, file } = await findEntityAndCheckPermissions(
userAbility,
ACTIONS.update,
fileModel,
id
);
await getService('upload').remove(file);
ctx.body = await pm.sanitizeOutput(file, { action: ACTIONS.read });
},
async updateSettings(ctx) {
const {
request: { body },
state: { userAbility },
} = ctx;
if (userAbility.cannot(ACTIONS.readSettings, fileModel)) {
return ctx.forbidden();
}
const data = await validateSettings(body);
await getService('upload').setSettings(data);
ctx.body = { data };
},
async getSettings(ctx) {
const {
state: { userAbility },
} = ctx;
if (userAbility.cannot(ACTIONS.readSettings, fileModel)) {
return ctx.forbidden();
}
const data = await getService('upload').getSettings();
ctx.body = { data };
},
async updateFileInfo(ctx) {
const {
state: { userAbility, user },
query: { id },
request: { body },
} = ctx;
const uploadService = getService('upload');
const { pm } = await findEntityAndCheckPermissions(userAbility, ACTIONS.update, fileModel, id);
const data = await validateUploadBody(body);
const file = await uploadService.updateFileInfo(id, data.fileInfo, { user });
ctx.body = await pm.sanitizeOutput(file, { action: ACTIONS.read });
},
async replaceFile(ctx) {
const {
state: { userAbility, user },
query: { id },
request: { body, files: { files } = {} },
} = ctx;
const uploadService = getService('upload');
const { pm } = await findEntityAndCheckPermissions(userAbility, ACTIONS.update, fileModel, id);
if (Array.isArray(files)) {
throw new ApplicationError('Cannot replace a file with multiple ones');
}
const data = await validateUploadBody(body);
const replacedFiles = await uploadService.replace(id, { data, file: files }, { user });
ctx.body = await pm.sanitizeOutput(replacedFiles, { action: ACTIONS.read });
},
async uploadFiles(ctx) {
const {
state: { userAbility, user },
request: { body, files: { files } = {} },
} = ctx;
const uploadService = getService('upload');
const pm = strapi.admin.services.permission.createPermissionsManager({
ability: userAbility,
action: ACTIONS.create,
model: fileModel,
});
if (!pm.isAllowed) {
return ctx.forbidden();
}
const data = await validateUploadBody(body);
const uploadedFiles = await uploadService.upload({ data, files }, { user });
ctx.body = await pm.sanitizeOutput(uploadedFiles, { action: ACTIONS.read });
},
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 new ApplicationError('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]);
if (_.isNil(file)) {
throw new NotFoundError();
}
const pm = strapi.admin.services.permission.createPermissionsManager({ ability, action, model });
const creatorId = _.get(file, [CREATED_BY_ATTRIBUTE, 'id']);
const author = creatorId ? await strapi.admin.services.user.findOne(creatorId, ['roles']) : null;
const fileWithRoles = _.set(_.cloneDeep(file), 'createdBy', author);
if (pm.ability.cannot(pm.action, pm.toSubject(fileWithRoles))) {
throw new ForbiddenError();
}
return { pm, file };
};

View File

@ -0,0 +1,65 @@
'use strict';
const { getService } = require('../utils');
const { ACTIONS } = require('../constants');
const findEntityAndCheckPermissions = require('./utils/find-entity-and-check-permissions');
const fileModel = 'plugin::upload.file';
module.exports = {
async find(ctx) {
const {
state: { userAbility },
} = ctx;
const pm = strapi.admin.services.permission.createPermissionsManager({
ability: userAbility,
action: ACTIONS.read,
model: fileModel,
});
if (!pm.isAllowed) {
return ctx.forbidden();
}
const query = pm.addPermissionsQueryTo(ctx.query);
const { results, pagination } = await getService('upload').findPage(query);
const sanitizedResults = await pm.sanitizeOutput(results);
return { results: sanitizedResults, pagination };
},
async findOne(ctx) {
const {
state: { userAbility },
params: { id },
} = ctx;
const { pm, file } = await findEntityAndCheckPermissions(
userAbility,
ACTIONS.read,
fileModel,
id
);
ctx.body = await pm.sanitizeOutput(file);
},
async destroy(ctx) {
const { id } = ctx.params;
const { userAbility } = ctx.state;
const { pm, file } = await findEntityAndCheckPermissions(
userAbility,
ACTIONS.update,
fileModel,
id
);
await getService('upload').remove(file);
ctx.body = await pm.sanitizeOutput(file, { action: ACTIONS.read });
},
};

View File

@ -0,0 +1,74 @@
'use strict';
const { setCreatorFields, pipeAsync } = require('@strapi/utils');
const { ApplicationError } = require('@strapi/utils').errors;
const { getService } = require('../utils');
const { validateCreateFolder } = require('./validation/folder');
const folderModel = 'plugin::upload.folder';
module.exports = {
async find(ctx) {
const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
ability: ctx.state.userAbility,
model: folderModel,
});
const { results, pagination } = await strapi.entityService.findWithRelationCounts(folderModel, {
...ctx.query,
populate: {
children: {
count: true,
},
files: {
count: true,
},
parent: true,
createdBy: true,
updatedBy: true,
},
});
ctx.body = {
results: await permissionsManager.sanitizeOutput(results),
pagination,
};
},
async create(ctx) {
const { user } = ctx.state;
const { body, query } = ctx.request;
await validateCreateFolder(body);
const existingFolders = await strapi.entityService.findMany(folderModel, {
filters: {
parent: body.parent,
name: body.name,
},
});
if (existingFolders.length > 0) {
throw new ApplicationError('name already taken');
}
const { setPathAndUID } = getService('folder');
// TODO: wrap with a transaction
const enrichFolder = pipeAsync(setPathAndUID, setCreatorFields({ user }));
const enrichedFolder = await enrichFolder(body);
const folder = await strapi.entityService.create(folderModel, {
...query,
data: enrichedFolder,
});
const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
ability: ctx.state.userAbility,
model: folderModel,
});
ctx.body = {
data: await permissionsManager.sanitizeOutput(folder),
};
},
};

View File

@ -0,0 +1,40 @@
'use strict';
const { getService } = require('../utils');
const { ACTIONS } = require('../constants');
const validateSettings = require('./validation/settings');
const fileModel = 'plugin::upload.file';
module.exports = {
async updateSettings(ctx) {
const {
request: { body },
state: { userAbility },
} = ctx;
if (userAbility.cannot(ACTIONS.readSettings, fileModel)) {
return ctx.forbidden();
}
const data = await validateSettings(body);
await getService('upload').setSettings(data);
ctx.body = { data };
},
async getSettings(ctx) {
const {
state: { userAbility },
} = ctx;
if (userAbility.cannot(ACTIONS.readSettings, fileModel)) {
return ctx.forbidden();
}
const data = await getService('upload').getSettings();
ctx.body = { data };
},
};

View File

@ -0,0 +1,88 @@
'use strict';
const _ = require('lodash');
const { ApplicationError } = require('@strapi/utils').errors;
const { getService } = require('../utils');
const { ACTIONS } = require('../constants');
const validateUploadBody = require('./validation/upload');
const findEntityAndCheckPermissions = require('./utils/find-entity-and-check-permissions');
const fileModel = 'plugin::upload.file';
module.exports = {
async updateFileInfo(ctx) {
const {
state: { userAbility, user },
query: { id },
request: { body },
} = ctx;
const uploadService = getService('upload');
const { pm } = await findEntityAndCheckPermissions(userAbility, ACTIONS.update, fileModel, id);
const data = await validateUploadBody(body);
const file = await uploadService.updateFileInfo(id, data.fileInfo, { user });
ctx.body = await pm.sanitizeOutput(file, { action: ACTIONS.read });
},
async replaceFile(ctx) {
const {
state: { userAbility, user },
query: { id },
request: { body, files: { files } = {} },
} = ctx;
const uploadService = getService('upload');
const { pm } = await findEntityAndCheckPermissions(userAbility, ACTIONS.update, fileModel, id);
if (Array.isArray(files)) {
throw new ApplicationError('Cannot replace a file with multiple ones');
}
const data = await validateUploadBody(body);
const replacedFiles = await uploadService.replace(id, { data, file: files }, { user });
ctx.body = await pm.sanitizeOutput(replacedFiles, { action: ACTIONS.read });
},
async uploadFiles(ctx) {
const {
state: { userAbility, user },
request: { body, files: { files } = {} },
} = ctx;
const uploadService = getService('upload');
const pm = strapi.admin.services.permission.createPermissionsManager({
ability: userAbility,
action: ACTIONS.create,
model: fileModel,
});
if (!pm.isAllowed) {
return ctx.forbidden();
}
const data = await validateUploadBody(body);
const uploadedFiles = await uploadService.upload({ data, files }, { user });
ctx.body = await pm.sanitizeOutput(uploadedFiles, { action: ACTIONS.read });
},
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 new ApplicationError('Files are empty');
}
await (id ? this.replaceFile : this.uploadFiles)(ctx);
},
};

View File

@ -3,7 +3,6 @@
const _ = require('lodash');
const utils = require('@strapi/utils');
const { getService } = require('../utils');
const validateSettings = require('./validation/settings');
const validateUploadBody = require('./validation/upload');
const { sanitize } = utils;
@ -57,24 +56,6 @@ module.exports = {
ctx.body = await sanitizeOutput(file, ctx);
},
async updateSettings(ctx) {
const {
request: { body },
} = ctx;
const data = await validateSettings(body);
await getService('upload').setSettings(data);
ctx.body = { data };
},
async getSettings(ctx) {
const data = await getService('upload').getSettings();
ctx.body = { data };
},
async updateFileInfo(ctx) {
const {
query: { id },
@ -135,15 +116,4 @@ module.exports = {
await (id ? this.replaceFile : this.uploadFiles)(ctx);
},
async search(ctx) {
const { id } = ctx.params;
const entries = await strapi.query('plugin::upload.file').findMany({
where: {
$or: [{ hash: { $contains: id } }, { name: { $contains: id } }],
},
});
ctx.body = await sanitizeOutput(entries, ctx);
},
};

View File

@ -1,9 +1,15 @@
'use strict';
const adminApi = require('./admin-api');
const adminFile = require('./admin-file');
const adminFolder = require('./admin-folder');
const adminSettings = require('./admin-settings');
const adminUpload = require('./admin-upload');
const contentApi = require('./content-api');
module.exports = {
'admin-api': adminApi,
'admin-file': adminFile,
'admin-folder': adminFolder,
'admin-settings': adminSettings,
'admin-upload': adminUpload,
'content-api': contentApi,
};

View File

@ -0,0 +1,33 @@
'use strict';
const _ = require('lodash');
const { contentTypes: contentTypesUtils } = require('@strapi/utils');
const { NotFoundError, ForbiddenError } = require('@strapi/utils').errors;
const { getService } = require('../../utils');
const { CREATED_BY_ATTRIBUTE } = contentTypesUtils.constants;
const findEntityAndCheckPermissions = async (ability, action, model, id) => {
const file = await getService('upload').findOne(id, [CREATED_BY_ATTRIBUTE]);
if (_.isNil(file)) {
throw new NotFoundError();
}
const pm = strapi.admin.services.permission.createPermissionsManager({ ability, action, model });
const creatorId = _.get(file, [CREATED_BY_ATTRIBUTE, 'id']);
const author = creatorId ? await strapi.admin.services.user.findOne(creatorId, ['roles']) : null;
const fileWithRoles = _.set(_.cloneDeep(file), 'createdBy', author);
if (pm.ability.cannot(pm.action, pm.toSubject(fileWithRoles))) {
throw new ForbiddenError();
}
return { pm, file };
};
module.exports = {
findEntityAndCheckPermissions,
};

View File

@ -0,0 +1,22 @@
'use strict';
const { yup, validateYupSchema } = require('@strapi/utils');
const NO_SLASH_REGEX = /^[^/]+$/;
const validateCreateFolderSchema = yup
.object()
.shape({
name: yup
.string()
.min(1)
.matches(NO_SLASH_REGEX, 'name cannot contain slashes')
.required(),
parent: yup.strapiID().nullable(),
})
.noUnknown()
.required();
module.exports = {
validateCreateFolder: validateYupSchema(validateCreateFolderSchema),
};

View File

@ -6,7 +6,7 @@ module.exports = {
{
method: 'GET',
path: '/settings',
handler: 'admin-api.getSettings',
handler: 'admin-settings.getSettings',
config: {
policies: [
'admin::isAuthenticatedAdmin',
@ -22,7 +22,7 @@ module.exports = {
{
method: 'PUT',
path: '/settings',
handler: 'admin-api.updateSettings',
handler: 'admin-settings.updateSettings',
config: {
policies: [
'admin::isAuthenticatedAdmin',
@ -38,7 +38,7 @@ module.exports = {
{
method: 'POST',
path: '/',
handler: 'admin-api.upload',
handler: 'admin-upload.upload',
config: {
policies: ['admin::isAuthenticatedAdmin'],
},
@ -46,7 +46,7 @@ module.exports = {
{
method: 'GET',
path: '/files',
handler: 'admin-api.find',
handler: 'admin-file.find',
config: {
policies: [
'admin::isAuthenticatedAdmin',
@ -62,7 +62,7 @@ module.exports = {
{
method: 'GET',
path: '/files/:id',
handler: 'admin-api.findOne',
handler: 'admin-file.findOne',
config: {
policies: [
'admin::isAuthenticatedAdmin',
@ -78,7 +78,7 @@ module.exports = {
{
method: 'DELETE',
path: '/files/:id',
handler: 'admin-api.destroy',
handler: 'admin-file.destroy',
config: {
policies: [
'admin::isAuthenticatedAdmin',
@ -91,5 +91,37 @@ module.exports = {
],
},
},
{
method: 'GET',
path: '/folders',
handler: 'admin-folder.find',
config: {
policies: [
'admin::isAuthenticatedAdmin',
{
name: 'admin::hasPermissions',
config: {
actions: ['plugin::upload.read'],
},
},
],
},
},
{
method: 'POST',
path: '/folders',
handler: 'admin-folder.create',
config: {
policies: [
'admin::isAuthenticatedAdmin',
{
name: 'admin::hasPermissions',
config: {
actions: ['plugin::upload.read'],
},
},
],
},
},
],
};

View File

@ -0,0 +1,42 @@
'use strict';
const uuid = require('uuid/v4');
const { trimChars, trimCharsEnd, trimCharsStart } = require('lodash/fp');
// TODO: to use once https://github.com/strapi/strapi/pull/12534 is merged
// const { joinBy } = require('@strapi/utils');
const folderModel = 'plugin::upload.folder';
const joinBy = (joint, ...args) => {
const trim = trimChars(joint);
const trimEnd = trimCharsEnd(joint);
const trimStart = trimCharsStart(joint);
return args.reduce((url, path, index) => {
if (args.length === 1) return path;
if (index === 0) return trimEnd(path);
if (index === args.length - 1) return url + joint + trimStart(path);
return url + joint + trim(path);
}, '');
};
const generateUID = () => uuid();
const setPathAndUID = async folder => {
let parentPath = '/';
if (folder.parent) {
const parentFolder = await strapi.entityService.findOne(folderModel, folder.parent);
parentPath = parentFolder.path;
}
return Object.assign(folder, {
uid: generateUID(),
path: joinBy('/', parentPath, folder.name),
});
};
module.exports = {
generateUID,
setPathAndUID,
};

View File

@ -1,11 +1,13 @@
'use strict';
const providerService = require('./provider');
const uploadService = require('./upload');
const provider = require('./provider');
const upload = require('./upload');
const imageManipulation = require('./image-manipulation');
const folder = require('./folder');
module.exports = {
provider: providerService,
upload: uploadService,
provider,
upload,
folder,
'image-manipulation': imageManipulation,
};

View File

@ -0,0 +1,181 @@
'use strict';
// Test a simple default API with no relations
const { omit } = require('lodash/fp');
const { createTestBuilder } = require('../../../../../test/helpers/builder');
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
const { createAuthRequest } = require('../../../../../test/helpers/request');
let strapi;
let rq;
let data = {
folders: [],
};
describe('Folder', () => {
const builder = createTestBuilder();
beforeAll(async () => {
strapi = await createStrapiInstance();
rq = await createAuthRequest({ strapi });
});
afterAll(async () => {
await strapi.destroy();
await builder.cleanup();
});
describe('create', () => {
test('Can create a folder at root level', async () => {
const res = await rq({
method: 'POST',
url: '/upload/folders?populate=parent',
body: {
name: 'folder-1',
parent: null,
},
});
expect(res.body.data).toMatchObject({
id: expect.anything(),
name: 'folder-1',
uid: expect.anything(),
path: '/folder-1',
createdAt: expect.anything(),
updatedAt: expect.anything(),
parent: null,
});
data.folders.push(omit('parent', res.body.data));
});
test('Cannot create a folder with duplicated name at root level', async () => {
const res = await rq({
method: 'POST',
url: '/upload/folders?populate=parent',
body: {
name: 'folder-1',
parent: null,
},
});
expect(res.status).toBe(400);
expect(res.body.error.message).toBe('name already taken');
});
test('Cannot create a folder with path containing a slash', async () => {
const res = await rq({
method: 'POST',
url: '/upload/folders?populate=parent',
body: {
name: 'folder-1/2',
parent: null,
},
});
expect(res.status).toBe(400);
expect(res.body.error.message).toBe('name cannot contain slashes');
});
test('Can create a folder inside another folder', async () => {
const res = await rq({
method: 'POST',
url: '/upload/folders?populate=parent',
body: {
name: 'folder-2',
parent: data.folders[0].id,
},
});
expect(res.body.data).toMatchObject({
id: expect.anything(),
name: 'folder-2',
uid: expect.anything(),
path: '/folder-1/folder-2',
createdAt: expect.anything(),
updatedAt: expect.anything(),
parent: data.folders[0],
});
data.folders.push(omit('parent', res.body.data));
});
});
describe('read', () => {
test('Can read folders', async () => {
const res = await rq({
method: 'GET',
url: '/upload/folders',
});
expect(res.body.pagination).toMatchObject({
page: 1,
pageCount: 1,
pageSize: 10,
total: 2,
});
expect(res.body.results).toHaveLength(2);
expect(res.body.results).toEqual(
expect.arrayContaining([
{
children: { count: 1 },
createdAt: expect.anything(),
createdBy: {
firstname: 'admin',
id: expect.anything(),
lastname: 'admin',
username: null,
},
files: { count: 0 },
id: expect.anything(),
name: 'folder-1',
parent: null,
path: '/folder-1',
uid: expect.anything(),
updatedAt: expect.anything(),
updatedBy: {
firstname: 'admin',
id: expect.anything(),
lastname: 'admin',
username: null,
},
},
{
children: { count: 0 },
createdAt: expect.anything(),
createdBy: {
firstname: 'admin',
id: expect.anything(),
lastname: 'admin',
username: null,
},
files: {
count: 0,
},
id: expect.anything(),
name: 'folder-2',
parent: {
createdAt: expect.anything(),
id: expect.anything(),
name: 'folder-1',
path: '/folder-1',
uid: expect.anything(),
updatedAt: expect.anything(),
},
path: '/folder-1/folder-2',
uid: expect.anything(),
updatedAt: expect.anything(),
updatedBy: {
firstname: 'admin',
id: expect.anything(),
lastname: 'admin',
username: null,
},
},
])
);
});
});
});

View File

@ -3,8 +3,8 @@
const fs = require('fs');
const path = require('path');
const { createStrapiInstance } = require('../../../../test/helpers/strapi');
const { createAuthRequest } = require('../../../../test/helpers/request');
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
const { createAuthRequest } = require('../../../../../test/helpers/request');
let strapi;
let rq;

View File

Before

Width:  |  Height:  |  Size: 787 B

After

Width:  |  Height:  |  Size: 787 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -4,9 +4,9 @@ const fs = require('fs');
const path = require('path');
// Helpers.
const { createTestBuilder } = require('../../../../test/helpers/builder');
const { createStrapiInstance } = require('../../../../test/helpers/strapi');
const { createAuthRequest } = require('../../../../test/helpers/request');
const { createTestBuilder } = require('../../../../../test/helpers/builder');
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
const { createAuthRequest } = require('../../../../../test/helpers/request');
const builder = createTestBuilder();
let strapi;