@@ -20,30 +20,25 @@ import (
20
20
"encoding/json"
21
21
"errors"
22
22
"github.com/devtron-labs/devtron/internal/sql/repository"
23
+ "github.com/devtron-labs/devtron/pkg/attributes/adapter"
24
+ "github.com/devtron-labs/devtron/pkg/attributes/bean"
23
25
"github.com/go-pg/pg"
24
26
"go.uber.org/zap"
25
27
"reflect"
26
28
)
27
29
28
30
type UserAttributesService interface {
29
- AddUserAttributes (request * UserAttributesDto ) (* UserAttributesDto , error )
30
- UpdateUserAttributes (request * UserAttributesDto ) (* UserAttributesDto , error )
31
- PatchUserAttributes (request * UserAttributesDto ) (* UserAttributesDto , error )
32
- GetUserAttribute (request * UserAttributesDto ) (* UserAttributesDto , error )
31
+ AddUserAttributes (request * bean. UserAttributesDto ) (* bean. UserAttributesDto , error )
32
+ UpdateUserAttributes (request * bean. UserAttributesDto ) (* bean. UserAttributesDto , error )
33
+ PatchUserAttributes (request * bean. UserAttributesDto ) (* bean. UserAttributesDto , error )
34
+ GetUserAttribute (request * bean. UserAttributesDto ) (* bean. UserAttributesDto , error )
33
35
}
34
36
35
37
type UserAttributesServiceImpl struct {
36
38
logger * zap.SugaredLogger
37
39
attributesRepository repository.UserAttributesRepository
38
40
}
39
41
40
- type UserAttributesDto struct {
41
- EmailId string `json:"emailId"`
42
- Key string `json:"key"`
43
- Value string `json:"value"`
44
- UserId int32 `json:"-"`
45
- }
46
-
47
42
func NewUserAttributesServiceImpl (logger * zap.SugaredLogger ,
48
43
attributesRepository repository.UserAttributesRepository ) * UserAttributesServiceImpl {
49
44
serviceImpl := & UserAttributesServiceImpl {
@@ -53,7 +48,7 @@ func NewUserAttributesServiceImpl(logger *zap.SugaredLogger,
53
48
return serviceImpl
54
49
}
55
50
56
- func (impl UserAttributesServiceImpl ) AddUserAttributes (request * UserAttributesDto ) (* UserAttributesDto , error ) {
51
+ func (impl UserAttributesServiceImpl ) AddUserAttributes (request * bean. UserAttributesDto ) (* bean. UserAttributesDto , error ) {
57
52
dao := & repository.UserAttributesDao {
58
53
EmailId : request .EmailId ,
59
54
Key : request .Key ,
@@ -68,7 +63,7 @@ func (impl UserAttributesServiceImpl) AddUserAttributes(request *UserAttributesD
68
63
return request , nil
69
64
}
70
65
71
- func (impl UserAttributesServiceImpl ) UpdateUserAttributes (request * UserAttributesDto ) (* UserAttributesDto , error ) {
66
+ func (impl UserAttributesServiceImpl ) UpdateUserAttributes (request * bean. UserAttributesDto ) (* bean. UserAttributesDto , error ) {
72
67
73
68
userAttribute , err := impl .GetUserAttribute (request )
74
69
if err != nil {
@@ -98,96 +93,160 @@ func (impl UserAttributesServiceImpl) UpdateUserAttributes(request *UserAttribut
98
93
return request , nil
99
94
}
100
95
101
- func (impl UserAttributesServiceImpl ) PatchUserAttributes (request * UserAttributesDto ) (* UserAttributesDto , error ) {
102
- userAttribute , err := impl .GetUserAttribute (request )
96
+ func (impl UserAttributesServiceImpl ) PatchUserAttributes (request * bean. UserAttributesDto ) (* bean. UserAttributesDto , error ) {
97
+ existingAttribute , err := impl .GetUserAttribute (request )
103
98
if err != nil {
104
99
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" )
100
+ return nil , errors .New ("error occurred while getting user attributes" )
106
101
}
107
- if userAttribute == nil {
102
+
103
+ if existingAttribute == nil {
108
104
impl .logger .Info ("no data found for request, so going to add instead of update" , "req" , request )
109
- attributes , err := impl .AddUserAttributes (request )
105
+ newAttribute , err := impl .AddUserAttributes (request )
110
106
if err != nil {
111
107
impl .logger .Errorw ("error in adding new user attributes" , "req" , request , "error" , err )
112
- return nil , errors .New ("error occurred while updating user attributes" )
108
+ return nil , errors .New ("error occurred while adding user attributes" )
113
109
}
114
- return attributes , nil
110
+ return newAttribute , nil
115
111
}
116
112
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 {})
113
+ existingData , err := impl .parseJSONValue (existingAttribute .Value , "existing" )
114
+ if err != nil {
115
+ impl .logger .Errorw ("error in parsing json" , "existingAttribute.Value" , existingAttribute .Value , "error" , err )
116
+ return nil , err
127
117
}
128
118
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 {})
119
+ newData , err := impl .parseJSONValue (request .Value , "request" )
120
+ if err != nil {
121
+ impl .logger .Errorw ("error in parsing request json" , "request.Value" , request .Value , "error" , err )
122
+ return nil , err
139
123
}
140
124
141
- // Check if there are any changes
142
- anyChanges := false
125
+ hasChanges := impl .mergeUserAttributesData (existingData , newData )
126
+ if ! hasChanges {
127
+ impl .logger .Infow ("no changes detected, skipping update" , "key" , request .Key )
128
+ return existingAttribute , nil
129
+ }
143
130
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
- }
131
+ return impl .updateAttributeInDatabase (request , existingData )
132
+ }
133
+
134
+ // parseJSONValue parses a JSON string into a map, with proper error handling
135
+ func (impl UserAttributesServiceImpl ) parseJSONValue (jsonValue , context string ) (map [string ]interface {}, error ) {
136
+ var data map [string ]interface {}
137
+
138
+ if jsonValue == "" {
139
+ return make (map [string ]interface {}), nil
151
140
}
152
141
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
142
+ err := json . Unmarshal ([] byte ( jsonValue ), & data )
143
+ if err != nil {
144
+ impl .logger .Errorw ( "error parsing JSON value" , "context" , context , "value" , jsonValue , "error " , err )
145
+ return nil , errors . New ( "error occurred while parsing user attributes data" )
157
146
}
158
147
159
- // Convert back to JSON string
160
- mergedJson , err := json .Marshal (existingData )
148
+ return data , nil
149
+ }
150
+
151
+ // updateAttributeInDatabase updates the merged data in the database
152
+ func (impl UserAttributesServiceImpl ) updateAttributeInDatabase (request * bean.UserAttributesDto , mergedData map [string ]interface {}) (* bean.UserAttributesDto , error ) {
153
+ mergedJSON , err := json .Marshal (mergedData )
161
154
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" )
155
+ impl .logger .Errorw ("error converting merged data to JSON " , "data" , mergedData , "error" , err )
156
+ return nil , errors .New ("error occurred while processing user attributes" )
164
157
}
165
158
166
159
dao := & repository.UserAttributesDao {
167
160
EmailId : request .EmailId ,
168
161
Key : request .Key ,
169
- Value : string (mergedJson ),
162
+ Value : string (mergedJSON ),
170
163
UserId : request .UserId ,
171
164
}
172
165
166
+ // Update in database
173
167
err = impl .attributesRepository .UpdateDataValByKey (dao )
174
168
if err != nil {
175
- impl .logger .Errorw ("error in update attributes" , "req " , dao , "error" , err )
169
+ impl .logger .Errorw ("error updating user attributes in database " , "dao " , dao , "error" , err )
176
170
return nil , errors .New ("error occurred while updating user attributes" )
177
171
}
178
172
179
- // Return the updated data
180
- result := & UserAttributesDto {
181
- EmailId : request .EmailId ,
182
- Key : request .Key ,
183
- Value : string (mergedJson ),
184
- UserId : request .UserId ,
173
+ // Build and return response
174
+ return adapter .BuildResponseDTO (request , string (mergedJSON )), nil
175
+ }
176
+
177
+ // mergeUserAttributesData merges newData into existingData with special handling for resources
178
+ func (impl UserAttributesServiceImpl ) mergeUserAttributesData (existingData , newData map [string ]interface {}) bool {
179
+ hasChanges := false
180
+
181
+ for key , newValue := range newData {
182
+ if key == bean .UserPreferencesResourcesKey {
183
+ // Special handling for resources - merge nested structure
184
+ if impl .mergeResourcesData (existingData , newValue ) {
185
+ hasChanges = true
186
+ }
187
+ } else {
188
+ if impl .mergeStandardAttribute (existingData , key , newValue ) {
189
+ hasChanges = true
190
+ }
191
+ }
192
+ }
193
+
194
+ return hasChanges
195
+ }
196
+
197
+ // mergeStandardAttribute merges a standard (non-resource) attribute
198
+ func (impl UserAttributesServiceImpl ) mergeStandardAttribute (existingData map [string ]interface {}, key string , newValue interface {}) bool {
199
+ existingValue , exists := existingData [key ]
200
+ if ! exists || ! reflect .DeepEqual (existingValue , newValue ) {
201
+ existingData [key ] = newValue
202
+ return true
203
+ }
204
+ return false
205
+ }
206
+
207
+ // mergeResourcesData handles the special merging logic for the resources object
208
+ func (impl UserAttributesServiceImpl ) mergeResourcesData (existingData map [string ]interface {}, newResourcesValue interface {}) bool {
209
+ impl .ensureResourcesStructureExists (existingData )
210
+
211
+ existingResources , ok := existingData [bean .UserPreferencesResourcesKey ].(map [string ]interface {})
212
+ if ! ok {
213
+ existingData [bean .UserPreferencesResourcesKey ] = newResourcesValue
214
+ return true
215
+ }
216
+
217
+ newResources , ok := newResourcesValue .(map [string ]interface {})
218
+ if ! ok {
219
+ existingData [bean .UserPreferencesResourcesKey ] = newResourcesValue
220
+ return true
221
+ }
222
+
223
+ return impl .mergeResourceTypes (existingResources , newResources )
224
+ }
225
+
226
+ // ensureResourcesStructureExists initializes the resources structure if it doesn't exist
227
+ func (impl UserAttributesServiceImpl ) ensureResourcesStructureExists (existingData map [string ]interface {}) {
228
+ if existingData [bean .UserPreferencesResourcesKey ] == nil {
229
+ existingData [bean .UserPreferencesResourcesKey ] = make (map [string ]interface {})
230
+ }
231
+ }
232
+
233
+ // mergeResourceTypes merges individual resource types from new resources into existing resources
234
+ func (impl UserAttributesServiceImpl ) mergeResourceTypes (existingResources , newResources map [string ]interface {}) bool {
235
+ hasChanges := false
236
+
237
+ // Merge each resource type from newResources
238
+ for resourceType , newResourceData := range newResources {
239
+ existingResourceData , exists := existingResources [resourceType ]
240
+ if ! exists || ! reflect .DeepEqual (existingResourceData , newResourceData ) {
241
+ existingResources [resourceType ] = newResourceData
242
+ hasChanges = true
243
+ }
185
244
}
186
245
187
- return result , nil
246
+ return hasChanges
188
247
}
189
248
190
- func (impl UserAttributesServiceImpl ) GetUserAttribute (request * UserAttributesDto ) (* UserAttributesDto , error ) {
249
+ func (impl UserAttributesServiceImpl ) GetUserAttribute (request * bean. UserAttributesDto ) (* bean. UserAttributesDto , error ) {
191
250
192
251
dao := & repository.UserAttributesDao {
193
252
EmailId : request .EmailId ,
@@ -203,7 +262,7 @@ func (impl UserAttributesServiceImpl) GetUserAttribute(request *UserAttributesDt
203
262
impl .logger .Errorw ("error in fetching user attributes" , "req" , request , "error" , err )
204
263
return nil , errors .New ("error occurred while getting user attributes" )
205
264
}
206
- resAttrDto := & UserAttributesDto {
265
+ resAttrDto := & bean. UserAttributesDto {
207
266
EmailId : request .EmailId ,
208
267
Key : request .Key ,
209
268
Value : modelValue ,
0 commit comments