db telemetry

This commit is contained in:
2026-05-02 16:29:07 +02:00
parent 327bb9c867
commit 52d58583e9
16 changed files with 241 additions and 98 deletions

View File

@@ -1,6 +1,7 @@
package bring
import (
"context"
"encoding/json"
"fmt"
"io"
@@ -9,6 +10,8 @@ import (
"regexp"
"strings"
"time"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
const baseURL = "https://api.bring.com/address"
@@ -27,6 +30,7 @@ func NewClient(apiUID, apiKey string) *Client {
apiKey: apiKey,
httpClient: &http.Client{
Timeout: 10 * time.Second,
Transport: otelhttp.NewTransport(http.DefaultTransport),
},
}
}
@@ -79,7 +83,7 @@ func parseStreet(street string) (name string, number string) {
return strings.TrimSpace(street), ""
}
func (c *Client) ValidateAddress(addr Address) (*Address, error) {
func (c *Client) ValidateAddress(ctx context.Context, addr Address) (*Address, error) {
streetName, streetNumber := parseStreet(addr.Street1)
params := url.Values{}
@@ -93,7 +97,7 @@ func (c *Client) ValidateAddress(addr Address) (*Address, error) {
reqURL := fmt.Sprintf("%s/api/no/validation?%s", baseURL, params.Encode())
req, err := http.NewRequest("GET", reqURL, nil)
req, err := http.NewRequestWithContext(ctx, "GET", reqURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}

View File

@@ -11,6 +11,8 @@ import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/plugin/opentelemetry/tracing"
)
var (
@@ -42,6 +44,14 @@ func Connect(dsn string) (*gorm.DB, error) {
return
}
if pluginErr := db.Use(tracing.NewPlugin(
tracing.WithoutMetrics(),
tracing.WithoutQueryVariables(),
)); pluginErr != nil {
err = fmt.Errorf("failed to register otel plugin: %w", pluginErr)
return
}
// Configure connection pool for PostgreSQL.
if isPostgres {
sqlDB, poolErr := db.DB()

View File

@@ -2,11 +2,14 @@ package shippo
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
const baseURL = "https://api.goshippo.com"
@@ -22,7 +25,8 @@ func NewClient(apiKey string) *Client {
return &Client{
apiKey: apiKey,
httpClient: &http.Client{
Timeout: 10 * time.Second,
Timeout: 10 * time.Second,
Transport: otelhttp.NewTransport(http.DefaultTransport),
},
}
}
@@ -75,7 +79,7 @@ type addressResponse struct {
// ValidateAddress sends an address to Shippo for validation.
// Returns the normalized/corrected address if valid, or an error if invalid.
func (c *Client) ValidateAddress(addr Address) (*Address, error) {
func (c *Client) ValidateAddress(ctx context.Context, addr Address) (*Address, error) {
reqBody := addressRequest{
Name: addr.Name,
Street1: addr.Street1,
@@ -93,7 +97,7 @@ func (c *Client) ValidateAddress(addr Address) (*Address, error) {
return nil, fmt.Errorf("failed to marshal address: %w", err)
}
req, err := http.NewRequest("POST", baseURL+"/v1/addresses", bytes.NewReader(body))
req, err := http.NewRequestWithContext(ctx, "POST", baseURL+"/v1/addresses", bytes.NewReader(body))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}

22
pkg/stripeclient/init.go Normal file
View File

@@ -0,0 +1,22 @@
package stripeclient
import (
"net/http"
"time"
"github.com/stripe/stripe-go/v78"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
// InitOTel wires the Stripe SDK to use an HTTP client whose transport emits
// OpenTelemetry client spans. Call once at startup, after tracing is initialized.
func InitOTel() {
httpClient := &http.Client{
Timeout: 80 * time.Second,
Transport: otelhttp.NewTransport(http.DefaultTransport),
}
backends := stripe.NewBackends(httpClient)
stripe.SetBackend(stripe.APIBackend, backends.API)
stripe.SetBackend(stripe.UploadsBackend, backends.Uploads)
stripe.SetBackend(stripe.ConnectBackend, backends.Connect)
}

View File

@@ -108,7 +108,7 @@ type Model struct {
pendingWidth int // buffered width from latest resize
pendingHeight int // buffered height from latest resize
Loading bool // true when fetching data from API
Loading bool // true when fetching data from API
error *VisibleError
APIClient *api.Client
@@ -587,6 +587,38 @@ func (m Model) deleteCardCmd(id uint) tea.Cmd {
}
}
// resolveCartAddress sets the shipping address on the cart, creating it first
// if the address had no ID yet (i.e the user typed it in fresh rather than picking
// from saved addresses)
func (m Model) resolveCartAddress() error {
if m.ShippingInfo == nil {
return nil
}
if m.ShippingInfo.ID != 0 {
if err := m.APIClient.SetCartAddress(m.ShippingInfo.ID); err != nil {
return fmt.Errorf("failed to set address: %w", err)
}
return nil
}
saved, err := m.APIClient.CreateAddress(api.CreateAddressRequest{
Name: m.ShippingInfo.Name,
Street: m.ShippingInfo.Street,
Street2: m.ShippingInfo.Street2,
City: m.ShippingInfo.City,
State: m.ShippingInfo.State,
Zip: m.ShippingInfo.Zip,
Country: m.ShippingInfo.Country,
Phone: m.ShippingInfo.Phone,
})
if err != nil {
return fmt.Errorf("failed to save address: %w", err)
}
if err := m.APIClient.SetCartAddress(saved.ID); err != nil {
return fmt.Errorf("failed to set address: %w", err)
}
return nil
}
// checkoutWithSavedCard sets address and saved card on the cart, then converts.
// Used when the user selects an existing card from the list (no tokenization needed).
func (m Model) checkoutWithSavedCard() tea.Cmd {
@@ -595,28 +627,8 @@ func (m Model) checkoutWithSavedCard() tea.Cmd {
return CheckoutResultMsg{Err: fmt.Errorf("API client or card not available")}
}
// 1. Set the shipping address on the cart
if m.ShippingInfo != nil && m.ShippingInfo.ID != 0 {
if err := m.APIClient.SetCartAddress(m.ShippingInfo.ID); err != nil {
return CheckoutResultMsg{Err: fmt.Errorf("failed to set address: %w", err)}
}
} else if m.ShippingInfo != nil {
saved, err := m.APIClient.CreateAddress(api.CreateAddressRequest{
Name: m.ShippingInfo.Name,
Street: m.ShippingInfo.Street,
Street2: m.ShippingInfo.Street2,
City: m.ShippingInfo.City,
State: m.ShippingInfo.State,
Zip: m.ShippingInfo.Zip,
Country: m.ShippingInfo.Country,
Phone: m.ShippingInfo.Phone,
})
if err != nil {
return CheckoutResultMsg{Err: fmt.Errorf("failed to save address: %w", err)}
}
if err := m.APIClient.SetCartAddress(saved.ID); err != nil {
return CheckoutResultMsg{Err: fmt.Errorf("failed to set address: %w", err)}
}
if err := m.resolveCartAddress(); err != nil {
return CheckoutResultMsg{Err: err}
}
// 2. Set the saved card on the cart
@@ -682,29 +694,8 @@ func (m Model) saveCardAndConvert(form PaymentFormCompleteMsg) tea.Cmd {
return CheckoutResultMsg{Err: fmt.Errorf("failed to save card: %w", err)}
}
// 2. Set the shipping address on the cart
if m.ShippingInfo != nil && m.ShippingInfo.ID != 0 {
if err := m.APIClient.SetCartAddress(m.ShippingInfo.ID); err != nil {
return CheckoutResultMsg{Err: fmt.Errorf("failed to set address: %w", err)}
}
} else if m.ShippingInfo != nil {
// Address was entered fresh (not from saved list), save it first
saved, err := m.APIClient.CreateAddress(api.CreateAddressRequest{
Name: m.ShippingInfo.Name,
Street: m.ShippingInfo.Street,
Street2: m.ShippingInfo.Street2,
City: m.ShippingInfo.City,
State: m.ShippingInfo.State,
Zip: m.ShippingInfo.Zip,
Country: m.ShippingInfo.Country,
Phone: m.ShippingInfo.Phone,
})
if err != nil {
return CheckoutResultMsg{Err: fmt.Errorf("failed to save address: %w", err)}
}
if err := m.APIClient.SetCartAddress(saved.ID); err != nil {
return CheckoutResultMsg{Err: fmt.Errorf("failed to set address: %w", err)}
}
if err := m.resolveCartAddress(); err != nil {
return CheckoutResultMsg{Err: err}
}
// 3. Set the card on the cart