-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrequest.go
More file actions
137 lines (116 loc) · 3.42 KB
/
Copy pathrequest.go
File metadata and controls
137 lines (116 loc) · 3.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package gonetic
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/joy-dx/gonetic/client/httpclient"
"github.com/joy-dx/gonetic/dto"
"github.com/joy-dx/gonetic/utils"
)
// Get RequestWithRetry
func (s *NetSvc) Get(ctx context.Context, url string, withRetry bool) (dto.Response, error) {
httpRequestConfig := httpclient.DefaultHTTPRequestConfig()
httpRequestConfig.WithURL(url)
cfg := dto.DefaultRequestConfig()
cfg.WithReqConfig(&httpRequestConfig).
WithTaskName("GET " + url)
if withRetry {
return s.RequestWithRetry(ctx, &cfg)
}
return s.RequestOnce(ctx, &cfg)
}
// Post RequestWithRetry
func (s *NetSvc) Post(ctx context.Context, url string, payload map[string]interface{}, withRetry bool) (dto.Response, error) {
httpRequestConfig := httpclient.DefaultHTTPRequestConfig()
httpRequestConfig.WithURL(url).
WithBody(payload).
WithMethod(http.MethodPost)
cfg := dto.DefaultRequestConfig()
cfg.WithReqConfig(&httpRequestConfig).
WithTaskName("POST " + url)
if withRetry {
return s.RequestWithRetry(ctx, &cfg)
}
return s.RequestOnce(ctx, &cfg)
}
func (s *NetSvc) RequestWithRetry(ctx context.Context, cfg *dto.RequestConfig) (dto.Response, error) {
if cfg == nil {
return dto.Response{}, errors.New("nil RequestConfig provided")
}
if cfg.MaxRetries < 0 {
cfg.MaxRetries = 0
}
if cfg.Delay == nil {
cfg.Delay = utils.ConstantDelay{Period: 1}
}
var lastErr error
for attempt := 0; attempt <= cfg.MaxRetries; attempt++ {
if attempt > 0 {
cfg.Delay.Wait(cfg.TaskName, attempt)
}
resp, err := s.RequestOnce(ctx, cfg)
if err != nil {
lastErr = err
// transient network errors → retry
if utils.IsTemporaryErr(err) && attempt < cfg.MaxRetries {
continue
}
return resp, err
}
if resp.StatusCode >= 500 {
lastErr = fmt.Errorf("server error (%d)", resp.StatusCode)
if attempt < cfg.MaxRetries {
continue
}
// exhausted retries: return response + error
return resp, fmt.Errorf(
"failed after %d attempts: %w",
cfg.MaxRetries+1,
lastErr,
)
}
return resp, nil
}
return dto.Response{}, fmt.Errorf("failed after %d attempts: %w", cfg.MaxRetries+1, lastErr)
}
func (s *NetSvc) RequestOnce(ctx context.Context, cfg *dto.RequestConfig) (dto.Response, error) {
if cfg.ClientRef == "" {
return dto.Response{}, errors.New("nil ClientRef provided")
}
if cfg.ReqConfig == nil {
return dto.Response{}, errors.New("nil ReqConfig provided")
}
if cfg.TaskName == "" {
cfg.TaskName = "http_request"
}
netClient, isOK := s.clients[cfg.ClientRef]
if !isOK {
return dto.Response{}, fmt.Errorf("client not found: %s", cfg.ClientRef)
}
// Sanity check that the req config matches the client type to avoid later casting confusion
if netClient.Type() != cfg.ReqConfig.Ref() {
return dto.Response{}, fmt.Errorf(
"client type mismatch: client=%s(%s) req=%s",
cfg.ClientRef,
netClient.Type(),
cfg.ReqConfig.Ref(),
)
}
if cfg.Timeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, cfg.Timeout)
defer cancel()
}
response, err := netClient.ProcessRequest(ctx, cfg)
if err != nil {
return dto.Response{}, fmt.Errorf("perform request: %w", err)
}
if cfg.ResponseObject != nil && len(response.Body) > 0 {
if unmarshalErr := json.Unmarshal(response.Body, cfg.ResponseObject); unmarshalErr != nil {
return response, fmt.Errorf("unmarshal response: %w", unmarshalErr)
}
}
return response, nil
}