Files
FamilyHUB/backend/src/integrations/familyHub/apiClient.go
T

232 lines
5.3 KiB
Go

package familyHub
import (
"FamilyHub/src/config"
"FamilyHub/src/domain"
"FamilyHub/src/utils"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"strconv"
"time"
)
var errUserNotFound = errors.New("user not found")
func NewApiClient(config config.Config) (*HTTPClient, error) {
return &HTTPClient{
config: config,
client: &http.Client{
Timeout: 60 * time.Second,
},
}, nil
}
func (c *HTTPClient) SendReceipt(ctx context.Context, payload domain.AddReceiptRequest) error {
requestBody := map[string]any{
"receipt_number": payload.Number,
"receipt_date": payload.Date,
}
if payload.FamilyID != nil {
requestBody["family_id"] = *payload.FamilyID
}
if payload.CreatedBy != nil {
requestBody["created_by"] = *payload.CreatedBy
}
if payload.Type != nil {
requestBody["type"] = *payload.Type
}
if payload.Category != nil {
requestBody["category"] = *payload.Category
}
if payload.Description != nil {
requestBody["description"] = *payload.Description
}
body, err := json.Marshal(requestBody)
if err != nil {
return err
}
req, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
c.config.APIHost+c.config.APIPort+"/api/v1/transactions",
bytes.NewReader(body),
)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
responseBody, statusCode, err := c.doRequest(req, "familyhub_api.transactions.create", body)
if err != nil {
return err
}
if statusCode >= 300 {
return fmt.Errorf("api error: status %d body %s", statusCode, utils.TruncateForLog(string(responseBody), 512))
}
return nil
}
func (c *HTTPClient) EnsureUser(ctx context.Context, payload domain.CreateUserRequest) error {
registered, err := c.IsUserRegistered(ctx, payload.TelegramID)
if err != nil {
return err
}
if registered {
return nil
}
return c.RegisterUser(ctx, payload)
}
func (c *HTTPClient) IsUserRegistered(ctx context.Context, telegramID int64) (bool, error) {
_, err := c.GetUserByTelegramID(ctx, telegramID)
if err == nil {
return true, nil
}
if errors.Is(err, errUserNotFound) {
return false, nil
}
return false, err
}
func (c *HTTPClient) RegisterUser(ctx context.Context, payload domain.CreateUserRequest) error {
body, err := json.Marshal(payload)
if err != nil {
return err
}
req, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
c.config.APIHost+c.config.APIPort+"/api/v1/users",
bytes.NewReader(body),
)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
responseBody, statusCode, err := c.doRequest(req, "familyhub_api.users.create", body)
if err != nil {
return err
}
if statusCode >= 300 {
return fmt.Errorf("api error: status %d body %s", statusCode, utils.TruncateForLog(string(responseBody), 512))
}
return nil
}
func (c *HTTPClient) GetUserByTelegramID(ctx context.Context, telegramID int64) (*domain.UserResponse, error) {
req, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
c.config.APIHost+c.config.APIPort+"/api/v1/users/by-telegram/"+strconv.FormatInt(telegramID, 10),
nil,
)
if err != nil {
return nil, err
}
responseBody, statusCode, err := c.doRequest(req, "familyhub_api.users.by_telegram", nil)
if err != nil {
return nil, err
}
if statusCode == http.StatusNotFound {
return nil, errUserNotFound
}
if statusCode >= 300 {
return nil, fmt.Errorf("api error: status %d body %s", statusCode, utils.TruncateForLog(string(responseBody), 512))
}
var user domain.UserResponse
if err := json.Unmarshal(responseBody, &user); err != nil {
return nil, err
}
return &user, nil
}
func (c *HTTPClient) CreateFamily(ctx context.Context, payload domain.CreateFamilyRequest) error {
body, err := json.Marshal(payload)
if err != nil {
return err
}
req, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
c.config.APIHost+c.config.APIPort+"/api/v1/families",
bytes.NewReader(body),
)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
responseBody, statusCode, err := c.doRequest(req, "familyhub_api.families.create", body)
if err != nil {
return err
}
if statusCode >= 300 {
return fmt.Errorf("api error: status %d body %s", statusCode, utils.TruncateForLog(string(responseBody), 512))
}
return nil
}
func (c *HTTPClient) doRequest(req *http.Request, service string, requestBody []byte) ([]byte, int, error) {
log.Printf(
"external request: service=%s method=%s url=%s body=%q",
service,
req.Method,
req.URL.String(),
utils.TruncateForLog(string(requestBody), utils.DefaultLogValueLimit),
)
resp, err := c.client.Do(req)
if err != nil {
log.Printf("external response: service=%s method=%s url=%s err=%v", service, req.Method, req.URL.String(), err)
return nil, 0, err
}
defer resp.Body.Close()
responseBody, readErr := io.ReadAll(resp.Body)
if readErr != nil {
log.Printf("external response: service=%s method=%s url=%s status=%d read_err=%v", service, req.Method, req.URL.String(), resp.StatusCode, readErr)
return nil, resp.StatusCode, readErr
}
log.Printf(
"external response: service=%s method=%s url=%s status=%d body=%q",
service,
req.Method,
req.URL.String(),
resp.StatusCode,
utils.TruncateForLog(string(responseBody), utils.DefaultLogValueLimit),
)
return responseBody, resp.StatusCode, nil
}