232 lines
5.3 KiB
Go
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
|
|
}
|