Skip to content

Commit

Permalink
Extend retry mechanism for specific api errors
Browse files Browse the repository at this point in the history
Truncate long messages
  • Loading branch information
foteinigk committed Apr 25, 2024
1 parent ee2462a commit 2711872
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 50 deletions.
2 changes: 1 addition & 1 deletion internal/txlib/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func (task *MergeResourcePollTask) Run(send func(string), abort func()) {
))
}

err := handleThrottling(
err := handleRetry(
func() error {
return txapi.PollResourceMerge(
merge,
Expand Down
30 changes: 22 additions & 8 deletions internal/txlib/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,19 @@ func (task *ResourcePullTask) Run(send func(string), abort func()) {
if args.Silent && !force {
return
}
send(fmt.Sprintf(

message := fmt.Sprintf(
"%s.%s - %s",
cfgResource.ProjectSlug,
cfgResource.ResourceSlug,
body,
))
)

if !args.Silent {
message = truncateMessage(message)
}

send(message)
}
sendMessage("Getting info", false)

Expand Down Expand Up @@ -364,13 +371,20 @@ func (task *FilePullTask) Run(send func(string), abort func()) {
}

cyan := color.New(color.FgCyan).SprintFunc()
send(fmt.Sprintf(

message := fmt.Sprintf(
"%s.%s %s - %s",
cfgResource.ProjectSlug,
cfgResource.ResourceSlug,
cyan("["+code+"]"),
body,
))
)

if !args.Silent {
message = truncateMessage(message)
}

send(message)
}
sendMessage("Pulling file", false)

Expand Down Expand Up @@ -409,7 +423,7 @@ func (task *FilePullTask) Run(send func(string), abort func()) {
// Creating download job

var download *jsonapi.Resource
err = handleThrottling(
err = handleRetry(
func() error {
var err error
download, err = txapi.CreateResourceStringsAsyncDownload(
Expand All @@ -434,7 +448,7 @@ func (task *FilePullTask) Run(send func(string), abort func()) {

// Polling

err = handleThrottling(
err = handleRetry(
func() error {
return txapi.PollResourceStringsDownload(download, sourceFile)
},
Expand Down Expand Up @@ -513,7 +527,7 @@ func (task *FilePullTask) Run(send func(string), abort func()) {
// Creating download job

var download *jsonapi.Resource
err = handleThrottling(
err = handleRetry(
func() error {
var err error
if args.Pseudo {
Expand Down Expand Up @@ -549,7 +563,7 @@ func (task *FilePullTask) Run(send func(string), abort func()) {

// Polling

err = handleThrottling(
err = handleRetry(
func() error {
return txapi.PollTranslationDownload(download, filePath)
},
Expand Down
39 changes: 28 additions & 11 deletions internal/txlib/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,12 +279,16 @@ func (task *ResourcePushTask) Run(send func(string), abort func()) {
if args.Silent && !force {
return
}
send(fmt.Sprintf(
message := fmt.Sprintf(
"%s.%s - %s",
cfgResource.ProjectSlug,
cfgResource.ResourceSlug,
body,
))
)
if !args.Silent {
message = truncateMessage(message)
}
send(message)
}
sendMessage("Getting info", false)
resource, err := txapi.GetResourceById(api, cfgResource.GetAPv3Id())
Expand Down Expand Up @@ -542,12 +546,16 @@ func (task *LanguagePushTask) Run(send func(string), abort func()) {
if args.Silent && !force {
return
}
send(fmt.Sprintf(
message := fmt.Sprintf(
"%s (%s) - %s",
parts[3],
strings.Join(languages, ", "),
body,
))
)
if !args.Silent {
message = truncateMessage(message)
}
send(message)
}
sendMessage("Pushing", false)

Expand Down Expand Up @@ -594,7 +602,12 @@ func (task *SourceFilePushTask) Run(send func(string), abort func()) {
if args.Silent && !force {
return
}
send(fmt.Sprintf("%s.%s - %s", parts[3], parts[5], body))

message := fmt.Sprintf("%s.%s - %s", parts[3], parts[5], body)
if !args.Silent {
message = truncateMessage(message)
}
send(message)
}

file, err := os.Open(sourceFile)
Expand Down Expand Up @@ -629,7 +642,7 @@ func (task *SourceFilePushTask) Run(send func(string), abort func()) {
// Uploading file

var sourceUpload *jsonapi.Resource
err = handleThrottling(
err = handleRetry(
func() error {
var err error
sourceUpload, err = txapi.UploadSource(
Expand All @@ -650,7 +663,7 @@ func (task *SourceFilePushTask) Run(send func(string), abort func()) {

// Polling

err = handleThrottling(
err = handleRetry(
func() error {
return txapi.PollSourceUpload(sourceUpload)
},
Expand Down Expand Up @@ -693,10 +706,14 @@ func (task *TranslationFileTask) Run(send func(string), abort func()) {
if args.Silent && !force {
return
}
send(fmt.Sprintf(
message := fmt.Sprintf(
"%s.%s %s - %s", parts[3], parts[5],
cyan("["+languageCode+"]"), body,
))
)
if !args.Silent {
message = truncateMessage(message)
}
send(message)
}

// Only check timestamps if -f isn't set and if resource isn't new
Expand All @@ -722,7 +739,7 @@ func (task *TranslationFileTask) Run(send func(string), abort func()) {
// Uploading file

var upload *jsonapi.Resource
err := handleThrottling(
err := handleRetry(
func() error {
var err error
upload, err = pushTranslation(
Expand All @@ -742,7 +759,7 @@ func (task *TranslationFileTask) Run(send func(string), abort func()) {
}

// Polling
err = handleThrottling(
err = handleRetry(
func() error {
return txapi.PollTranslationUpload(upload)
},
Expand Down
18 changes: 13 additions & 5 deletions internal/txlib/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,11 @@ func makeRemoteToLocalLanguageMappings(
}

/*
Run 'do'. If the error returned by 'do' is a jsonapi.ThrottleError, sleep the number of
Run 'do'. If the error returned by 'do' is a jsonapi.RetryError, sleep the number of
seconds indicated by the error and try again. Meanwhile, inform the user of
what's going on using 'send'.
*/
func handleThrottling(do func() error, initialMsg string, send func(string)) error {
func handleRetry(do func() error, initialMsg string, send func(string)) error {
for {
if len(initialMsg) > 0 {
send(initialMsg)
Expand All @@ -166,21 +166,21 @@ func handleThrottling(do func() error, initialMsg string, send func(string)) err
if err == nil {
return nil
} else {
var e *jsonapi.ThrottleError
var e *jsonapi.RetryError
if errors.As(err, &e) {
retryAfter := e.RetryAfter
if isatty.IsTerminal(os.Stdout.Fd()) {
for retryAfter > 0 {
send(fmt.Sprintf(
"Throttled, will retry after %d seconds",
"will retry after %d seconds",
retryAfter,
))
time.Sleep(time.Second)
retryAfter -= 1
}
} else {
send(fmt.Sprintf(
"Throttled, will retry after %d seconds",
"will retry after %d seconds",
retryAfter,
))
time.Sleep(time.Duration(retryAfter) * time.Second)
Expand Down Expand Up @@ -210,3 +210,11 @@ func isValidResolutionPolicy(policy string) (IsValid bool) {
return false

}

func truncateMessage(message string) string {
maxLength := 80
if len(message) > maxLength {
return message[:maxLength-2] + ".."
}
return message
}
24 changes: 24 additions & 0 deletions internal/txlib/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,27 @@ func TestConflictResolution(t *testing.T) {
}

}

func TestTruncateMessage(t *testing.T) {
result := truncateMessage("short message")
assert.Equal(t, result, "short message")

result = truncateMessage(
"this is a long message that needs to be truncated because it exceeds " +
"the maximum length of 75 characters",
)
assert.Equal(
t,
result,
"this is a long message that needs to be truncated because it exceeds the maxim..",
)

result = truncateMessage(
"a message with exactly 75 characters - this message should not be truncated",
)
assert.Equal(
t,
result,
"a message with exactly 75 characters - this message should not be truncated",
)
}
6 changes: 3 additions & 3 deletions pkg/jsonapi/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ func (c *Connection) request(
}
defer response.Body.Close()

throttleErrorResponse := parseThrottleResponse(response)
if throttleErrorResponse != nil {
return nil, throttleErrorResponse
retryErrorResponse := parseRetryResponse(response)
if retryErrorResponse != nil {
return nil, retryErrorResponse
}

errorResponse := parseErrorResponse(response.StatusCode, body)
Expand Down
39 changes: 21 additions & 18 deletions pkg/jsonapi/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ Error type for {json:api} errors.
You can inspect the contents of the error response with type assertions.
Example:
project := jsonapi.Resource{...}
err := project.Save() // Here the server responds with an error
switch e := err.(type) {
case *jsonapi.Error:
// "Smartly" inspect the contents of the error
for _, errorItem := range e.Errors {
if errorItem.Status == "404" {
fmt.Println("Something was not found")
project := jsonapi.Resource{...}
err := project.Save() // Here the server responds with an error
switch e := err.(type) {
case *jsonapi.Error:
// "Smartly" inspect the contents of the error
for _, errorItem := range e.Errors {
if errorItem.Status == "404" {
fmt.Println("Something was not found")
}
}
}
default:
fmt.Printf("%s\n", e)
}
default:
fmt.Printf("%s\n", e)
}
*/
type Error struct {
StatusCode int
Expand Down Expand Up @@ -77,21 +77,24 @@ func (m *RedirectError) Error() string {
"`var e *jsonapi.RedirectError; errors.As(err, &e); e.Location`"
}

type ThrottleError struct {
type RetryError struct {
RetryAfter int
}

func (err ThrottleError) Error() string {
func (err RetryError) Error() string {
return fmt.Sprintf("throttled; retry after %d", err.RetryAfter)
}

func parseThrottleResponse(response *http.Response) *ThrottleError {
if response.StatusCode != 429 {
func parseRetryResponse(response *http.Response) *RetryError {
if response.StatusCode != 429 &&
response.StatusCode != 502 &&
response.StatusCode != 503 &&
response.StatusCode != 504 {
return nil
}
retryAfter, err := strconv.Atoi(response.Header.Get("Retry-After"))
if err != nil {
return &ThrottleError{1}
return &RetryError{1}
}
return &ThrottleError{retryAfter}
return &RetryError{retryAfter}
}
2 changes: 1 addition & 1 deletion pkg/txapi/resource_strings_async_downloads.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func PollResourceStringsDownload(download *jsonapi.Resource, filePath string) er
return nil
} else if download.Attributes["status"] == "failed" {
return fmt.Errorf(
"download of translation '%s' failed",
"failed to download translation '%s'",
download.Relationships["resource"].DataSingular.Id,
)

Expand Down
2 changes: 1 addition & 1 deletion pkg/txapi/resource_strings_async_uploads.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func PollSourceUpload(upload *jsonapi.Resource) error {

if uploadAttributes.Status == "failed" {
// Wrap the "error"
return fmt.Errorf("upload of resource '%s' failed - %w",
return fmt.Errorf("failed to upload of resource '%s' - %w",
upload.Relationships["resource"].DataSingular.Id,
&uploadAttributes)
} else if uploadAttributes.Status == "succeeded" {
Expand Down
2 changes: 1 addition & 1 deletion pkg/txapi/resource_translations_async_downloads.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func PollTranslationDownload(download *jsonapi.Resource, filePath string) error
break
} else if download.Attributes["status"] == "failed" {
return fmt.Errorf(
"download of translation '%s' failed",
"failed to download translation '%s'",
download.Relationships["resource"].DataSingular.Id,
)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/txapi/resource_translations_async_uploads.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func PollTranslationUpload(upload *jsonapi.Resource) error {
if uploadAttributes.Status == "failed" {
// Wrap the "error"
return fmt.Errorf(
"upload of resource '%s', language '%s' failed - %w",
"failed to upload resource '%s', language '%s' - %w",
upload.Relationships["resource"].DataSingular.Id,
upload.Relationships["language"].DataSingular.Id,
&uploadAttributes)
Expand Down

0 comments on commit 2711872

Please sign in to comment.