Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `ym.Ptr[T]` generic helper for optional pointer fields in request structs.
- `ym.ValidateRecipient` shared validation for ChatID/Login recipient parameters.
- Root-level and package-level `doc.go` files with usage examples.
- `CONTRIBUTING.md` with development setup and guidelines.
- `CHANGELOG.md` (this file).
- `Makefile` with `test`, `lint`, `release-patch`, and `release-minor` targets.
- GitHub Actions release workflow (`.github/workflows/release.yml`).
Expand Down
72 changes: 0 additions & 72 deletions CONTRIBUTING.md

This file was deleted.

4 changes: 0 additions & 4 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,3 @@ go get github.com/rekurt/ymsdk@v0.1.0
```bash
go test ./...
```

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md).
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,3 @@ go get github.com/rekurt/ymsdk@v0.1.0
```bash
go test ./...
```

## Участие в разработке

См. [CONTRIBUTING.md](CONTRIBUTING.md).
13 changes: 8 additions & 5 deletions client/ym/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (c *Client) DoRequest(ctx context.Context, method, path string, body any) (
var netErr net.Error
if errors.As(doErr, &netErr) && retryCfg.RetryNetwork && attempt < attempts {
time.Sleep(backoff)
backoff = nextBackoff(backoff, retryCfg.MaxBackoff)
backoff = NextBackoff(backoff, retryCfg.MaxBackoff)

continue
}
Expand Down Expand Up @@ -133,9 +133,9 @@ func (c *Client) DoRequest(ctx context.Context, method, path string, body any) (
continue
}

if shouldRetryHTTP(apiErr.HTTPStatus, retryCfg.RetryHTTP) && attempt < attempts {
if ShouldRetryHTTP(apiErr.HTTPStatus, retryCfg.RetryHTTP) && attempt < attempts {
time.Sleep(backoff)
backoff = nextBackoff(backoff, retryCfg.MaxBackoff)
backoff = NextBackoff(backoff, retryCfg.MaxBackoff)

continue
}
Expand Down Expand Up @@ -250,7 +250,9 @@ func applyDefaults(cfg Config) Config {
return cfg
}

func nextBackoff(current, maximum time.Duration) time.Duration {
// NextBackoff doubles the current backoff duration, capping at maximum.
// Used internally for retry logic; exported for use by multipart upload services.
func NextBackoff(current, maximum time.Duration) time.Duration {
if current <= 0 {
current = 500 * time.Millisecond
}
Expand All @@ -262,7 +264,8 @@ func nextBackoff(current, maximum time.Duration) time.Duration {
return next
}

func shouldRetryHTTP(status int, list []int) bool {
// ShouldRetryHTTP checks if the given HTTP status code is in the retryable list.
func ShouldRetryHTTP(status int, list []int) bool {
for _, s := range list {
if status == s {
return true
Expand Down
28 changes: 3 additions & 25 deletions client/ym/files/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ func (s *Service) doMultipartWithRetry(
var netErr net.Error
if errors.As(doErr, &netErr) && retryCfg.RetryNetwork && attempt < attempts {
time.Sleep(backoff)
backoff = nextBackoffFiles(backoff, retryCfg.MaxBackoff)
backoff = ym.NextBackoff(backoff, retryCfg.MaxBackoff)

continue
}
Expand Down Expand Up @@ -198,9 +198,9 @@ func (s *Service) doMultipartWithRetry(
continue
}

if shouldRetryHTTPFiles(apiErr.HTTPStatus, retryCfg.RetryHTTP) && attempt < attempts {
if ym.ShouldRetryHTTP(apiErr.HTTPStatus, retryCfg.RetryHTTP) && attempt < attempts {
time.Sleep(backoff)
backoff = nextBackoffFiles(backoff, retryCfg.MaxBackoff)
backoff = ym.NextBackoff(backoff, retryCfg.MaxBackoff)

continue
}
Expand All @@ -210,25 +210,3 @@ func (s *Service) doMultipartWithRetry(

return nil, fmt.Errorf("yandex-messenger/files: retries exhausted for %s %s", method, path)
}

func nextBackoffFiles(current, maximum time.Duration) time.Duration {
if current <= 0 {
current = 500 * time.Millisecond
}
next := current * 2
if maximum > 0 && next > maximum {
return maximum
}

return next
}

func shouldRetryHTTPFiles(status int, list []int) bool {
for _, s := range list {
if status == s {
return true
}
}

return false
}
28 changes: 3 additions & 25 deletions client/ym/messages/attachments.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ func (s *Service) doMultipart(ctx context.Context, path, contentType string, pay
var netErr net.Error
if errors.As(doErr, &netErr) && retryCfg.RetryNetwork && attempt < attempts {
time.Sleep(backoff)
backoff = nextBackoffFiles(backoff, retryCfg.MaxBackoff)
backoff = ym.NextBackoff(backoff, retryCfg.MaxBackoff)

continue
}
Expand Down Expand Up @@ -344,9 +344,9 @@ func (s *Service) doMultipart(ctx context.Context, path, contentType string, pay

continue
}
if shouldRetryHTTPFiles(apiErr.HTTPStatus, retryCfg.RetryHTTP) && attempt < attempts {
if ym.ShouldRetryHTTP(apiErr.HTTPStatus, retryCfg.RetryHTTP) && attempt < attempts {
time.Sleep(backoff)
backoff = nextBackoffFiles(backoff, retryCfg.MaxBackoff)
backoff = ym.NextBackoff(backoff, retryCfg.MaxBackoff)

continue
}
Expand All @@ -356,25 +356,3 @@ func (s *Service) doMultipart(ctx context.Context, path, contentType string, pay

return nil, fmt.Errorf("yandex-messenger/messages: retries exhausted for %s", path)
}

func nextBackoffFiles(current, maximum time.Duration) time.Duration {
if current <= 0 {
current = 500 * time.Millisecond
}
next := current * 2
if maximum > 0 && next > maximum {
return maximum
}

return next
}

func shouldRetryHTTPFiles(status int, list []int) bool {
for _, s := range list {
if status == s {
return true
}
}

return false
}
42 changes: 42 additions & 0 deletions client/ym/ptr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ym

import (
"testing"
)

func TestPtr(t *testing.T) {
t.Run("int", func(t *testing.T) {
p := Ptr(42)
if *p != 42 {
t.Fatalf("expected 42, got %d", *p)
}
})

t.Run("string", func(t *testing.T) {
p := Ptr("hello")
if *p != "hello" {
t.Fatalf("expected hello, got %s", *p)
}
})

t.Run("bool", func(t *testing.T) {
p := Ptr(true)
if !*p {
t.Fatal("expected true")
}
})

t.Run("ChatID", func(t *testing.T) {
p := Ptr(ChatID("chat-1"))
if *p != "chat-1" {
t.Fatalf("expected chat-1, got %s", *p)
}
})

t.Run("zero value", func(t *testing.T) {
p := Ptr(0)
if *p != 0 {
t.Fatalf("expected 0, got %d", *p)
}
})
}
58 changes: 58 additions & 0 deletions client/ym/retry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package ym

import (
"testing"
"time"
)

func TestNextBackoff(t *testing.T) {
t.Run("doubles backoff", func(t *testing.T) {
got := NextBackoff(500*time.Millisecond, 10*time.Second)
if got != time.Second {
t.Fatalf("expected 1s, got %v", got)
}
})

t.Run("caps at maximum", func(t *testing.T) {
got := NextBackoff(8*time.Second, 10*time.Second)
if got != 10*time.Second {
t.Fatalf("expected 10s, got %v", got)
}
})

t.Run("zero current uses default", func(t *testing.T) {
got := NextBackoff(0, 10*time.Second)
if got != time.Second {
t.Fatalf("expected 1s, got %v", got)
}
})

t.Run("no maximum", func(t *testing.T) {
got := NextBackoff(time.Second, 0)
if got != 2*time.Second {
t.Fatalf("expected 2s, got %v", got)
}
})
}

func TestShouldRetryHTTP(t *testing.T) {
list := []int{500, 502, 503, 504}

t.Run("retryable status", func(t *testing.T) {
if !ShouldRetryHTTP(502, list) {
t.Fatal("expected 502 to be retryable")
}
})

t.Run("non-retryable status", func(t *testing.T) {
if ShouldRetryHTTP(400, list) {
t.Fatal("expected 400 to not be retryable")
}
})

t.Run("empty list", func(t *testing.T) {
if ShouldRetryHTTP(500, nil) {
t.Fatal("expected false with nil list")
}
})
}
Loading
Loading