@@ -20,30 +20,25 @@ import (
2020 "encoding/json"
2121 "errors"
2222 "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"
2325 "github.com/go-pg/pg"
2426 "go.uber.org/zap"
2527 "reflect"
2628)
2729
2830type 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 )
3335}
3436
3537type UserAttributesServiceImpl struct {
3638 logger * zap.SugaredLogger
3739 attributesRepository repository.UserAttributesRepository
3840}
3941
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-
4742func NewUserAttributesServiceImpl (logger * zap.SugaredLogger ,
4843 attributesRepository repository.UserAttributesRepository ) * UserAttributesServiceImpl {
4944 serviceImpl := & UserAttributesServiceImpl {
@@ -53,7 +48,7 @@ func NewUserAttributesServiceImpl(logger *zap.SugaredLogger,
5348 return serviceImpl
5449}
5550
56- func (impl UserAttributesServiceImpl ) AddUserAttributes (request * UserAttributesDto ) (* UserAttributesDto , error ) {
51+ func (impl UserAttributesServiceImpl ) AddUserAttributes (request * bean. UserAttributesDto ) (* bean. UserAttributesDto , error ) {
5752 dao := & repository.UserAttributesDao {
5853 EmailId : request .EmailId ,
5954 Key : request .Key ,
@@ -68,7 +63,7 @@ func (impl UserAttributesServiceImpl) AddUserAttributes(request *UserAttributesD
6863 return request , nil
6964}
7065
71- func (impl UserAttributesServiceImpl ) UpdateUserAttributes (request * UserAttributesDto ) (* UserAttributesDto , error ) {
66+ func (impl UserAttributesServiceImpl ) UpdateUserAttributes (request * bean. UserAttributesDto ) (* bean. UserAttributesDto , error ) {
7267
7368 userAttribute , err := impl .GetUserAttribute (request )
7469 if err != nil {
@@ -98,96 +93,160 @@ func (impl UserAttributesServiceImpl) UpdateUserAttributes(request *UserAttribut
9893 return request , nil
9994}
10095
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 )
10398 if err != nil {
10499 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" )
106101 }
107- if userAttribute == nil {
102+
103+ if existingAttribute == nil {
108104 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 )
110106 if err != nil {
111107 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" )
113109 }
114- return attributes , nil
110+ return newAttribute , nil
115111 }
116112
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
127117 }
128118
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
139123 }
140124
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+ }
143130
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
151140 }
152141
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" )
157146 }
158147
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 )
161154 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" )
164157 }
165158
166159 dao := & repository.UserAttributesDao {
167160 EmailId : request .EmailId ,
168161 Key : request .Key ,
169- Value : string (mergedJson ),
162+ Value : string (mergedJSON ),
170163 UserId : request .UserId ,
171164 }
172165
166+ // Update in database
173167 err = impl .attributesRepository .UpdateDataValByKey (dao )
174168 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 )
176170 return nil , errors .New ("error occurred while updating user attributes" )
177171 }
178172
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+ }
185244 }
186245
187- return result , nil
246+ return hasChanges
188247}
189248
190- func (impl UserAttributesServiceImpl ) GetUserAttribute (request * UserAttributesDto ) (* UserAttributesDto , error ) {
249+ func (impl UserAttributesServiceImpl ) GetUserAttribute (request * bean. UserAttributesDto ) (* bean. UserAttributesDto , error ) {
191250
192251 dao := & repository.UserAttributesDao {
193252 EmailId : request .EmailId ,
@@ -203,7 +262,7 @@ func (impl UserAttributesServiceImpl) GetUserAttribute(request *UserAttributesDt
203262 impl .logger .Errorw ("error in fetching user attributes" , "req" , request , "error" , err )
204263 return nil , errors .New ("error occurred while getting user attributes" )
205264 }
206- resAttrDto := & UserAttributesDto {
265+ resAttrDto := & bean. UserAttributesDto {
207266 EmailId : request .EmailId ,
208267 Key : request .Key ,
209268 Value : modelValue ,
0 commit comments