mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 23:24:03 +00:00
Fix relations mainField allowing hidden attributes
This commit is contained in:
parent
229d74e62c
commit
4df9a1112f
@ -99,4 +99,14 @@ module.exports = {
|
||||
searchable: false,
|
||||
},
|
||||
},
|
||||
config: {
|
||||
attributes: {
|
||||
resetPasswordToken: {
|
||||
hidden: true,
|
||||
},
|
||||
registrationToken: {
|
||||
hidden: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -0,0 +1,228 @@
|
||||
'use strict';
|
||||
|
||||
const createContext = require('../../../../../../test/helpers/create-context');
|
||||
const relations = require('../relations');
|
||||
|
||||
const contentTypes = {
|
||||
main: {
|
||||
uid: 'main',
|
||||
attributes: {
|
||||
relation: { type: 'relation', target: 'target' },
|
||||
relationWithHidden: { type: 'relation', target: 'targetWithHidden' },
|
||||
},
|
||||
},
|
||||
targetWithHidden: {
|
||||
uid: 'targetWithHidden',
|
||||
attributes: { myField: { type: 'string' } },
|
||||
config: {
|
||||
attributes: {
|
||||
myField: {
|
||||
hidden: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
target: {
|
||||
uid: 'target',
|
||||
attributes: { myField: { type: 'string' } },
|
||||
},
|
||||
};
|
||||
|
||||
describe('Relations', () => {
|
||||
beforeAll(() => {
|
||||
global.strapi = {
|
||||
getModel: jest.fn((uid) => {
|
||||
return contentTypes[uid];
|
||||
}),
|
||||
entityService: {
|
||||
findPage: jest.fn(),
|
||||
load: jest.fn(),
|
||||
},
|
||||
plugins: {
|
||||
'content-manager': {
|
||||
services: {
|
||||
'permission-checker': {
|
||||
create: () => ({
|
||||
cannot: {
|
||||
read: jest.fn().mockReturnValue(false),
|
||||
},
|
||||
sanitizedQuery: {
|
||||
read: jest.fn().mockReturnValue({}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
'populate-builder': () => ({
|
||||
populateFromQuery: jest.fn().mockReturnThis(),
|
||||
build: jest.fn().mockReturnValue({}),
|
||||
}),
|
||||
'content-types': {
|
||||
findConfiguration: jest.fn().mockReturnValue({
|
||||
metadatas: {
|
||||
relation: {
|
||||
edit: {
|
||||
mainField: 'myField',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
'entity-manager': {
|
||||
findOne: jest.fn(() => ({})),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('findAvailable', () => {
|
||||
test('Query mainField when mainField is listable', async () => {
|
||||
const ctx = createContext(
|
||||
{
|
||||
params: {
|
||||
model: 'main',
|
||||
targetField: 'relation',
|
||||
},
|
||||
query: {
|
||||
_q: 'foobar',
|
||||
},
|
||||
},
|
||||
{
|
||||
state: {
|
||||
userAbility: {
|
||||
can: jest.fn().mockReturnValue(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await relations.findAvailable(ctx);
|
||||
|
||||
expect(strapi.entityService.findPage).toHaveBeenCalledWith(
|
||||
'target',
|
||||
expect.objectContaining({
|
||||
sort: 'myField',
|
||||
fields: ['id', 'myField'],
|
||||
filters: {
|
||||
$and: [
|
||||
{
|
||||
myField: {
|
||||
$containsi: 'foobar',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('Replace mainField by id when mainField is not listable', async () => {
|
||||
const ctx = createContext(
|
||||
{
|
||||
params: {
|
||||
model: 'main',
|
||||
targetField: 'relationWithHidden',
|
||||
},
|
||||
query: {
|
||||
_q: 'foobar',
|
||||
},
|
||||
},
|
||||
{
|
||||
state: {
|
||||
userAbility: {
|
||||
can: jest.fn().mockReturnValue(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await relations.findAvailable(ctx);
|
||||
|
||||
expect(strapi.entityService.findPage).toHaveBeenCalledWith(
|
||||
'targetWithHidden',
|
||||
expect.objectContaining({
|
||||
sort: 'id',
|
||||
fields: ['id'],
|
||||
filters: {
|
||||
$and: [
|
||||
{
|
||||
id: {
|
||||
$containsi: 'foobar',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findExisting', () => {
|
||||
test('Query mainField when mainField is listable', async () => {
|
||||
const ctx = createContext(
|
||||
{
|
||||
params: {
|
||||
model: 'main',
|
||||
targetField: 'relation',
|
||||
id: 1,
|
||||
},
|
||||
query: {
|
||||
_q: 'foobar',
|
||||
},
|
||||
},
|
||||
{
|
||||
state: {
|
||||
userAbility: {
|
||||
can: jest.fn().mockReturnValue(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await relations.findExisting(ctx);
|
||||
|
||||
expect(strapi.entityService.load).toHaveBeenCalledWith(
|
||||
'main',
|
||||
{ id: 1 },
|
||||
'relation',
|
||||
expect.objectContaining({
|
||||
fields: ['id', 'myField'],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('Replace mainField by id when mainField is not listable', async () => {
|
||||
const ctx = createContext(
|
||||
{
|
||||
params: {
|
||||
model: 'main',
|
||||
targetField: 'relationWithHidden',
|
||||
id: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
state: {
|
||||
userAbility: {
|
||||
can: jest.fn().mockReturnValue(false),
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await relations.findExisting(ctx);
|
||||
|
||||
expect(strapi.entityService.load).toHaveBeenCalledWith(
|
||||
'main',
|
||||
{ id: 1 },
|
||||
'relationWithHidden',
|
||||
expect.objectContaining({
|
||||
fields: ['id'],
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,12 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const { prop, isEmpty } = require('lodash/fp');
|
||||
const { prop, isEmpty, uniq } = require('lodash/fp');
|
||||
const { hasDraftAndPublish } = require('@strapi/utils').contentTypes;
|
||||
const { isAnyToMany } = require('@strapi/utils').relations;
|
||||
const { PUBLISHED_AT_ATTRIBUTE } = require('@strapi/utils').contentTypes.constants;
|
||||
|
||||
const { getService } = require('../utils');
|
||||
const { validateFindAvailable, validateFindExisting } = require('./validation/relations');
|
||||
const { isListable } = require('../services/utils/configuration/attributes');
|
||||
|
||||
const addFiltersClause = (params, filtersClause) => {
|
||||
params.filters = params.filters || {};
|
||||
@ -40,8 +41,6 @@ module.exports = {
|
||||
|
||||
const isComponent = modelSchema.modelType === 'component';
|
||||
|
||||
// RBAC checks when it's a content-type
|
||||
// TODO: do RBAC check for components too
|
||||
if (!isComponent) {
|
||||
const permissionChecker = getService('permission-checker').create({
|
||||
userAbility,
|
||||
@ -86,9 +85,13 @@ module.exports = {
|
||||
const modelConfig = isComponent
|
||||
? await getService('components').findConfiguration(modelSchema)
|
||||
: await getService('content-types').findConfiguration(modelSchema);
|
||||
const mainField = prop(`metadatas.${targetField}.edit.mainField`, modelConfig) || 'id';
|
||||
|
||||
const fieldsToSelect = ['id', mainField];
|
||||
let mainField = prop(`metadatas.${targetField}.edit.mainField`, modelConfig) || 'id';
|
||||
if (!isListable(targetedModel, mainField)) {
|
||||
mainField = 'id';
|
||||
}
|
||||
|
||||
const fieldsToSelect = uniq(['id', mainField]);
|
||||
if (hasDraftAndPublish(targetedModel)) {
|
||||
fieldsToSelect.push(PUBLISHED_AT_ATTRIBUTE);
|
||||
}
|
||||
@ -153,8 +156,6 @@ module.exports = {
|
||||
|
||||
const isComponent = modelSchema.modelType === 'component';
|
||||
|
||||
// RBAC checks when it's a content-type
|
||||
// TODO: do RBAC check for components too
|
||||
if (!isComponent) {
|
||||
const entityManager = getService('entity-manager');
|
||||
const permissionChecker = getService('permission-checker').create({
|
||||
@ -194,9 +195,12 @@ module.exports = {
|
||||
? await getService('components').findConfiguration(modelSchema)
|
||||
: await getService('content-types').findConfiguration(modelSchema);
|
||||
|
||||
const mainField = prop(`metadatas.${targetField}.edit.mainField`, modelConfig) || 'id';
|
||||
let mainField = prop(`metadatas.${targetField}.edit.mainField`, modelConfig) || 'id';
|
||||
if (!isListable(targetedModel, mainField)) {
|
||||
mainField = 'id';
|
||||
}
|
||||
|
||||
const fieldsToSelect = ['id', mainField];
|
||||
const fieldsToSelect = uniq(['id', mainField]);
|
||||
if (hasDraftAndPublish(targetedModel)) {
|
||||
fieldsToSelect.push(PUBLISHED_AT_ATTRIBUTE);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { yup } = require('@strapi/utils');
|
||||
const { getService } = require('../../utils');
|
||||
const {
|
||||
isListable,
|
||||
hasEditableAttribute,
|
||||
@ -51,7 +52,25 @@ const createMetadasSchema = (schema) => {
|
||||
placeholder: yup.string(),
|
||||
editable: yup.boolean(),
|
||||
visible: yup.boolean(),
|
||||
mainField: yup.string(),
|
||||
mainField: yup.lazy((value) => {
|
||||
if (!value) {
|
||||
return yup.string();
|
||||
}
|
||||
|
||||
const targetSchema = getService('content-types').findContentType(
|
||||
schema.attributes[key].targetModel
|
||||
);
|
||||
|
||||
if (!targetSchema) {
|
||||
return yup.string();
|
||||
}
|
||||
|
||||
const validAttributes = Object.keys(targetSchema.attributes).filter((key) =>
|
||||
isListable(targetSchema, key)
|
||||
);
|
||||
|
||||
return yup.string().oneOf(validAttributes.concat('id')).default('id');
|
||||
}),
|
||||
step: yup
|
||||
.number()
|
||||
.integer()
|
||||
|
||||
@ -6,6 +6,7 @@ const {
|
||||
isSortable,
|
||||
isSearchable,
|
||||
isVisible,
|
||||
isListable,
|
||||
isRelation,
|
||||
getDefaultMainField,
|
||||
} = require('./attributes');
|
||||
@ -121,7 +122,7 @@ async function syncMetadatas(configuration, schema) {
|
||||
|
||||
if (!targetSchema) return acc;
|
||||
|
||||
if (!isSortable(targetSchema, edit.mainField)) {
|
||||
if (!isSortable(targetSchema, edit.mainField) && !isListable(targetSchema, edit.mainField)) {
|
||||
_.set(updatedMeta, ['edit', 'mainField'], getDefaultMainField(targetSchema));
|
||||
_.set(acc, [key], updatedMeta);
|
||||
return acc;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user