strapi/packages/strapi-utils/lib/__tests__/sanitize-entity.test.js
Diego Albitres 0670c896e3
Add privateAttributes to global and per model response (#7331)
* Added privateAttributes globally and per model

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Added tests for sanitizeEntity

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Fixed broken test

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Added privateAttributes to GraphQL responses

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Added docs for privateAttributes

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Refactored and fixed PR comments

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Merged tests and fixed broken tests from merge

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Updated documentation for new option

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Refactored GraphQL implementation

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Fixed PR comments

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Fixed tests for sanitize-entity

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Removed code and tests for ignorePrivateFor

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Exported getPrivateAttributes for GraphQL plugin

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>

* Removed ignorePrivateFor from docs

Signed-off-by: Diego Albitres <diego.albitres@gmail.com>
2020-09-24 09:40:10 +02:00

361 lines
10 KiB
JavaScript

'use strict';
const _ = require('lodash');
const { sanitizeEntity } = require('../sanitize-entity');
describe('Sanitize Entity', () => {
const input = {
id: 1,
email: 'foo@bar.com',
firstname: 'foo',
lastname: 'bar',
password: 'qwerty',
};
const article = {
name: 'foobar',
content: 'lorem ipsum',
secret: 'very-secret',
};
const inputWithRelation = {
...input,
article,
};
const inputWithDz = {
...input,
dz: [
{
__component: 'article',
name: 'foo',
content: 'bar',
secret: 'secret-foobar',
},
],
};
const userModel = {
primaryKey: 'id',
options: {
timestamps: ['created_at', 'updated_at'],
},
attributes: {
email: {
type: 'text',
private: true,
},
firstname: {
type: 'text',
},
lastname: {
type: 'text',
},
password: {
type: 'password',
},
},
};
const userRelModel = {
...userModel,
attributes: {
...userModel.attributes,
article: {
collection: 'article',
},
},
};
const userDzModel = {
...userModel,
attributes: {
...userModel.attributes,
dz: {
type: 'dynamiczone',
components: ['article'],
},
},
};
const articleModel = {
primaryKey: 'id',
options: {
timestamps: ['created_at', 'updated_at'],
},
attributes: {
name: {
type: 'text',
},
content: {
type: 'text',
},
secret: {
type: 'text',
private: true,
},
},
};
const models = {
user: userModel,
article: articleModel,
userRel: userRelModel,
userDz: userDzModel,
};
beforeEach(() => {
global.strapi = {
getModel(name) {
return models[name];
},
config: {
get: jest.fn,
},
};
});
describe('Basic', () => {
const tests = [
[
{ withPrivate: false, isOutput: true, includeFields: null },
_.pick(input, ['id', 'firstname', 'lastname']),
],
[{ withPrivate: false, isOutput: false, includeFields: null }, input],
[
{ withPrivate: false, isOutput: true, includeFields: ['firstname'] },
_.pick(input, ['id', 'firstname']),
],
[
{ withPrivate: false, isOutput: true, includeFields: ['email', 'firstname'] },
_.pick(input, ['id', 'firstname']),
],
[{ withPrivate: false, isOutput: true, includeFields: ['password'] }, _.pick(input, ['id'])],
[
{ withPrivate: true, isOutput: true, includeFields: null },
_.pick(input, ['id', 'email', 'firstname', 'lastname']),
],
[{ withPrivate: true, isOutput: false, includeFields: null }, input],
[
{ withPrivate: true, isOutput: true, includeFields: ['firstname'] },
_.pick(input, ['id', 'firstname']),
],
[
{ withPrivate: true, isOutput: true, includeFields: ['email', 'firstname'] },
_.pick(input, ['id', 'email', 'firstname']),
],
[{ withPrivate: true, isOutput: true, includeFields: ['password'] }, _.pick(input, ['id'])],
[
{ withPrivate: true, isOutput: false, includeFields: ['firstname'] },
_.pick(input, ['id', 'firstname']),
],
[
{ withPrivate: true, isOutput: false, includeFields: ['email', 'firstname'] },
_.pick(input, ['id', 'email', 'firstname']),
],
[
{ withPrivate: true, isOutput: false, includeFields: ['password'] },
_.pick(input, ['id', 'password']),
],
];
test.each(tests)(`Test n°%#`, (options, expected) => {
const { user: model } = models;
expect(sanitizeEntity(input, { ...options, model })).toStrictEqual(expected);
});
});
describe('With private attributes', () => {
describe('When options.privateAttributes exists in model, the attributes in options.privateAttributes must be hidden', () => {
const tests = [
[{ withPrivate: false, isOutput: true, includeFields: null }, _.pick(input, ['lastname'])],
[{ withPrivate: false, isOutput: false, includeFields: null }, input],
[{ withPrivate: false, isOutput: true, includeFields: ['firstname'] }, {}],
[{ withPrivate: false, isOutput: true, includeFields: ['email', 'firstname'] }, {}],
[{ withPrivate: false, isOutput: true, includeFields: ['password'] }, {}],
[
{ withPrivate: true, isOutput: true, includeFields: null },
_.pick(input, ['id', 'email', 'firstname', 'lastname']),
],
[{ withPrivate: true, isOutput: false, includeFields: null }, input],
[
{ withPrivate: true, isOutput: true, includeFields: ['firstname'] },
_.pick(input, ['id', 'firstname']),
],
[
{ withPrivate: true, isOutput: true, includeFields: ['email', 'firstname'] },
_.pick(input, ['id', 'email', 'firstname']),
],
[{ withPrivate: true, isOutput: true, includeFields: ['password'] }, _.pick(input, ['id'])],
[
{ withPrivate: true, isOutput: false, includeFields: ['firstname'] },
_.pick(input, ['id', 'firstname']),
],
[
{ withPrivate: true, isOutput: false, includeFields: ['email', 'firstname'] },
_.pick(input, ['id', 'email', 'firstname']),
],
[
{ withPrivate: true, isOutput: false, includeFields: ['password'] },
_.pick(input, ['id', 'password']),
],
];
const model = {
...models.user,
options: {
...models.user.options,
privateAttributes: ['firstname'],
},
};
test.each(tests)(`Test n°%#`, (options, expected) => {
global.strapi = {
config: {
get: jest.fn(path => {
return path === 'api.responses.privateAttributes' ? ['id'] : [];
}),
},
};
expect(sanitizeEntity(input, { ...options, model })).toStrictEqual(expected);
});
});
});
describe('With relation', () => {
const tests = [
[
inputWithRelation,
{ withPrivate: false, isOutput: true, includeFields: null },
_.pick(inputWithRelation, [
'id',
'firstname',
'lastname',
'article.name',
'article.content',
]),
],
[
inputWithRelation,
{ withPrivate: false, isOutput: true, includeFields: ['firstname', 'lastname'] },
_.pick(inputWithRelation, ['id', 'firstname', 'lastname']),
],
[
inputWithRelation,
{ withPrivate: false, isOutput: true, includeFields: ['article'] },
_.pick(inputWithRelation, ['id', 'article.name', 'article.content']),
],
[
inputWithRelation,
{ withPrivate: false, isOutput: true, includeFields: ['article.name'] },
_.pick(inputWithRelation, ['id', 'article.name']),
],
[
inputWithRelation,
{ withPrivate: true, isOutput: true, includeFields: null },
_.pick(inputWithRelation, ['id', 'email', 'firstname', 'lastname', 'article']),
],
[
{ ...inputWithRelation, article: _.times(3, () => _.clone(article)) },
{ withPrivate: false, isOutput: true, includeFields: null },
{
..._.pick(inputWithRelation, ['id', 'firstname', 'lastname']),
article: _.times(3, () => _.pick(article, ['name', 'content'])),
},
],
];
test.each(tests)(`Test n°%#`, (source, options, expected) => {
const { userRel: model } = models;
expect(sanitizeEntity(source, { ...options, model })).toStrictEqual(expected);
});
});
describe('With Dynamic Zone', () => {
test('Dynamic zone null', () => {
const { userDz: model } = models;
const dataSource = { ...inputWithDz, dz: null };
const expected = _.pick(dataSource, ['id', 'firstname', 'lastname', 'dz']);
expect(sanitizeEntity(dataSource, { model })).toStrictEqual(expected);
});
test('Dynamic zone with a basic component', () => {
const { userDz: model } = models;
const expected = {
..._.pick(inputWithDz, ['id', 'firstname', 'lastname']),
dz: inputWithDz.dz.map(comp => _.pick(comp, ['__component', 'name', 'content'])),
};
expect(sanitizeEntity(inputWithDz, { model })).toStrictEqual(expected);
});
});
describe('Edge cases', () => {
test('It returns null if the model is nil', () => {
expect(sanitizeEntity(input, { model: null })).toBeNull();
expect(sanitizeEntity(input, { model: undefined })).toBeNull();
});
test(`It returns the input data as-is if it's not an object or an array`, () => {
const { user: model } = models;
expect(sanitizeEntity('foobar', { model })).toBe('foobar');
expect(sanitizeEntity(undefined, { model })).toBeUndefined();
expect(sanitizeEntity(null, { model })).toBeNull();
});
test('It sanitizes all entries if the input is an array', () => {
const dataSource = [_.clone(input), _.clone(input)];
const expected = [
_.pick(input, 'id', 'firstname', 'lastname'),
_.pick(input, 'id', 'firstname', 'lastname'),
];
const { user: model } = models;
expect(sanitizeEntity(dataSource, { model })).toStrictEqual(expected);
});
test(`It should put the relation value as-is if it's nil'`, () => {
const dataSource = { ...inputWithRelation, article: null };
const expected = _.omit(dataSource, ['email', 'password']);
const { userRel: model } = models;
expect(sanitizeEntity(dataSource, { model })).toStrictEqual(expected);
});
test(`It should make sure that legacy data parsing returns an object`, () => {
const dataSource = {
toJSON: jest.fn(() => input),
};
const invalidDataSource = {
toJSON: jest.fn(() => null),
};
const { user: model } = models;
expect(sanitizeEntity(dataSource, { model })).toStrictEqual(
_.omit(input, ['email', 'password'])
);
expect(dataSource.toJSON).toHaveBeenCalled();
expect(sanitizeEntity(invalidDataSource, { model })).toBeNull();
expect(invalidDataSource.toJSON).toHaveBeenCalled();
});
test('It should handle custom fields', () => {
const dataSource = { ...input, foo: 'bar' };
const expected = _.omit(dataSource, ['email', 'password']);
const { user: model } = models;
expect(sanitizeEntity(dataSource, { model })).toStrictEqual(expected);
});
});
});