Apply feedbacks

Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com>
This commit is contained in:
Alexandre Bodin 2020-11-06 19:25:09 +01:00
parent 25d541c2d9
commit 04c85dd218
14 changed files with 434 additions and 80 deletions

View File

@ -4,7 +4,7 @@ const createContext = require('../../../../test/helpers/create-context');
const singleTypes = require('../single-types');
describe('Single Types', () => {
test('find', async () => {
test('Successfull find', async () => {
const state = {
userAbility: {
can: jest.fn(),
@ -73,4 +73,390 @@ describe('Single Types', () => {
expect(permissionChecker.cannot.create).toHaveBeenCalled();
expect(notFound).toHaveBeenCalled();
});
test('Successfull create', async () => {
const modelUid = 'test-uid';
const state = {
userAbility: {
can: jest.fn(),
cannot: jest.fn(() => false),
},
user: {
id: 1,
},
};
const createPermissionsManager = jest.fn(() => ({
ability: state.userAbility,
}));
const permissionChecker = {
cannot: {
update: jest.fn(() => false),
create: jest.fn(() => false),
},
sanitizeCreateInput: obj => obj,
sanitizeOutput: obj => obj,
};
const createFn = jest.fn(() => ({}));
const sendTelemetry = jest.fn(() => ({}));
global.strapi = {
admin: {
services: {
permission: {
createPermissionsManager,
},
},
},
getModel() {
return {
options: {
draftAndPublish: true,
},
attributes: {
title: {
type: 'string',
},
},
};
},
plugins: {
'content-manager': {
services: {
'entity-manager': {
find() {
return Promise.resolve();
},
assocCreatorRoles(enitty) {
return enitty;
},
create: createFn,
},
'permission-checker': {
create() {
return permissionChecker;
},
},
},
},
},
entityService: {
find: jest.fn(),
},
telemetry: {
send: sendTelemetry,
},
};
const ctx = createContext(
{
params: {
model: modelUid,
},
body: {
title: 'test',
},
},
{ state }
);
await singleTypes.createOrUpdate(ctx);
expect(permissionChecker.cannot.create).toHaveBeenCalled();
expect(createFn).toHaveBeenCalledWith(
expect.objectContaining({
title: 'test',
created_by: 1,
updated_by: 1,
}),
modelUid
);
expect(sendTelemetry).toHaveBeenCalledWith('didCreateFirstContentTypeEntry', {
model: modelUid,
});
});
test('Successfull delete', async () => {
const modelUid = 'test-uid';
const entity = {
id: 1,
title: 'entityTitle',
};
const state = {
userAbility: {
can: jest.fn(),
cannot: jest.fn(() => false),
},
user: {
id: 1,
},
};
const createPermissionsManager = jest.fn(() => ({
ability: state.userAbility,
}));
const permissionChecker = {
cannot: {
delete: jest.fn(() => false),
},
sanitizeOutput: jest.fn(obj => obj),
};
const deleteFn = jest.fn(() => ({}));
global.strapi = {
admin: {
services: {
permission: {
createPermissionsManager,
},
},
},
getModel() {
return {
options: {
draftAndPublish: true,
},
attributes: {
title: {
type: 'string',
},
},
};
},
plugins: {
'content-manager': {
services: {
'entity-manager': {
find() {
return Promise.resolve(entity);
},
assocCreatorRoles(enitty) {
return enitty;
},
delete: deleteFn,
},
'permission-checker': {
create() {
return permissionChecker;
},
},
},
},
},
entityService: {
find: jest.fn(),
},
};
const ctx = createContext(
{
params: {
id: entity.id,
model: modelUid,
},
},
{ state }
);
await singleTypes.delete(ctx);
expect(deleteFn).toHaveBeenCalledWith(entity, modelUid);
expect(permissionChecker.cannot.delete).toHaveBeenCalledWith(entity);
expect(permissionChecker.sanitizeOutput).toHaveBeenCalled();
});
test('Successfull publish', async () => {
const modelUid = 'test-uid';
const entity = {
id: 1,
title: 'entityTitle',
};
const state = {
userAbility: {
can: jest.fn(),
cannot: jest.fn(() => false),
},
user: {
id: 1,
},
};
const createPermissionsManager = jest.fn(() => ({
ability: state.userAbility,
}));
const permissionChecker = {
cannot: {
publish: jest.fn(() => false),
},
sanitizeOutput: jest.fn(obj => obj),
};
const publishFn = jest.fn(() => ({}));
global.strapi = {
admin: {
services: {
permission: {
createPermissionsManager,
},
},
},
getModel() {
return {
options: {
draftAndPublish: true,
},
attributes: {
title: {
type: 'string',
},
},
};
},
plugins: {
'content-manager': {
services: {
'entity-manager': {
find() {
return Promise.resolve(entity);
},
assocCreatorRoles(enitty) {
return enitty;
},
publish: publishFn,
},
'permission-checker': {
create() {
return permissionChecker;
},
},
},
},
},
entityService: {
find: jest.fn(),
},
};
const ctx = createContext(
{
params: {
id: entity.id,
model: modelUid,
},
},
{ state }
);
await singleTypes.publish(ctx);
expect(publishFn).toHaveBeenCalledWith(entity, modelUid);
expect(permissionChecker.cannot.publish).toHaveBeenCalledWith(entity);
expect(permissionChecker.sanitizeOutput).toHaveBeenCalled();
});
test('Successfull unpublish', async () => {
const modelUid = 'test-uid';
const entity = {
id: 1,
title: 'entityTitle',
};
const state = {
userAbility: {
can: jest.fn(),
cannot: jest.fn(() => false),
},
user: {
id: 1,
},
};
const createPermissionsManager = jest.fn(() => ({
ability: state.userAbility,
}));
const permissionChecker = {
cannot: {
unpublish: jest.fn(() => false),
},
sanitizeOutput: jest.fn(obj => obj),
};
const unpublishFn = jest.fn(() => ({}));
global.strapi = {
admin: {
services: {
permission: {
createPermissionsManager,
},
},
},
getModel() {
return {
options: {
draftAndPublish: true,
},
attributes: {
title: {
type: 'string',
},
},
};
},
plugins: {
'content-manager': {
services: {
'entity-manager': {
find() {
return Promise.resolve(entity);
},
assocCreatorRoles(enitty) {
return enitty;
},
unpublish: unpublishFn,
},
'permission-checker': {
create() {
return permissionChecker;
},
},
},
},
},
entityService: {
find: jest.fn(),
},
};
const ctx = createContext(
{
params: {
id: entity.id,
model: modelUid,
},
},
{ state }
);
await singleTypes.unpublish(ctx);
expect(unpublishFn).toHaveBeenCalledWith(entity, modelUid);
expect(permissionChecker.cannot.unpublish).toHaveBeenCalledWith(entity);
expect(permissionChecker.sanitizeOutput).toHaveBeenCalled();
});
});

View File

@ -8,6 +8,7 @@ const {
setCreatorFields,
pickWritableAttributes,
} = require('../utils');
const { validateBulkDeleteInput } = require('./validation');
module.exports = {
async find(ctx) {
@ -203,6 +204,8 @@ module.exports = {
const { query, body } = ctx.request;
const { ids } = body;
await validateBulkDeleteInput(body);
const entityManager = getService('entity-manager');
const permissionChecker = getService('permission-checker').create({ userAbility, model });
@ -214,7 +217,6 @@ module.exports = {
const idsWhereClause = { [`id_in`]: ids };
const params = {
_limit: 100,
...permissionQuery,
_where: [idsWhereClause].concat(permissionQuery._where || {}),
};

View File

@ -7,6 +7,10 @@ const createModelConfigurationSchema = require('./model-configuration');
const TYPES = ['singleType', 'collectionType'];
const handleError = error => {
throw strapi.errors.badRequest('ValidationError', formatYupErrors(error));
};
/**
* Validates type kind
*/
@ -19,6 +23,23 @@ const validateKind = kind => {
.catch(error => Promise.reject(formatYupErrors(error)));
};
const validateBulkDeleteInput = (data = {}) => {
return yup
.object({
ids: yup
.array()
.of(yup.strapiID())
.min(1)
.required(),
})
.required()
.validate(data, {
strict: true,
abortEarly: false,
})
.catch(handleError);
};
const validateGenerateUIDInput = data => {
return yup
.object({
@ -30,9 +51,7 @@ const validateGenerateUIDInput = data => {
strict: true,
abortEarly: false,
})
.catch(error => {
throw strapi.errors.badRequest('ValidationError', formatYupErrors(error));
});
.catch(handleError);
};
const validateCheckUIDAvailabilityInput = data => {
@ -49,9 +68,7 @@ const validateCheckUIDAvailabilityInput = data => {
strict: true,
abortEarly: false,
})
.catch(error => {
throw strapi.errors.badRequest('ValidationError', formatYupErrors(error));
});
.catch(handleError);
};
const validateUIDField = (contentTypeUID, field) => {
@ -74,6 +91,7 @@ const validateUIDField = (contentTypeUID, field) => {
module.exports = {
createModelConfigurationSchema,
validateKind,
validateBulkDeleteInput,
validateGenerateUIDInput,
validateCheckUIDAvailabilityInput,
validateUIDField,

View File

@ -60,7 +60,7 @@ module.exports = ({ isComponent, prefix, storeUtils, getModels }) => {
const contentTypesToAdd = difference(currentUIDS, DBUIDs);
const contentTypesToDelete = difference(DBUIDs, currentUIDS);
// delette old schemas
// delete old schemas
await Promise.all(contentTypesToDelete.map(uid => deleteConfiguration(uid)));
// create new schemas

View File

@ -250,8 +250,6 @@ describe('Core API - Basic + dz', () => {
body: product,
});
console.log(res.body.data);
expect(res.statusCode).toBe(400);
expect(_.get(res.body.data, ['errors', 'dz[0].__component', '0'])).toBe(
'dz[0].__component is a required field'

View File

@ -1,8 +1,8 @@
'use strict';
module.exports = fn => async () => {
module.exports = fn => async (...args) => {
try {
await fn();
await fn(...args);
} catch (error) {
if (strapi.errors.isBoom(error)) {
throw error;

View File

@ -18,26 +18,6 @@ let data;
let rq;
let modelsUtils;
const deleteFixtures = async () => {
for (const [name, modelName] of [
['references', 'reference'],
['tags', 'tag'],
['products', 'product'],
['categories', 'category'],
['articles', 'article'],
]) {
const uid = `application::${modelName}.${modelName}`;
await rq({
method: 'POST',
url: `/content-manager/collection-types/${uid}/actions/bulkDelete`,
body: {
ids: (data[name] || []).map(({ id }) => id),
},
});
}
};
describe('Create Strapi API End to End', () => {
beforeAll(async () => {
const token = await registerAndLogin();
@ -52,9 +32,12 @@ describe('Create Strapi API End to End', () => {
form.reference,
form.product,
]);
await modelsUtils.cleanupContentTypes(['article', 'tag', 'category', 'reference', 'product']);
}, 60000);
afterAll(async () => {
await modelsUtils.cleanupContentTypes(['article', 'tag', 'category', 'reference', 'product']);
await modelsUtils.deleteContentTypes(['article', 'tag', 'category', 'reference', 'product']);
}, 60000);
@ -67,7 +50,7 @@ describe('Create Strapi API End to End', () => {
});
afterAll(async () => {
await deleteFixtures();
await modelsUtils.cleanupContentTypes(['article', 'tag']);
});
test('Create tag1', async () => {
@ -260,7 +243,7 @@ describe('Create Strapi API End to End', () => {
});
afterAll(async () => {
await deleteFixtures();
await modelsUtils.cleanupContentTypes(['article', 'category']);
});
test('Create cat1', async () => {
@ -483,7 +466,7 @@ describe('Create Strapi API End to End', () => {
});
afterAll(async () => {
await deleteFixtures();
await modelsUtils.cleanupContentTypes(['article', 'reference']);
});
test('Create ref1', async () => {
@ -572,7 +555,7 @@ describe('Create Strapi API End to End', () => {
});
afterAll(async () => {
await deleteFixtures();
await modelsUtils.cleanupContentTypes(['reference', 'tag']);
});
test('Attach Tag to a Reference', async () => {

View File

@ -95,15 +95,6 @@ async function createFixtures() {
}
}
async function deleteFixtures() {
for (let product of data.products) {
await rq({
method: 'DELETE',
url: `/products/${product.id}`,
});
}
}
describe('Filtering API', () => {
beforeAll(async () => {
const token = await registerAndLogin();
@ -111,12 +102,12 @@ describe('Filtering API', () => {
modelsUtils = createModelsUtils({ rq });
await modelsUtils.createContentTypes([product]);
await modelsUtils.cleanupContentTypes([product]);
await modelsUtils.cleanupContentTypes(['product']);
await createFixtures();
}, 60000);
afterAll(async () => {
await deleteFixtures();
await modelsUtils.cleanupContentTypes(['product']);
await modelsUtils.deleteContentTypes(['product']);
}, 60000);

View File

@ -168,24 +168,6 @@ const createFixtures = async () => {
}
};
const deleteFixtures = async () => {
for (const [name, modelName] of [
['countries', 'country'],
['categories', 'category'],
['products', 'product'],
]) {
const uid = `application::${modelName}.${modelName}`;
await rq({
method: 'POST',
url: `/content-manager/collection-types/${uid}/actions/bulkDelete`,
body: {
ids: data.api[name].map(({ id }) => id),
},
});
}
};
describe('Publication State', () => {
beforeAll(async () => {
const token = await registerAndLogin();
@ -201,8 +183,6 @@ describe('Publication State', () => {
}, 60000);
afterAll(async () => {
await deleteFixtures();
await modelsUtils.cleanupContentTypes(['product', 'category', 'country']);
await modelsUtils.deleteComponent('comp');
await modelsUtils.deleteContentTypes(['product', 'category', 'country']);

View File

@ -1,7 +1,5 @@
'use strict';
// required first because it loads env files.
const http = require('http');
const path = require('path');
const fse = require('fs-extra');

View File

@ -1,7 +1,4 @@
'use strict';
// required first because it loads env files.
const { green } = require('chalk');
// eslint-disable-next-line node/no-extraneous-require

View File

@ -1,7 +1,5 @@
'use strict';
// required first because it loads env files.
const path = require('path');
const cluster = require('cluster');
const fs = require('fs-extra');

View File

@ -1,7 +1,5 @@
'use strict';
// required first because it loads env files.
// eslint-disable-next-line node/no-extraneous-require
const strapiAdmin = require('strapi-admin');
const { getConfigUrls, getAbsoluteServerUrl } = require('strapi-utils');

View File

@ -122,16 +122,21 @@ module.exports = ({ rq }) => {
async function cleanupContentType(model) {
const { body } = await rq({
url: `/content-manager/collections-types/application::${model}.${model}`,
url: `/content-manager/collection-types/application::${model}.${model}`,
qs: {
pageSize: 1000,
},
method: 'GET',
});
if (Array.isArray(body) && body.length > 0) {
console.log(body);
if (Array.isArray(body.results) && body.results.length > 0) {
await rq({
url: `/content-manager/collection-types/application::${model}.${model}/actions/bulkDelete`,
method: 'POST',
body: {
ids: body.map(({ id }) => id),
ids: body.results.map(({ id }) => id),
},
});
}