Deletes CPA values on CPA field type change (#31122)
* Deletes CPA values on CPA field type change * Fix error method name reference * Cleans the state when a CPA field's type is updated * Fix types * Fix linter * Webapp no longer makes a decision on the change and server sents a flag in the WS message * Fix linter --------- Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
b3649132d0
Коммит
e51ea025db
@@ -1952,9 +1952,23 @@ export function handleCustomAttributesCreated(msg) {
|
||||
}
|
||||
|
||||
export function handleCustomAttributesUpdated(msg) {
|
||||
return {
|
||||
type: GeneralTypes.CUSTOM_PROFILE_ATTRIBUTE_FIELD_PATCHED,
|
||||
data: msg.data.field,
|
||||
return (dispatch) => {
|
||||
const {field, delete_values: deleteValues} = msg.data;
|
||||
|
||||
// Check if server indicates values should be deleted
|
||||
if (deleteValues) {
|
||||
// Clear values for the field when type changes
|
||||
dispatch({
|
||||
type: UserTypes.CLEAR_CPA_VALUES,
|
||||
data: {fieldId: field.id},
|
||||
});
|
||||
}
|
||||
|
||||
// Update the field
|
||||
dispatch({
|
||||
type: GeneralTypes.CUSTOM_PROFILE_ATTRIBUTE_FIELD_PATCHED,
|
||||
data: field,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1461,4 +1461,181 @@ describe('handleCustomAttributeCRUD', () => {
|
||||
expect(cpaFields.length).toEqual(1);
|
||||
expect(cpaFields.filter(({id}) => id === field2.id)[0]).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('handleCustomAttributesUpdated', () => {
|
||||
test('should update the CustomAttributeField in the state', () => {
|
||||
const testStore = realConfigureStore(makeInitialState());
|
||||
|
||||
// First create a field
|
||||
testStore.dispatch(handleCustomAttributesCreated({
|
||||
event: SocketEvents.CPA_FIELD_CREATED,
|
||||
data: {
|
||||
field: field1,
|
||||
},
|
||||
}));
|
||||
|
||||
let cpaFields = getCustomProfileAttributes(testStore.getState());
|
||||
expect(cpaFields).toBeTruthy();
|
||||
expect(cpaFields.length).toEqual(1);
|
||||
expect(cpaFields.filter(({id}) => id === field1.id)[0].name).toEqual(field1.name);
|
||||
|
||||
// Update the field
|
||||
const updatedField = {...field1, name: 'Updated Field Name'};
|
||||
testStore.dispatch(handleCustomAttributesUpdated({
|
||||
event: SocketEvents.CPA_FIELD_UPDATED,
|
||||
data: {
|
||||
field: updatedField,
|
||||
},
|
||||
}));
|
||||
|
||||
cpaFields = getCustomProfileAttributes(testStore.getState());
|
||||
expect(cpaFields).toBeTruthy();
|
||||
expect(cpaFields.length).toEqual(1);
|
||||
expect(cpaFields.filter(({id}) => id === field1.id)[0].name).toEqual('Updated Field Name');
|
||||
});
|
||||
|
||||
test('should clear values when delete_values is true', () => {
|
||||
// Set up a state with a user that has a custom profile attribute value
|
||||
const testStore = realConfigureStore({
|
||||
entities: {
|
||||
general: {},
|
||||
users: {
|
||||
profiles: {
|
||||
user1: {
|
||||
id: 'user1',
|
||||
custom_profile_attributes: {
|
||||
[field1.id]: 'some value',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// First create a field
|
||||
testStore.dispatch(handleCustomAttributesCreated({
|
||||
event: SocketEvents.CPA_FIELD_CREATED,
|
||||
data: {
|
||||
field: field1,
|
||||
},
|
||||
}));
|
||||
|
||||
// Update the field with delete_values flag
|
||||
const updatedField = {...field1, type: 'select'};
|
||||
testStore.dispatch(handleCustomAttributesUpdated({
|
||||
event: SocketEvents.CPA_FIELD_UPDATED,
|
||||
data: {
|
||||
field: updatedField,
|
||||
delete_values: true,
|
||||
},
|
||||
}));
|
||||
|
||||
// Check that the field was updated
|
||||
const cpaFields = getCustomProfileAttributes(testStore.getState());
|
||||
expect(cpaFields).toBeTruthy();
|
||||
expect(cpaFields.length).toEqual(1);
|
||||
expect(cpaFields.filter(({id}) => id === field1.id)[0].type).toEqual('select');
|
||||
|
||||
// Check that the user's values for this field were cleared
|
||||
const user = testStore.getState().entities.users.profiles.user1;
|
||||
expect(user.custom_profile_attributes?.[field1.id]).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should not clear values when delete_values is false', () => {
|
||||
// Set up a state with a user that has a custom profile attribute value
|
||||
const testStore = realConfigureStore({
|
||||
entities: {
|
||||
general: {},
|
||||
users: {
|
||||
profiles: {
|
||||
user1: {
|
||||
id: 'user1',
|
||||
custom_profile_attributes: {
|
||||
[field1.id]: 'some value',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// First create a field
|
||||
testStore.dispatch(handleCustomAttributesCreated({
|
||||
event: SocketEvents.CPA_FIELD_CREATED,
|
||||
data: {
|
||||
field: field1,
|
||||
},
|
||||
}));
|
||||
|
||||
// Update the field but with delete_values flag set to false
|
||||
const updatedField = {...field1, name: 'Updated Field Name', type: 'text'};
|
||||
testStore.dispatch(handleCustomAttributesUpdated({
|
||||
event: SocketEvents.CPA_FIELD_UPDATED,
|
||||
data: {
|
||||
field: updatedField,
|
||||
delete_values: false,
|
||||
},
|
||||
}));
|
||||
|
||||
// Check that the field was updated
|
||||
const cpaFields = getCustomProfileAttributes(testStore.getState());
|
||||
expect(cpaFields).toBeTruthy();
|
||||
expect(cpaFields.length).toEqual(1);
|
||||
expect(cpaFields.filter(({id}) => id === field1.id)[0].name).toEqual('Updated Field Name');
|
||||
|
||||
// Check that the user's values for this field were NOT cleared
|
||||
const user = testStore.getState().entities.users.profiles.user1;
|
||||
expect(user.custom_profile_attributes).toBeTruthy();
|
||||
expect(user.custom_profile_attributes[field1.id]).toEqual('some value');
|
||||
});
|
||||
|
||||
test('should not clear values when delete_values is not specified', () => {
|
||||
// Set up a state with a user that has a custom profile attribute value
|
||||
const testStore = realConfigureStore({
|
||||
entities: {
|
||||
general: {},
|
||||
users: {
|
||||
profiles: {
|
||||
user1: {
|
||||
id: 'user1',
|
||||
custom_profile_attributes: {
|
||||
[field1.id]: 'some value',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// First create a field
|
||||
testStore.dispatch(handleCustomAttributesCreated({
|
||||
event: SocketEvents.CPA_FIELD_CREATED,
|
||||
data: {
|
||||
field: field1,
|
||||
},
|
||||
}));
|
||||
|
||||
// Update the field without specifying delete_values
|
||||
const updatedField = {...field1, type: 'number'};
|
||||
testStore.dispatch(handleCustomAttributesUpdated({
|
||||
event: SocketEvents.CPA_FIELD_UPDATED,
|
||||
data: {
|
||||
field: updatedField,
|
||||
|
||||
// delete_values not specified
|
||||
},
|
||||
}));
|
||||
|
||||
// Check that the field was updated
|
||||
const cpaFields = getCustomProfileAttributes(testStore.getState());
|
||||
expect(cpaFields).toBeTruthy();
|
||||
expect(cpaFields.length).toEqual(1);
|
||||
expect(cpaFields.filter(({id}) => id === field1.id)[0].type).toEqual('number');
|
||||
|
||||
// Check that the user's values for this field were NOT cleared
|
||||
const user = testStore.getState().entities.users.profiles.user1;
|
||||
expect(user.custom_profile_attributes).toBeTruthy();
|
||||
expect(user.custom_profile_attributes[field1.id]).toEqual('some value');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -74,4 +74,5 @@ export default keyMirror({
|
||||
RECEIVED_FILTERED_USER_STATS: null,
|
||||
PROFILE_NO_LONGER_VISIBLE: null,
|
||||
LOGIN: null,
|
||||
CLEAR_CPA_VALUES: null,
|
||||
});
|
||||
|
||||
@@ -1086,6 +1086,103 @@ describe('Reducers.users', () => {
|
||||
|
||||
expect(updatedProfiles.first_user_id.custom_profile_attributes!.field1).toEqual('updatedValue');
|
||||
});
|
||||
|
||||
test('UserTypes.CLEAR_CPA_VALUES, should clear the custom profile attribute value for specified field ID', () => {
|
||||
const user1 = TestHelper.getUserMock({
|
||||
id: 'user1',
|
||||
custom_profile_attributes: {
|
||||
field1: 'value1',
|
||||
field2: 'value2',
|
||||
},
|
||||
});
|
||||
|
||||
const user2 = TestHelper.getUserMock({
|
||||
id: 'user2',
|
||||
custom_profile_attributes: {
|
||||
field1: 'user2_value1',
|
||||
field3: 'user2_value3',
|
||||
},
|
||||
});
|
||||
|
||||
const user3 = TestHelper.getUserMock({
|
||||
id: 'user3',
|
||||
|
||||
// No custom profile attributes
|
||||
});
|
||||
|
||||
const state = deepFreezeAndThrowOnMutation({
|
||||
profiles: {user1, user2, user3},
|
||||
}) as UsersState;
|
||||
|
||||
const action = {
|
||||
type: UserTypes.CLEAR_CPA_VALUES,
|
||||
data: {
|
||||
fieldId: 'field1',
|
||||
},
|
||||
};
|
||||
|
||||
const newState = reducer(state, action);
|
||||
|
||||
// Check user1 (had field1, should be removed)
|
||||
expect(newState.profiles.user1.custom_profile_attributes).toEqual({
|
||||
field2: 'value2',
|
||||
});
|
||||
|
||||
// Check user2 (had field1, should be removed)
|
||||
expect(newState.profiles.user2.custom_profile_attributes).toEqual({
|
||||
field3: 'user2_value3',
|
||||
});
|
||||
|
||||
// Check user3 (had no attributes, should remain the same)
|
||||
expect(newState.profiles.user3.custom_profile_attributes).toBeUndefined();
|
||||
});
|
||||
|
||||
test('UserTypes.CLEAR_CPA_VALUES, should return the same state if no profiles have the specified field', () => {
|
||||
const user1 = TestHelper.getUserMock({
|
||||
id: 'user1',
|
||||
custom_profile_attributes: {
|
||||
field1: 'value1',
|
||||
field2: 'value2',
|
||||
},
|
||||
});
|
||||
|
||||
const state = deepFreezeAndThrowOnMutation({
|
||||
profiles: {user1},
|
||||
}) as UsersState;
|
||||
|
||||
const action = {
|
||||
type: UserTypes.CLEAR_CPA_VALUES,
|
||||
data: {
|
||||
fieldId: 'non_existent_field',
|
||||
},
|
||||
};
|
||||
|
||||
const newState = reducer(state, action);
|
||||
|
||||
// State should have changed (new object), but values should remain the same
|
||||
expect(newState).not.toBe(state);
|
||||
expect(newState.profiles.user1).toBe(user1);
|
||||
expect(newState.profiles.user1.custom_profile_attributes).toEqual({
|
||||
field1: 'value1',
|
||||
field2: 'value2',
|
||||
});
|
||||
});
|
||||
|
||||
test('UserTypes.CLEAR_CPA_VALUES, should handle empty profiles state', () => {
|
||||
const state = deepFreezeAndThrowOnMutation({
|
||||
profiles: {},
|
||||
}) as UsersState;
|
||||
|
||||
const action = {
|
||||
type: UserTypes.CLEAR_CPA_VALUES,
|
||||
data: {
|
||||
fieldId: 'field1',
|
||||
},
|
||||
};
|
||||
|
||||
const newState = reducer(state as UsersState, action);
|
||||
expect(newState.profiles).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
test('PROFILE_NO_LONGER_VISIBLE should remove references to users from state', () => {
|
||||
|
||||
@@ -236,6 +236,26 @@ function profiles(state: UsersState['profiles'] = {}, action: MMReduxAction) {
|
||||
const profileAttributes = {...existingProfile.custom_profile_attributes, ...customAttributeValues};
|
||||
return receiveUserProfile(state, {...existingProfile, custom_profile_attributes: profileAttributes});
|
||||
}
|
||||
case UserTypes.CLEAR_CPA_VALUES: {
|
||||
const {fieldId} = action.data;
|
||||
|
||||
return Object.values(state).reduce<Record<string, UserProfile>>((nextState, profile) => {
|
||||
// Only modify profiles that have this field value
|
||||
if (profile.custom_profile_attributes && profile.custom_profile_attributes[fieldId] !== undefined) {
|
||||
const newAttributes = {...profile.custom_profile_attributes};
|
||||
delete newAttributes[fieldId];
|
||||
|
||||
nextState[profile.id] = {
|
||||
...profile,
|
||||
custom_profile_attributes: newAttributes,
|
||||
};
|
||||
} else {
|
||||
nextState[profile.id] = profile;
|
||||
}
|
||||
|
||||
return nextState;
|
||||
}, {});
|
||||
}
|
||||
case UserTypes.RECEIVED_PROFILES_LIST: {
|
||||
const users: UserProfile[] = action.data;
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user