2025-10-29 06:14:57 -04:00

101 lines
2.2 KiB
Go

package external_api
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"github.com/tuusuario/go-sync-service/internal/domain/dto"
"io"
"net/http"
"time"
)
// Estructura para guardar trazabilidad
// Client genérico con reintentos
type GenericClient struct {
Client *http.Client
MaxRetries int
RetryDelay time.Duration
EnableTrace bool // true = guarda trazabilidad
}
// Constructor
func NewGenericClient(maxRetries int, retryDelay time.Duration) *GenericClient {
return &GenericClient{
Client: &http.Client{Timeout: 30 * time.Second, Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}},
MaxRetries: maxRetries,
RetryDelay: retryDelay,
}
}
// Método genérico
func (gc *GenericClient) DoRequest(method, url string, headers map[string]string, body []byte) (map[string]interface{}, *dto.RequestTrace, error) {
trace := &dto.RequestTrace{
Method: method,
URL: url,
Headers: headers,
RequestBody: string(body),
StartedAt: time.Now(),
}
var lastErr error
var resp *http.Response
for attempt := 0; attempt <= gc.MaxRetries; attempt++ {
trace.Retries = attempt
req, err := http.NewRequest(method, url, bytes.NewReader(body))
if err != nil {
lastErr = err
break
}
// Headers
for k, v := range headers {
req.Header.Set(k, v)
}
start := time.Now()
resp, err = gc.Client.Do(req)
trace.Duration = time.Since(start)
if err == nil && resp.StatusCode < 500 {
break // éxito o error de cliente, no reintentar
}
lastErr = err
time.Sleep(gc.RetryDelay * (1 << attempt)) // backoff exponencial
}
if lastErr != nil {
trace.EndedAt = time.Now()
return nil, trace, lastErr
}
defer resp.Body.Close()
trace.StatusCode = resp.StatusCode
bodyResp, _ := io.ReadAll(resp.Body)
trace.Response = string(bodyResp)
trace.EndedAt = time.Now()
if resp.StatusCode >= 400 {
return nil, trace, errors.New("HTTP error: " + resp.Status)
}
// Si el código de estado es 204 (No Content), no hay cuerpo de respuesta
if resp.StatusCode == http.StatusNoContent {
return nil, trace, nil
}
var result map[string]interface{}
if err := json.Unmarshal(bodyResp, &result); err != nil {
return nil, trace, err
}
return result, trace, nil
}