mirror of
https://github.com/mattermost/mattermost.git
synced 2026-11-03 12:48:37 -06:00
Address CodeRabbit findings on graph option parents and empty values
Reject duplicate parent names in OptionParentLinks, normalize a cleared graph attribute to an empty array, and correct the session-attribute option-count comment. Co-authored-by: mattermost-code <matty-code@mattermost.com>
This commit is contained in:
1 parent
071f3d84cf
commit
2635e3d7d3
5 files changed
+57
-2
No files matched your search
@@ -284,10 +284,16 @@ func (pf *PropertyField) OptionParentLinks() (add []*PropertyOptionEdge, replaci
|
||||
replacing = append(replacing, optionID)
|
||||
|
||||
named, _ := option["name"].(string)
|
||||
if len(parents) > PropertyGraphMaxParentsPerOption {
|
||||
return nil, nil, fmt.Errorf("the option at index %d would have %d options directly above it, and no option may have more than %d", i, len(parents), PropertyGraphMaxParentsPerOption)
|
||||
}
|
||||
seen := make(map[string]bool, len(parents))
|
||||
for _, parent := range parents {
|
||||
switch {
|
||||
case parent == "":
|
||||
return nil, nil, fmt.Errorf("the option at index %d is put under an option with no name", i)
|
||||
case seen[parent]:
|
||||
return nil, nil, fmt.Errorf("the option at index %d names %q as a parent more than once", i, parent)
|
||||
case ambiguous[parent]:
|
||||
return nil, nil, fmt.Errorf("the option at index %d is put under %q, and two of the field's options are called that", i, parent)
|
||||
case idsByName[parent] == "":
|
||||
@@ -295,6 +301,7 @@ func (pf *PropertyField) OptionParentLinks() (add []*PropertyOptionEdge, replaci
|
||||
case idsByName[parent] == optionID:
|
||||
return nil, nil, fmt.Errorf("the option at index %d, %q, is put under itself", i, named)
|
||||
}
|
||||
seen[parent] = true
|
||||
add = append(add, &PropertyOptionEdge{
|
||||
FieldID: pf.ID,
|
||||
ChildOptionID: optionID,
|
||||
|
||||
@@ -2144,6 +2144,11 @@ func TestPropertyField_OptionParentLinks(t *testing.T) {
|
||||
},
|
||||
reason: "two of the field's options are called that",
|
||||
},
|
||||
{
|
||||
name: "the same parent named twice",
|
||||
options: []map[string]any{{"id": NewId(), "name": "Air"}, {"id": NewId(), "name": "Sea", "parents": []any{"Air", "Air"}}},
|
||||
reason: `names "Air" as a parent more than once`,
|
||||
},
|
||||
{
|
||||
name: "a parent with no name",
|
||||
options: []map[string]any{{"id": NewId(), "name": "Air", "parents": []any{""}}},
|
||||
|
||||
@@ -188,7 +188,7 @@ func (f *SAField) EnabledForPlatform(platform string) bool {
|
||||
// would be wrong for a field whose list was withheld for exceeding
|
||||
// PropertyFieldMaxHydratedOptions. No session attribute field can be in that
|
||||
// state: the schema is the fixed set SessionAttributeSystemFields declares,
|
||||
// whose largest option list has six entries, nothing but the seeding code writes
|
||||
// whose largest option list has 19 entries, nothing but the seeding code writes
|
||||
// those lists, and an API caller may only change enabled, ttl_seconds, and
|
||||
// grace_period_seconds on a seeded field.
|
||||
func IsValidSessionAttributeValue(field *PropertyField, value any) bool {
|
||||
|
||||
@@ -859,6 +859,49 @@ describe('components/user_settings/general/UserSettingsGeneral', () => {
|
||||
expect(props.actions.saveCustomProfileAttribute).toHaveBeenCalledWith('user_id', 'field1', ['opt1', 'opt2']);
|
||||
});
|
||||
|
||||
test('updateSelectAttribute() should clear a graph attribute as an empty array', async () => {
|
||||
const saveCustomProfileAttribute = jest.fn().mockResolvedValue({});
|
||||
const graphAttribute: UserPropertyField = {
|
||||
...customProfileAttribute,
|
||||
type: 'graph',
|
||||
attrs: {
|
||||
value_type: '',
|
||||
visibility: 'when_set',
|
||||
sort_order: 0,
|
||||
options: [
|
||||
{id: 'opt1', name: 'Option 1', color: ''},
|
||||
{id: 'opt2', name: 'Option 2', color: ''},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const testUser = {...user, custom_profile_attributes: {field1: ['opt1']}};
|
||||
const props = {
|
||||
...requiredProps,
|
||||
enableCustomProfileAttributes: true,
|
||||
customProfileAttributeFields: [graphAttribute],
|
||||
user: testUser,
|
||||
activeSection: 'customAttribute_field1',
|
||||
actions: {
|
||||
...requiredProps.actions,
|
||||
saveCustomProfileAttribute,
|
||||
},
|
||||
};
|
||||
|
||||
const {container} = renderWithContext(<UserSettingsGeneral {...props}/>);
|
||||
|
||||
const clearIndicator = container.querySelector('.react-select__clear-indicator');
|
||||
expect(clearIndicator).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(clearIndicator!);
|
||||
await screen.findByText('Select');
|
||||
|
||||
const saveButton = screen.getByRole('button', {name: 'Save'});
|
||||
await userEvent.click(saveButton);
|
||||
|
||||
expect(props.actions.saveCustomProfileAttribute).toHaveBeenCalledWith('user_id', 'field1', []);
|
||||
});
|
||||
|
||||
test('should not show custom attribute input field when LDAP attribute is set', async () => {
|
||||
const props = {
|
||||
...requiredProps,
|
||||
|
||||
@@ -469,7 +469,7 @@ export class UserSettingsGeneralTab extends PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (attributeField.type === 'multiselect' && !attributeValue) {
|
||||
if ((attributeField.type === 'multiselect' || attributeField.type === 'graph') && !attributeValue) {
|
||||
attributeValue = [];
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user