| 138 | } |
| 139 | |
| 140 | func (p *provider) do(ctx context.Context, method, url string, data []byte) ([]byte, error) { |
| 141 | const maxRetries = 3 |
| 142 | if p.token == "" { |
| 143 | if err := p.refreshToken(ctx); err != nil { |
| 144 | return nil, errors.Wrapf(err, "failed to refresh token") |
| 145 | } |
| 146 | } |
| 147 | for i := 0; i < maxRetries; i++ { |
| 148 | if ctx.Err() != nil { |
| 149 | return nil, ctx.Err() |
| 150 | } |
| 151 | |
| 152 | b, cont, err := func() ([]byte, bool, error) { |
| 153 | req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(data)) |
| 154 | if err != nil { |
| 155 | return nil, false, errors.Wrapf(err, "failed to construct %s %s", method, url) |
| 156 | } |
| 157 | |
| 158 | req.Header.Set("Content-Type", "application/json; charset=utf-8") |
| 159 | if strings.HasPrefix(url, "https://api.dingtalk.com") { |
| 160 | req.Header.Add("x-acs-dingtalk-access-token", p.token) |
| 161 | } else { |
| 162 | url = url + "?access_token=" + p.token |
| 163 | } |
| 164 | |
| 165 | resp, err := p.c.Do(req) |
| 166 | if err != nil { |
| 167 | return nil, false, errors.Wrapf(err, "%s %s", method, url) |
| 168 | } |
| 169 | defer resp.Body.Close() |
| 170 | |
| 171 | b, err := io.ReadAll(resp.Body) |
| 172 | if err != nil { |
| 173 | return nil, false, errors.Wrapf(err, "failed to read body of %s %s", method, url) |
| 174 | } |
| 175 | |
| 176 | var response struct { |
| 177 | Errcode int `json:"errcode"` |
| 178 | Errmsg string `json:"errmsg"` |
| 179 | |
| 180 | Code string `json:"code"` |
| 181 | Message string `json:"message"` |
| 182 | } |
| 183 | if err := json.Unmarshal(b, &response); err != nil { |
| 184 | return nil, false, errors.Errorf("failed to unmarshal response") |
| 185 | } |
| 186 | if response.Errcode == 88 || response.Code == "InvalidAuthentication" { |
| 187 | if err := p.refreshToken(ctx); err != nil { |
| 188 | return nil, false, errors.Wrapf(err, "failed to refresh token") |
| 189 | } |
| 190 | return nil, true, nil |
| 191 | } |
| 192 | if resp.StatusCode != http.StatusOK { |
| 193 | return nil, false, errors.Errorf("received non-200 HTTP code %d for %s %s, %+v", resp.StatusCode, method, url, response) |
| 194 | } |
| 195 | return b, false, nil |
| 196 | }() |
| 197 | if err != nil { |