Skip to content

Commit 55c5ac8

Browse files
authored
Merge pull request #6490 from devtron-labs/user-attribute-api
misc: patch api for user attribute
2 parents 9186fae + d8a0b4f commit 55c5ac8

File tree

3 files changed

+136
-47
lines changed

3 files changed

+136
-47
lines changed

api/restHandler/UserAttributesRestHandler.go

+42-47
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
type UserAttributesRestHandler interface {
3333
AddUserAttributes(w http.ResponseWriter, r *http.Request)
3434
UpdateUserAttributes(w http.ResponseWriter, r *http.Request)
35+
PatchUserAttributes(w http.ResponseWriter, r *http.Request)
3536
GetUserAttribute(w http.ResponseWriter, r *http.Request)
3637
}
3738

@@ -54,35 +55,13 @@ func NewUserAttributesRestHandlerImpl(logger *zap.SugaredLogger, enforcer casbin
5455
}
5556

5657
func (handler *UserAttributesRestHandlerImpl) AddUserAttributes(w http.ResponseWriter, r *http.Request) {
57-
userId, err := handler.userService.GetLoggedInUser(r)
58-
if userId == 0 || err != nil {
59-
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
60-
return
61-
}
62-
decoder := json.NewDecoder(r.Body)
63-
var dto attributes.UserAttributesDto
64-
err = decoder.Decode(&dto)
65-
if err != nil {
66-
handler.logger.Errorw("request err, AddUserAttributes", "err", err, "payload", dto)
67-
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
58+
dto, success := handler.validateUserAttributesRequest(w, r, "PatchUserAttributes")
59+
if !success {
6860
return
6961
}
7062

71-
dto.UserId = userId
72-
//if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionCreate, "*"); !ok {
73-
// common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
74-
// return
75-
//}
76-
emailId, err := handler.userService.GetActiveEmailById(userId)
77-
if err != nil {
78-
handler.logger.Errorw("request err, UpdateUserAttributes", "err", err, "payload", dto)
79-
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
80-
return
81-
}
82-
dto.EmailId = emailId
83-
8463
handler.logger.Infow("request payload, AddUserAttributes", "payload", dto)
85-
resp, err := handler.userAttributesService.AddUserAttributes(&dto)
64+
resp, err := handler.userAttributesService.AddUserAttributes(dto)
8665
if err != nil {
8766
handler.logger.Errorw("service err, AddUserAttributes", "err", err, "payload", dto)
8867
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
@@ -98,43 +77,64 @@ func (handler *UserAttributesRestHandlerImpl) AddUserAttributes(w http.ResponseW
9877
// @Success 200 {object} attributes.UserAttributesDto
9978
// @Router /orchestrator/attributes/user/update [POST]
10079
func (handler *UserAttributesRestHandlerImpl) UpdateUserAttributes(w http.ResponseWriter, r *http.Request) {
80+
dto, success := handler.validateUserAttributesRequest(w, r, "PatchUserAttributes")
81+
if !success {
82+
return
83+
}
84+
85+
handler.logger.Infow("request payload, UpdateUserAttributes", "payload", dto)
86+
resp, err := handler.userAttributesService.UpdateUserAttributes(dto)
87+
if err != nil {
88+
handler.logger.Errorw("service err, UpdateUserAttributes", "err", err, "payload", dto)
89+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
90+
return
91+
}
92+
common.WriteJsonResp(w, nil, resp, http.StatusOK)
93+
}
94+
95+
func (handler *UserAttributesRestHandlerImpl) PatchUserAttributes(w http.ResponseWriter, r *http.Request) {
96+
dto, success := handler.validateUserAttributesRequest(w, r, "PatchUserAttributes")
97+
if !success {
98+
return
99+
}
100+
101+
handler.logger.Infow("request payload, PatchUserAttributes", "payload", dto)
102+
resp, err := handler.userAttributesService.PatchUserAttributes(dto)
103+
if err != nil {
104+
handler.logger.Errorw("service err, PatchUserAttributes", "err", err, "payload", dto)
105+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
106+
return
107+
}
108+
common.WriteJsonResp(w, nil, resp, http.StatusOK)
109+
}
110+
111+
func (handler *UserAttributesRestHandlerImpl) validateUserAttributesRequest(w http.ResponseWriter, r *http.Request, operation string) (*attributes.UserAttributesDto, bool) {
101112
userId, err := handler.userService.GetLoggedInUser(r)
102113
if userId == 0 || err != nil {
103114
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
104-
return
115+
return nil, false
105116
}
106117

107118
decoder := json.NewDecoder(r.Body)
108119
var dto attributes.UserAttributesDto
109120
err = decoder.Decode(&dto)
110121
if err != nil {
111-
handler.logger.Errorw("request err, UpdateUserAttributes", "err", err, "payload", dto)
122+
handler.logger.Errorw("request err, "+operation, "err", err, "payload", dto)
112123
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
113-
return
124+
return nil, false
114125
}
115126

116127
dto.UserId = userId
117-
//if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionUpdate, "*"); !ok {
118-
// common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
119-
// return
120-
//}
121128

122129
emailId, err := handler.userService.GetActiveEmailById(userId)
123130
if err != nil {
124-
handler.logger.Errorw("request err, UpdateUserAttributes", "err", err, "payload", dto)
131+
handler.logger.Errorw("request err, "+operation, "err", err, "payload", dto)
125132
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
126-
return
133+
return nil, false
127134
}
128135
dto.EmailId = emailId
129136

130-
handler.logger.Infow("request payload, UpdateUserAttributes", "payload", dto)
131-
resp, err := handler.userAttributesService.UpdateUserAttributes(&dto)
132-
if err != nil {
133-
handler.logger.Errorw("service err, UpdateUserAttributes", "err", err, "payload", dto)
134-
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
135-
return
136-
}
137-
common.WriteJsonResp(w, nil, resp, http.StatusOK)
137+
return &dto, true
138138
}
139139

140140
// @Summary get user attributes
@@ -158,11 +158,6 @@ func (handler *UserAttributesRestHandlerImpl) GetUserAttribute(w http.ResponseWr
158158
return
159159
}
160160

161-
//if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); !ok {
162-
// common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
163-
// return
164-
//}
165-
166161
dto := attributes.UserAttributesDto{}
167162

168163
emailId, err := handler.userService.GetActiveEmailById(userId)

api/router/UserAttributesRouter.go

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ func NewUserAttributesRouterImpl(userAttributesRestHandler user.UserAttributesRe
3939
func (router UserAttributesRouterImpl) InitUserAttributesRouter(attributesRouter *mux.Router) {
4040
attributesRouter.Path("/update").
4141
HandlerFunc(router.userAttributesRestHandler.UpdateUserAttributes).Methods("POST")
42+
attributesRouter.Path("/patch").
43+
HandlerFunc(router.userAttributesRestHandler.PatchUserAttributes).Methods("PATCH")
4244
attributesRouter.Path("/get").
4345
HandlerFunc(router.userAttributesRestHandler.GetUserAttribute).Queries("key", "{key}").Methods("GET")
4446
}

pkg/attributes/UserAttributesService.go

+92
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@
1717
package attributes
1818

1919
import (
20+
"encoding/json"
2021
"errors"
2122
"github.com/devtron-labs/devtron/internal/sql/repository"
2223
"github.com/go-pg/pg"
2324
"go.uber.org/zap"
25+
"reflect"
2426
)
2527

2628
type UserAttributesService interface {
2729
AddUserAttributes(request *UserAttributesDto) (*UserAttributesDto, error)
2830
UpdateUserAttributes(request *UserAttributesDto) (*UserAttributesDto, error)
31+
PatchUserAttributes(request *UserAttributesDto) (*UserAttributesDto, error)
2932
GetUserAttribute(request *UserAttributesDto) (*UserAttributesDto, error)
3033
}
3134

@@ -95,6 +98,95 @@ func (impl UserAttributesServiceImpl) UpdateUserAttributes(request *UserAttribut
9598
return request, nil
9699
}
97100

101+
func (impl UserAttributesServiceImpl) PatchUserAttributes(request *UserAttributesDto) (*UserAttributesDto, error) {
102+
userAttribute, err := impl.GetUserAttribute(request)
103+
if err != nil {
104+
impl.logger.Errorw("error while getting user attributes during patch request", "req", request, "error", err)
105+
return nil, errors.New("error occurred while updating user attributes")
106+
}
107+
if userAttribute == nil {
108+
impl.logger.Info("no data found for request, so going to add instead of update", "req", request)
109+
attributes, err := impl.AddUserAttributes(request)
110+
if err != nil {
111+
impl.logger.Errorw("error in adding new user attributes", "req", request, "error", err)
112+
return nil, errors.New("error occurred while updating user attributes")
113+
}
114+
return attributes, nil
115+
}
116+
117+
// Parse existing JSON
118+
var existingData map[string]interface{}
119+
if userAttribute.Value != "" {
120+
err = json.Unmarshal([]byte(userAttribute.Value), &existingData)
121+
if err != nil {
122+
impl.logger.Errorw("error parsing existing json value", "value", userAttribute.Value, "error", err)
123+
return nil, errors.New("error occurred while updating user attributes")
124+
}
125+
} else {
126+
existingData = make(map[string]interface{})
127+
}
128+
129+
// Parse new JSON
130+
var newData map[string]interface{}
131+
if request.Value != "" {
132+
err = json.Unmarshal([]byte(request.Value), &newData)
133+
if err != nil {
134+
impl.logger.Errorw("error parsing request json value", "value", request.Value, "error", err)
135+
return nil, errors.New("error occurred while updating user attributes")
136+
}
137+
} else {
138+
newData = make(map[string]interface{})
139+
}
140+
141+
// Check if there are any changes
142+
anyChanges := false
143+
144+
// Merge the objects (patch style)
145+
for key, newValue := range newData {
146+
existingValue, exists := existingData[key]
147+
if !exists || !reflect.DeepEqual(existingValue, newValue) {
148+
existingData[key] = newValue
149+
anyChanges = true
150+
}
151+
}
152+
153+
// If no changes, return the existing data
154+
if !anyChanges {
155+
impl.logger.Infow("no change detected, skipping update", "key", request.Key)
156+
return userAttribute, nil
157+
}
158+
159+
// Convert back to JSON string
160+
mergedJson, err := json.Marshal(existingData)
161+
if err != nil {
162+
impl.logger.Errorw("error converting merged data to json", "data", existingData, "error", err)
163+
return nil, errors.New("error occurred while updating user attributes")
164+
}
165+
166+
dao := &repository.UserAttributesDao{
167+
EmailId: request.EmailId,
168+
Key: request.Key,
169+
Value: string(mergedJson),
170+
UserId: request.UserId,
171+
}
172+
173+
err = impl.attributesRepository.UpdateDataValByKey(dao)
174+
if err != nil {
175+
impl.logger.Errorw("error in update attributes", "req", dao, "error", err)
176+
return nil, errors.New("error occurred while updating user attributes")
177+
}
178+
179+
// Return the updated data
180+
result := &UserAttributesDto{
181+
EmailId: request.EmailId,
182+
Key: request.Key,
183+
Value: string(mergedJson),
184+
UserId: request.UserId,
185+
}
186+
187+
return result, nil
188+
}
189+
98190
func (impl UserAttributesServiceImpl) GetUserAttribute(request *UserAttributesDto) (*UserAttributesDto, error) {
99191

100192
dao := &repository.UserAttributesDao{

0 commit comments

Comments
 (0)