11package service
22
33import (
4+ "errors"
45 "fmt"
6+ "io"
57 "net/http"
8+ "strings"
69
710 "github.com/UDL-TF/UnitedAPI/internal/logger"
811 "github.com/UDL-TF/UnitedAPI/internal/model"
@@ -41,8 +44,9 @@ func (s *ScoreService) ProcessScoreData(scoreData *model.ScoreData) error {
4144
4245 // Call external API to update scores
4346 if err := s .updateScores (scoreData .MatchID ); err != nil {
44- logger .Log .Error ("Failed to update scores via external API" , zap .Error (err ))
45- return fmt .Errorf ("error updating scores: %w" , err )
47+ if handledErr := s .handleScoreUpdateError (scoreData .MatchID , "Failed to update scores via external API" , err ); handledErr != nil {
48+ return handledErr
49+ }
4650 }
4751
4852 // Check if all rounds are done
@@ -83,8 +87,9 @@ func (s *ScoreService) completeMatch(matchID int) error {
8387 }
8488
8589 if err := s .updateScores (matchID ); err != nil {
86- logger .Log .Error ("Failed to update scores via external API after completion" , zap .Error (err ), zap .Int ("matchID" , matchID ))
87- return fmt .Errorf ("error updating scores: %w" , err )
90+ if handledErr := s .handleScoreUpdateError (matchID , "Failed to update scores via external API after completion" , err ); handledErr != nil {
91+ return handledErr
92+ }
8893 }
8994
9095 return nil
@@ -107,9 +112,42 @@ func (s *ScoreService) updateScores(matchID int) error {
107112 }
108113 defer resp .Body .Close ()
109114
115+ bodyBytes , _ := io .ReadAll (resp .Body )
110116 if resp .StatusCode != http .StatusOK {
111- return fmt . Errorf ( "external API returned non-200 status: %d" , resp .StatusCode )
117+ return & ScoreUpdateError { StatusCode : resp .StatusCode , Body : strings . TrimSpace ( string ( bodyBytes ))}
112118 }
113119
114120 return nil
115121}
122+
123+ // handleScoreUpdateError centralizes how we react to external score sync failures
124+ func (s * ScoreService ) handleScoreUpdateError (matchID int , logMessage string , err error ) error {
125+ var scoreErr * ScoreUpdateError
126+ if errors .As (err , & scoreErr ) && scoreErr .StatusCode == http .StatusUnprocessableEntity {
127+ logger .Log .Warn (logMessage ,
128+ zap .Int ("matchID" , matchID ),
129+ zap .Int ("status" , scoreErr .StatusCode ),
130+ zap .String ("externalMessage" , scoreErr .Body ))
131+ return nil
132+ }
133+
134+ logger .Log .Error (logMessage , zap .Error (err ), zap .Int ("matchID" , matchID ))
135+ return fmt .Errorf ("error updating scores: %w" , err )
136+ }
137+
138+ // ScoreUpdateError captures structured details from the external API when it fails
139+ type ScoreUpdateError struct {
140+ StatusCode int
141+ Body string
142+ }
143+
144+ // Error implements the error interface for ScoreUpdateError
145+ func (e * ScoreUpdateError ) Error () string {
146+ if e == nil {
147+ return ""
148+ }
149+ if e .Body != "" {
150+ return fmt .Sprintf ("external API returned status %d: %s" , e .StatusCode , e .Body )
151+ }
152+ return fmt .Sprintf ("external API returned status %d" , e .StatusCode )
153+ }
0 commit comments