strapi-utils
Advanced tools
Comparing version 3.2.0-beta.0 to 3.2.0-beta.1
'use strict'; | ||
const { getVisibleAttributes, getNonWritableAttributes, constants } = require('../content-types'); | ||
const { | ||
isPrivateAttribute, | ||
getPrivateAttributes, | ||
getVisibleAttributes, | ||
getNonWritableAttributes, | ||
constants, | ||
} = require('../content-types'); | ||
const createModelWithPrivates = (privateAttributes = []) => ({ | ||
options: { | ||
privateAttributes, | ||
}, | ||
attributes: { | ||
foo: { | ||
type: 'string', | ||
private: true, | ||
}, | ||
bar: { | ||
type: 'number', | ||
private: false, | ||
}, | ||
foobar: { | ||
type: 'string', | ||
}, | ||
}, | ||
}); | ||
const createConfig = (privateAttributes = []) => ({ | ||
get: jest.fn(() => privateAttributes), | ||
}); | ||
const createModel = opts => ({ | ||
@@ -11,2 +40,6 @@ primaryKey: 'id', | ||
describe('Content types utils', () => { | ||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
}); | ||
test('Verify constants exist', () => { | ||
@@ -30,3 +63,2 @@ expect(constants.CREATED_BY_ATTRIBUTE).toBeDefined(); | ||
'id', | ||
constants.PUBLISHED_AT_ATTRIBUTE, | ||
constants.CREATED_BY_ATTRIBUTE, | ||
@@ -153,2 +185,75 @@ constants.UPDATED_BY_ATTRIBUTE, | ||
}); | ||
describe('getPrivateAttributes', () => { | ||
test('Attribute is private in the model attributes', () => { | ||
const model = createModelWithPrivates(); | ||
global.strapi = { config: createConfig() }; | ||
const privateAttributes = getPrivateAttributes(model); | ||
expect(privateAttributes).toContain('foo'); | ||
expect(privateAttributes).not.toContain('bar'); | ||
expect(privateAttributes).not.toContain('foobar'); | ||
expect(strapi.config.get).toHaveBeenCalledWith('api.responses.privateAttributes', []); | ||
}); | ||
test('Attribute is set to private in the app config', () => { | ||
const model = createModelWithPrivates(); | ||
global.strapi = { config: createConfig(['bar']) }; | ||
const privateAttributes = getPrivateAttributes(model); | ||
expect(privateAttributes).toContain('foo'); | ||
expect(privateAttributes).toContain('bar'); | ||
expect(privateAttributes).not.toContain('foobar'); | ||
expect(strapi.config.get).toHaveBeenCalledWith('api.responses.privateAttributes', []); | ||
}); | ||
test('Attribute is set to private in the model options', () => { | ||
const model = createModelWithPrivates(['foobar']); | ||
global.strapi = { config: createConfig() }; | ||
const privateAttributes = getPrivateAttributes(model); | ||
expect(privateAttributes).toContain('foo'); | ||
expect(privateAttributes).not.toContain('bar'); | ||
expect(privateAttributes).toContain('foobar'); | ||
expect(strapi.config.get).toHaveBeenCalledWith('api.responses.privateAttributes', []); | ||
}); | ||
}); | ||
describe('isPrivateAttribute', () => { | ||
test('Attribute is private in the model attributes', () => { | ||
const model = createModelWithPrivates(); | ||
global.strapi = { config: createConfig() }; | ||
Object.assign(model, { privateAttributes: getPrivateAttributes(model) }); | ||
expect(isPrivateAttribute(model, 'foo')).toBeTruthy(); | ||
expect(isPrivateAttribute(model, 'bar')).toBeFalsy(); | ||
expect(isPrivateAttribute(model, 'foobar')).toBeFalsy(); | ||
expect(strapi.config.get).toHaveBeenCalledWith('api.responses.privateAttributes', []); | ||
}); | ||
test('Attribute is set to private in the app config', () => { | ||
const model = createModelWithPrivates(); | ||
global.strapi = { config: createConfig(['bar']) }; | ||
Object.assign(model, { privateAttributes: getPrivateAttributes(model) }); | ||
expect(isPrivateAttribute(model, 'foo')).toBeTruthy(); | ||
expect(isPrivateAttribute(model, 'bar')).toBeTruthy(); | ||
expect(isPrivateAttribute(model, 'foobar')).toBeFalsy(); | ||
expect(strapi.config.get).toHaveBeenCalledWith('api.responses.privateAttributes', []); | ||
}); | ||
test('Attribute is set to private in the model options', () => { | ||
const model = createModelWithPrivates(['foobar']); | ||
global.strapi = { config: createConfig() }; | ||
Object.assign(model, { privateAttributes: getPrivateAttributes(model) }); | ||
expect(isPrivateAttribute(model, 'foo')).toBeTruthy(); | ||
expect(isPrivateAttribute(model, 'bar')).toBeFalsy(); | ||
expect(isPrivateAttribute(model, 'foobar')).toBeTruthy(); | ||
expect(strapi.config.get).toHaveBeenCalledWith('api.responses.privateAttributes', []); | ||
}); | ||
}); | ||
}); |
@@ -8,2 +8,3 @@ 'use strict'; | ||
const input = { | ||
id: 1, | ||
email: 'foo@bar.com', | ||
@@ -43,2 +44,3 @@ firstname: 'foo', | ||
}, | ||
privateAttributes: ['email'], | ||
attributes: { | ||
@@ -87,2 +89,3 @@ email: { | ||
}, | ||
privateAttributes: ['secret'], | ||
attributes: { | ||
@@ -109,7 +112,12 @@ name: { | ||
global.strapi = { | ||
getModel(name) { | ||
return models[name]; | ||
}, | ||
}; | ||
beforeEach(() => { | ||
global.strapi = { | ||
getModel(name) { | ||
return models[name]; | ||
}, | ||
config: { | ||
get: jest.fn, | ||
}, | ||
}; | ||
}); | ||
@@ -120,3 +128,3 @@ describe('Basic', () => { | ||
{ withPrivate: false, isOutput: true, includeFields: null }, | ||
_.pick(input, ['firstname', 'lastname']), | ||
_.pick(input, ['id', 'firstname', 'lastname']), | ||
], | ||
@@ -126,12 +134,12 @@ [{ withPrivate: false, isOutput: false, includeFields: null }, input], | ||
{ withPrivate: false, isOutput: true, includeFields: ['firstname'] }, | ||
_.pick(input, ['firstname']), | ||
_.pick(input, ['id', 'firstname']), | ||
], | ||
[ | ||
{ withPrivate: false, isOutput: true, includeFields: ['email', 'firstname'] }, | ||
_.pick(input, ['firstname']), | ||
_.pick(input, ['id', 'firstname']), | ||
], | ||
[{ withPrivate: false, isOutput: true, includeFields: ['password'] }, {}], | ||
[{ withPrivate: false, isOutput: true, includeFields: ['password'] }, _.pick(input, ['id'])], | ||
[ | ||
{ withPrivate: true, isOutput: true, includeFields: null }, | ||
_.pick(input, ['email', 'firstname', 'lastname']), | ||
_.pick(input, ['id', 'email', 'firstname', 'lastname']), | ||
], | ||
@@ -141,20 +149,20 @@ [{ withPrivate: true, isOutput: false, includeFields: null }, input], | ||
{ withPrivate: true, isOutput: true, includeFields: ['firstname'] }, | ||
_.pick(input, ['firstname']), | ||
_.pick(input, ['id', 'firstname']), | ||
], | ||
[ | ||
{ withPrivate: true, isOutput: true, includeFields: ['email', 'firstname'] }, | ||
_.pick(input, ['email', 'firstname']), | ||
_.pick(input, ['id', 'email', 'firstname']), | ||
], | ||
[{ withPrivate: true, isOutput: true, includeFields: ['password'] }, {}], | ||
[{ withPrivate: true, isOutput: true, includeFields: ['password'] }, _.pick(input, ['id'])], | ||
[ | ||
{ withPrivate: true, isOutput: false, includeFields: ['firstname'] }, | ||
_.pick(input, ['firstname']), | ||
_.pick(input, ['id', 'firstname']), | ||
], | ||
[ | ||
{ withPrivate: true, isOutput: false, includeFields: ['email', 'firstname'] }, | ||
_.pick(input, ['email', 'firstname']), | ||
_.pick(input, ['id', 'email', 'firstname']), | ||
], | ||
[ | ||
{ withPrivate: true, isOutput: false, includeFields: ['password'] }, | ||
_.pick(input, ['password']), | ||
_.pick(input, ['id', 'password']), | ||
], | ||
@@ -169,2 +177,53 @@ ]; | ||
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'], | ||
}, | ||
privateAttributes: [].concat(models.user.privateAttributes, ['firstname'], ['id']), | ||
}; | ||
test.each(tests)(`Test n°%#`, (options, expected) => { | ||
expect(sanitizeEntity(input, { ...options, model })).toStrictEqual(expected); | ||
}); | ||
}); | ||
}); | ||
describe('With relation', () => { | ||
@@ -175,6 +234,9 @@ const tests = [ | ||
{ withPrivate: false, isOutput: true, includeFields: null }, | ||
{ | ||
..._.pick(inputWithRelation, ['firstname', 'lastname']), | ||
article: _.pick(inputWithRelation.article, ['name', 'content']), | ||
}, | ||
_.pick(inputWithRelation, [ | ||
'id', | ||
'firstname', | ||
'lastname', | ||
'article.name', | ||
'article.content', | ||
]), | ||
], | ||
@@ -184,3 +246,3 @@ [ | ||
{ withPrivate: false, isOutput: true, includeFields: ['firstname', 'lastname'] }, | ||
_.pick(inputWithRelation, ['firstname', 'lastname']), | ||
_.pick(inputWithRelation, ['id', 'firstname', 'lastname']), | ||
], | ||
@@ -190,5 +252,3 @@ [ | ||
{ withPrivate: false, isOutput: true, includeFields: ['article'] }, | ||
{ | ||
article: _.pick(inputWithRelation.article, ['name', 'content']), | ||
}, | ||
_.pick(inputWithRelation, ['id', 'article.name', 'article.content']), | ||
], | ||
@@ -198,3 +258,3 @@ [ | ||
{ withPrivate: false, isOutput: true, includeFields: ['article.name'] }, | ||
_.pick(inputWithRelation, ['article.name']), | ||
_.pick(inputWithRelation, ['id', 'article.name']), | ||
], | ||
@@ -204,3 +264,3 @@ [ | ||
{ withPrivate: true, isOutput: true, includeFields: null }, | ||
_.pick(inputWithRelation, ['email', 'firstname', 'lastname', 'article']), | ||
_.pick(inputWithRelation, ['id', 'email', 'firstname', 'lastname', 'article']), | ||
], | ||
@@ -211,3 +271,3 @@ [ | ||
{ | ||
..._.pick(inputWithRelation, 'firstname', 'lastname'), | ||
..._.pick(inputWithRelation, ['id', 'firstname', 'lastname']), | ||
article: _.times(3, () => _.pick(article, ['name', 'content'])), | ||
@@ -229,3 +289,3 @@ }, | ||
const expected = _.pick(dataSource, ['firstname', 'lastname', 'dz']); | ||
const expected = _.pick(dataSource, ['id', 'firstname', 'lastname', 'dz']); | ||
@@ -239,3 +299,3 @@ expect(sanitizeEntity(dataSource, { model })).toStrictEqual(expected); | ||
const expected = { | ||
..._.pick(inputWithDz, ['firstname', 'lastname']), | ||
..._.pick(inputWithDz, ['id', 'firstname', 'lastname']), | ||
dz: inputWithDz.dz.map(comp => _.pick(comp, ['__component', 'name', 'content'])), | ||
@@ -265,4 +325,4 @@ }; | ||
const expected = [ | ||
_.pick(input, 'firstname', 'lastname'), | ||
_.pick(input, 'firstname', 'lastname'), | ||
_.pick(input, 'id', 'firstname', 'lastname'), | ||
_.pick(input, 'id', 'firstname', 'lastname'), | ||
]; | ||
@@ -269,0 +329,0 @@ |
@@ -13,12 +13,5 @@ 'use strict'; | ||
const HIDDEN_ATTRIBUTES = [ | ||
ID_ATTRIBUTE, | ||
PUBLISHED_AT_ATTRIBUTE, | ||
CREATED_BY_ATTRIBUTE, | ||
UPDATED_BY_ATTRIBUTE, | ||
]; | ||
const NON_WRITABLE_ATTRIBUTES = [ID_ATTRIBUTE, CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE]; | ||
const NON_VISIBLE_ATTRIBUTES = [...NON_WRITABLE_ATTRIBUTES, PUBLISHED_AT_ATTRIBUTE]; | ||
// making it clear hidden attributes could still be writable | ||
const NON_WRITABLE_ATTRIBUTES = [...HIDDEN_ATTRIBUTES]; | ||
const constants = { | ||
@@ -56,3 +49,3 @@ ID_ATTRIBUTE, | ||
const getNonVisibleAttributes = model => { | ||
return _.uniq([model.primaryKey, ...getTimestamps(model), ...HIDDEN_ATTRIBUTES]); | ||
return _.uniq([model.primaryKey, ...getTimestamps(model), ...NON_VISIBLE_ATTRIBUTES]); | ||
}; | ||
@@ -69,5 +62,20 @@ | ||
const getPrivateAttributes = (model = {}) => { | ||
return _.union( | ||
strapi.config.get('api.responses.privateAttributes', []), | ||
_.get(model, 'options.privateAttributes', []), | ||
_.keys(_.pickBy(model.attributes, attr => !!attr.private)) | ||
); | ||
}; | ||
const isPrivateAttribute = (model = {}, attributeName) => { | ||
return model.privateAttributes.includes(attributeName); | ||
}; | ||
module.exports = { | ||
getPrivateAttributes, | ||
isPrivateAttribute, | ||
constants, | ||
getNonWritableAttributes, | ||
getNonVisibleAttributes, | ||
getVisibleAttributes, | ||
@@ -74,0 +82,0 @@ hasDraftAndPublish, |
'use strict'; | ||
const _ = require('lodash'); | ||
const { constants } = require('./content-types'); | ||
const { constants, isPrivateAttribute } = require('./content-types'); | ||
const { | ||
@@ -40,3 +40,3 @@ ID_ATTRIBUTE, | ||
if (shouldRemoveAttribute(attribute, { withPrivate, isOutput })) { | ||
if (shouldRemoveAttribute(model, key, attribute, { withPrivate, isOutput })) { | ||
return acc; | ||
@@ -138,9 +138,5 @@ } | ||
const shouldRemoveAttribute = (attribute, { withPrivate, isOutput }) => { | ||
if (_.isNil(attribute)) { | ||
return false; | ||
} | ||
const shouldRemoveAttribute = (model, key, attribute = {}, { withPrivate, isOutput }) => { | ||
const isPassword = attribute.type === 'password'; | ||
const isPrivate = attribute.private === true; | ||
const isPrivate = isPrivateAttribute(model, key); | ||
@@ -147,0 +143,0 @@ const shouldRemovePassword = isOutput; |
{ | ||
"name": "strapi-utils", | ||
"version": "3.2.0-beta.0", | ||
"version": "3.2.0-beta.1", | ||
"description": "Shared utilities for the Strapi packages", | ||
@@ -48,3 +48,3 @@ "homepage": "http://strapi.io", | ||
"license": "SEE LICENSE IN LICENSE", | ||
"gitHead": "90761175f6a0f3a36d9742f3e169cc0cc8068972" | ||
"gitHead": "fcb2878987cbb6b78b55a14b8370f1d348bf33f7" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
91292
2700