[MM-62552] Custom Profile Attributes: use json.RawMessage for the value. (#29989)

* refactor: Move property value sanitization to model layer

* feat: Add value sanitization for custom profile attributes

* refactor: Update custom profile attributes to use json.RawMessage

* refactor: Update patchCustomProfileAttribute to handle json.RawMessage directly

* refactor: Refactor custom profile attributes handler with improved validation

* refactor: Rename `patchCustomProfileAttribute` to `patchCPAValues`

* refactor: Replace ReturnJSON with json.NewEncoder and add error logging

* feat: Add encoding/json import to property_value.go

* refactor: Update property value tests to use json.RawMessage

* fix: Convert string value to json.RawMessage in property value test

* fix: Convert string literals to json.RawMessage in property value tests

* fix: Add missing encoding/json import in custom_profile_attributes.go

* fix: Preserve JSON RawMessage type in listCPAValues function

* fix: Update custom profile attributes test to use json.RawMessage

* feat: Add json import to custom_profile_attributes_test.go

* refactor: Update ListCPAValues and PatchCPAValues to use json.RawMessage

* refactor: Rename `actualValue` to `updatedValue` in custom profile attributes test

* refactor: Improve user permission and audit logging for custom profile attributes patch

* refactor: Optimize CPA field lookup by using ListCPAFields() and map

* fix: Correct user ID reference in custom profile attributes patch endpoint

* refactor: Change patchCPAValues to use map[string]json.RawMessage for results

* refactor: format and fix tests

* test: Add comprehensive unit tests for sanitizePropertyValue function

* test: Add test case for invalid property value type

* feat: Use `model.NewId()` to generate valid IDs in custom profile attributes tests

* refactor: Replace hardcoded IDs with dynamic variables in custom profile attributes test

* refactor: restore variable name

* refactor: drop undesired changes

* chore: refresh app layers

* feat: Update API definition to support string or string array values for custom profile attributes

* test: Add test cases for multiselect custom profile attribute values

* test: Add tests for multiselect custom profile attribute values

* test: Isolate array value test in separate t.Run

* test: Add test case for multiselect array values in custom profile attributes

* refactor: Move array value test from TestCreateCPAField to TestPatchCPAValue

* test: Update custom profile attributes test assertions

* test: add test case for handling array values in GetCPAValue

* test: Add array value tests for property value store

* refactor(store): no need to convert to json the rawmessage

* chore: lint

* i18n

* use model to interface with sqlx

* fix: Allow empty strings for text, date, and select profile attributes

* refactor: Filter out empty strings in multiselect and multiuser fields

* refactor: Update multiuser field sanitization to validate and error on invalid IDs

* refactor: Simplify sanitizePropertyValue function with reduced code duplication

* fix: Allow empty user ID in custom profile attribute sanitization

* refactor: Convert comment-based subtests to nested t.Run in TestSanitizePropertyValue

* refactor: Convert comment-based subtests to nested t.Run tests in TestSanitizePropertyValue

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Julien Tant
2025-02-05 10:21:22 -07:00
коммит произвёл GitHub
родитель 6560b4c0cf
Коммит bcc395d139
11 изменённых файлов: 502 добавлений и 217 удалений

Просмотреть файл

@@ -9465,21 +9465,21 @@ func (c *Client4) DeleteCPAField(ctx context.Context, fieldID string) (*Response
return BuildResponse(r), nil
}
func (c *Client4) ListCPAValues(ctx context.Context, userID string) (map[string]string, *Response, error) {
func (c *Client4) ListCPAValues(ctx context.Context, userID string) (map[string]json.RawMessage, *Response, error) {
r, err := c.DoAPIGet(ctx, c.userCustomProfileAttributesRoute(userID), "")
if err != nil {
return nil, BuildResponse(r), err
}
defer closeBody(r)
fields := make(map[string]string)
fields := make(map[string]json.RawMessage)
if err := json.NewDecoder(r.Body).Decode(&fields); err != nil {
return nil, nil, NewAppError("ListCPAValues", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return fields, BuildResponse(r), nil
}
func (c *Client4) PatchCPAValues(ctx context.Context, values map[string]string) (map[string]string, *Response, error) {
func (c *Client4) PatchCPAValues(ctx context.Context, values map[string]json.RawMessage) (map[string]json.RawMessage, *Response, error) {
buf, err := json.Marshal(values)
if err != nil {
return nil, nil, NewAppError("PatchCPAValues", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
@@ -9491,7 +9491,7 @@ func (c *Client4) PatchCPAValues(ctx context.Context, values map[string]string)
}
defer closeBody(r)
var patchedValues map[string]string
var patchedValues map[string]json.RawMessage
if err := json.NewDecoder(r.Body).Decode(&patchedValues); err != nil {
return nil, nil, NewAppError("PatchCPAValues", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
}

Просмотреть файл

@@ -71,12 +71,12 @@ func (pf *PropertyField) IsValid() error {
return NewAppError("PropertyField.IsValid", "model.property_field.is_valid.app_error", map[string]any{"FieldName": "name", "Reason": "value cannot be empty"}, "id="+pf.ID, http.StatusBadRequest)
}
if !(pf.Type == PropertyFieldTypeText ||
pf.Type == PropertyFieldTypeSelect ||
pf.Type == PropertyFieldTypeMultiselect ||
pf.Type == PropertyFieldTypeDate ||
pf.Type == PropertyFieldTypeUser ||
pf.Type == PropertyFieldTypeMultiuser) {
if pf.Type != PropertyFieldTypeText &&
pf.Type != PropertyFieldTypeSelect &&
pf.Type != PropertyFieldTypeMultiselect &&
pf.Type != PropertyFieldTypeDate &&
pf.Type != PropertyFieldTypeUser &&
pf.Type != PropertyFieldTypeMultiuser {
return NewAppError("PropertyField.IsValid", "model.property_field.is_valid.app_error", map[string]any{"FieldName": "type", "Reason": "unknown value"}, "id="+pf.ID, http.StatusBadRequest)
}

Просмотреть файл

@@ -3,18 +3,21 @@
package model
import "net/http"
import (
"encoding/json"
"net/http"
)
type PropertyValue struct {
ID string `json:"id"`
TargetID string `json:"target_id"`
TargetType string `json:"target_type"`
GroupID string `json:"group_id"`
FieldID string `json:"field_id"`
Value string `json:"value"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
ID string `json:"id"`
TargetID string `json:"target_id"`
TargetType string `json:"target_type"`
GroupID string `json:"group_id"`
FieldID string `json:"field_id"`
Value json.RawMessage `json:"value"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
}
func (pv *PropertyValue) PreSave() {