Added structured logging across services and repositories. Updated SQL queries to use parameterized placeholders for better readability and security. Enhanced error handling for external service communication.
This commit is contained in:
@@ -3,11 +3,14 @@ package familyHub
|
||||
import (
|
||||
"FamilyHub/src/config"
|
||||
"FamilyHub/src/domain"
|
||||
"FamilyHub/src/utils"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -63,14 +66,13 @@ func (c *HTTPClient) SendReceipt(ctx context.Context, payload domain.AddReceiptR
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
responseBody, statusCode, err := c.doRequest(req, "familyhub_api.transactions.create", body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("api error: status %d", resp.StatusCode)
|
||||
if statusCode >= 300 {
|
||||
return fmt.Errorf("api error: status %d body %s", statusCode, utils.TruncateForLog(string(responseBody), 512))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -120,14 +122,13 @@ func (c *HTTPClient) RegisterUser(ctx context.Context, payload domain.CreateUser
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
responseBody, statusCode, err := c.doRequest(req, "familyhub_api.users.create", body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("api error: status %d", resp.StatusCode)
|
||||
if statusCode >= 300 {
|
||||
return fmt.Errorf("api error: status %d body %s", statusCode, utils.TruncateForLog(string(responseBody), 512))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -144,22 +145,21 @@ func (c *HTTPClient) GetUserByTelegramID(ctx context.Context, telegramID int64)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
responseBody, statusCode, err := c.doRequest(req, "familyhub_api.users.by_telegram", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
if statusCode == http.StatusNotFound {
|
||||
return nil, errUserNotFound
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 300 {
|
||||
return nil, fmt.Errorf("api error: status %d", resp.StatusCode)
|
||||
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.NewDecoder(resp.Body).Decode(&user); err != nil {
|
||||
if err := json.Unmarshal(responseBody, &user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -184,15 +184,48 @@ func (c *HTTPClient) CreateFamily(ctx context.Context, payload domain.CreateFami
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
responseBody, statusCode, err := c.doRequest(req, "familyhub_api.families.create", body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("api error: status %d", resp.StatusCode)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -2,9 +2,14 @@ package familyHub
|
||||
|
||||
import (
|
||||
"FamilyHub/src/config"
|
||||
"FamilyHub/src/utils"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -18,19 +23,42 @@ func NewBotClient(config config.Config) (*HTTPClient, error) {
|
||||
}
|
||||
|
||||
func (c *HTTPClient) SendMessage(ctx context.Context, chatId int64, message string) error {
|
||||
url := c.config.TelegramApi + "/bot" + c.config.BotToken + "/sendMessage?chat_id=" + strconv.FormatInt(chatId, 10) + "&text=" + message
|
||||
req, err := http.NewRequestWithContext(
|
||||
ctx,
|
||||
http.MethodGet,
|
||||
c.config.TelegramApi+"/bot"+c.config.BotToken+"/sendMessage?chat_id="+strconv.FormatInt(chatId, 10)+"&text="+message,
|
||||
url,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logURL := strings.ReplaceAll(req.URL.String(), c.config.BotToken, "***")
|
||||
log.Printf(
|
||||
"external request: service=telegram_bot.send_message method=%s url=%s body=%q",
|
||||
http.MethodGet,
|
||||
logURL,
|
||||
utils.TruncateForLog(fmt.Sprintf("chat_id=%d&text=%s", chatId, message), utils.DefaultLogValueLimit),
|
||||
)
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
log.Printf("external response: service=telegram_bot.send_message method=%s url=%s err=%v", http.MethodGet, logURL, err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
responseBody, readErr := io.ReadAll(resp.Body)
|
||||
if readErr != nil {
|
||||
log.Printf("external response: service=telegram_bot.send_message method=%s url=%s status=%d read_err=%v", http.MethodGet, logURL, resp.StatusCode, readErr)
|
||||
return readErr
|
||||
}
|
||||
|
||||
log.Printf(
|
||||
"external response: service=telegram_bot.send_message method=%s url=%s status=%d body=%q",
|
||||
http.MethodGet,
|
||||
logURL,
|
||||
resp.StatusCode,
|
||||
utils.TruncateForLog(string(responseBody), utils.DefaultLogValueLimit),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package ocr
|
||||
|
||||
import (
|
||||
"FamilyHub/src/utils"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
vision "cloud.google.com/go/vision/apiv1"
|
||||
)
|
||||
@@ -30,19 +32,29 @@ func (g *GoogleOCR) Close() error {
|
||||
}
|
||||
|
||||
func (g *GoogleOCR) Recognize(ctx context.Context, image []byte) (string, error) {
|
||||
log.Printf("external request: service=google_ocr.detect_text image_size_bytes=%d", len(image))
|
||||
|
||||
img, err := vision.NewImageFromReader(bytes.NewReader(image))
|
||||
if err != nil {
|
||||
log.Printf("external response: service=google_ocr.detect_text err=%v", err)
|
||||
return "", fmt.Errorf("load image: %w", err)
|
||||
}
|
||||
|
||||
annotations, err := g.client.DetectTexts(ctx, img, nil, 1)
|
||||
if err != nil {
|
||||
log.Printf("external response: service=google_ocr.detect_text err=%v", err)
|
||||
return "", fmt.Errorf("detect text: %w", err)
|
||||
}
|
||||
|
||||
if len(annotations) == 0 {
|
||||
log.Printf("external response: service=google_ocr.detect_text result=%q", "")
|
||||
return "", nil
|
||||
}
|
||||
|
||||
log.Printf(
|
||||
"external response: service=google_ocr.detect_text result=%q annotations=%d",
|
||||
utils.TruncateForLog(annotations[0].Description, utils.DefaultLogValueLimit),
|
||||
len(annotations),
|
||||
)
|
||||
return annotations[0].Description, nil
|
||||
}
|
||||
|
||||
@@ -64,6 +64,14 @@ func (s *receiptProvider) GetReceipt(
|
||||
var receipt domain.Receipt
|
||||
|
||||
body, contentType := buildMultipartBody(date, number)
|
||||
requestBody := body.String()
|
||||
log.Printf(
|
||||
"external request: service=receipt_provider method=%s url=%s content_type=%s body=%q",
|
||||
http.MethodPost,
|
||||
url,
|
||||
contentType,
|
||||
utils.TruncateForLog(requestBody, utils.DefaultLogValueLimit),
|
||||
)
|
||||
httpReq, err := http.NewRequestWithContext(
|
||||
ctx,
|
||||
http.MethodPost,
|
||||
@@ -79,18 +87,27 @@ func (s *receiptProvider) GetReceipt(
|
||||
|
||||
resp, err := s.client.Do(httpReq)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
log.Printf("external response: service=receipt_provider method=%s url=%s err=%v", http.MethodPost, url, err)
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
responseBody, readErr := io.ReadAll(resp.Body)
|
||||
if readErr != nil {
|
||||
log.Printf("failed to read external service response body: %v", readErr)
|
||||
return nil, readErr
|
||||
}
|
||||
|
||||
bodyText := strings.TrimSpace(string(responseBody))
|
||||
log.Printf(
|
||||
"external response: service=receipt_provider method=%s url=%s status=%d body=%q",
|
||||
http.MethodPost,
|
||||
url,
|
||||
resp.StatusCode,
|
||||
utils.TruncateForLog(bodyText, utils.DefaultLogValueLimit),
|
||||
)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
responseBody, readErr := io.ReadAll(io.LimitReader(resp.Body, 4096))
|
||||
if readErr != nil {
|
||||
log.Printf("failed to read external service error body: %v", readErr)
|
||||
}
|
||||
bodyText := strings.TrimSpace(string(responseBody))
|
||||
log.Printf("external service returned %d body=%q", resp.StatusCode, bodyText)
|
||||
return nil, &ExternalServiceError{
|
||||
StatusCode: resp.StatusCode,
|
||||
Body: bodyText,
|
||||
@@ -100,8 +117,8 @@ func (s *receiptProvider) GetReceipt(
|
||||
var raw struct {
|
||||
Message map[string]interface{} `json:"message"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&raw); err != nil {
|
||||
log.Printf("external service returned %s\n", err.Error())
|
||||
if err := json.Unmarshal(responseBody, &raw); err != nil {
|
||||
log.Printf("external service returned invalid json: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -112,6 +129,7 @@ func (s *receiptProvider) GetReceipt(
|
||||
}
|
||||
|
||||
if receipt.IssuedAtRaw == "" {
|
||||
log.Printf("external response parse failed: service=receipt_provider err=%v date=%s number=%s", ErrReceiptNotFound, date, number)
|
||||
return nil, ErrReceiptNotFound
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user