@@ -43,7 +43,7 @@ type ComplexInput struct {
4343 Category string `json:"category" validate:"required,min=1,max=100"`
4444 SurfaceGoal string `json:"surface_goal,omitempty" validate:"omitempty,min=1,max=255"` // Optional: Goal to be created with complex
4545 UnderlyingGoal string `json:"underlying_goal,omitempty" validate:"omitempty,min=1,max=255"` // Optional: Goal to be created with complex
46- InitialActions []ActionInput `json:"initial_actions ,omitempty" validate:"omitempty,dive"` // Optional: Actions for the new goal
46+ Actions []ActionInput `json:"actions ,omitempty" validate:"omitempty,dive"` // Optional: Actions for the new goal
4747}
4848
4949// Goal represents the goal entity.
@@ -169,6 +169,7 @@ func CreateComplexHandler(c *gin.Context) {
169169 UnderlyingGoal : input .UnderlyingGoal ,
170170 }
171171 if result := tx .Create (& goal ); result .Error != nil {
172+ //lint:ignore ST1005 Error message is clear
172173 tx .Rollback ()
173174 c .JSON (http .StatusInternalServerError , NewErrorResponse (http .StatusInternalServerError , "Failed to create associated goal: " + result .Error .Error ()))
174175 return
@@ -258,25 +259,106 @@ func UpdateComplexHandler(c *gin.Context) {
258259 return
259260 }
260261
261- var complex Complex
262- if result := db .Where ("id = ? AND user_id = ?" , complexID , userID .(string )).First (& complex ); result .Error != nil {
263- if result .Error == gorm .ErrRecordNotFound {
262+ // Start a database transaction
263+ tx := db .Begin ()
264+ if tx .Error != nil {
265+ c .JSON (http .StatusInternalServerError , NewErrorResponse (http .StatusInternalServerError , "Failed to start transaction: " + tx .Error .Error ()))
266+ return
267+ }
268+
269+ var existingComplex Complex
270+ if err := tx .Where ("id = ? AND user_id = ?" , complexID , userID .(string )).First (& existingComplex ).Error ; err != nil {
271+ tx .Rollback ()
272+ if err == gorm .ErrRecordNotFound {
264273 c .JSON (http .StatusNotFound , NewErrorResponse (http .StatusNotFound , "Complex not found to update" ))
265274 return
266275 }
267- c .JSON (http .StatusInternalServerError , NewErrorResponse (http .StatusInternalServerError , "Failed to find complex to update: " + result . Error .Error ()))
276+ c .JSON (http .StatusInternalServerError , NewErrorResponse (http .StatusInternalServerError , "Failed to find complex to update: " + err .Error ()))
268277 return
269278 }
270279
271- complex .Content = input .Content
272- complex .Category = input .Category
280+ // Update Complex fields
281+ existingComplex .Content = input .Content
282+ existingComplex .Category = input .Category
273283
274- if result := db .Save (& complex ); result .Error != nil {
275- c .JSON (http .StatusInternalServerError , NewErrorResponse (http .StatusInternalServerError , "Failed to update complex: " + result .Error .Error ()))
284+ if err := tx .Save (& existingComplex ).Error ; err != nil {
285+ tx .Rollback ()
286+ c .JSON (http .StatusInternalServerError , NewErrorResponse (http .StatusInternalServerError , "Failed to update complex: " + err .Error ()))
276287 return
277288 }
278289
279- c .JSON (http .StatusOK , complex )
290+ // Handle associated Goal (create or update)
291+ if input .SurfaceGoal != "" && input .UnderlyingGoal != "" {
292+ var goal Goal
293+ err := tx .Where ("complex_id = ? AND user_id = ?" , existingComplex .ID , userID .(string )).First (& goal ).Error
294+
295+ if err != nil && err != gorm .ErrRecordNotFound {
296+ tx .Rollback ()
297+ c .JSON (http .StatusInternalServerError , NewErrorResponse (http .StatusInternalServerError , "Error finding associated goal: " + err .Error ()))
298+ return
299+ }
300+
301+ if err == gorm .ErrRecordNotFound { // Goal does not exist, create it
302+ goal = Goal {
303+ UserID : userID .(string ),
304+ ComplexID : existingComplex .ID ,
305+ SurfaceGoal : input .SurfaceGoal ,
306+ UnderlyingGoal : input .UnderlyingGoal ,
307+ }
308+ if err := tx .Create (& goal ).Error ; err != nil {
309+ tx .Rollback ()
310+ c .JSON (http .StatusInternalServerError , NewErrorResponse (http .StatusInternalServerError , "Failed to create associated goal: " + err .Error ()))
311+ return
312+ }
313+ // If new goal created, and initial actions are provided, create them
314+ if len (input .Actions ) > 0 {
315+ for _ , actionInput := range input .Actions {
316+ var completedAtParsed * time.Time
317+ if actionInput .CompletedAt != "" {
318+ t , err := time .Parse (time .RFC3339 , actionInput .CompletedAt )
319+ if err != nil {
320+ tx .Rollback ()
321+ c .JSON (http .StatusBadRequest , NewErrorResponse (http .StatusBadRequest , "Invalid completed_at format for action: " + err .Error ()))
322+ return
323+ }
324+ completedAtParsed = & t
325+ }
326+
327+ action := Action {
328+ UserID : userID .(string ),
329+ GoalID : goal .ID , // Link to the newly created goal
330+ Content : actionInput .Content ,
331+ CompletedAt : completedAtParsed ,
332+ }
333+ if err := tx .Create (& action ).Error ; err != nil {
334+ tx .Rollback ()
335+ c .JSON (http .StatusInternalServerError , NewErrorResponse (http .StatusInternalServerError , "Failed to create action: " + err .Error ()))
336+ return
337+ }
338+ }
339+ }
340+ } else { // Goal exists, update it
341+ goal .SurfaceGoal = input .SurfaceGoal
342+ goal .UnderlyingGoal = input .UnderlyingGoal
343+ if err := tx .Save (& goal ).Error ; err != nil {
344+ tx .Rollback ()
345+ c .JSON (http .StatusInternalServerError , NewErrorResponse (http .StatusInternalServerError , "Failed to update associated goal: " + err .Error ()))
346+ return
347+ }
348+ }
349+ } else if input .SurfaceGoal != "" || input .UnderlyingGoal != "" {
350+ tx .Rollback ()
351+ c .JSON (http .StatusBadRequest , NewErrorResponse (http .StatusBadRequest , "Both surface_goal and underlying_goal are required to create or update a goal via complex endpoint" ))
352+ return
353+ }
354+
355+ if err := tx .Commit ().Error ; err != nil {
356+ c .JSON (http .StatusInternalServerError , NewErrorResponse (http .StatusInternalServerError , "Failed to commit transaction: " + err .Error ()))
357+ return
358+ }
359+
360+ db .Preload ("Goals" ).First (& existingComplex , existingComplex .ID )
361+ c .JSON (http .StatusOK , existingComplex )
280362}
281363
282364// DeleteComplexHandler handles deleting a complex by its ID.
0 commit comments