more cart
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
package order
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ProfilePreferencesClient is the subset of the profile service's UCP customer
|
||||
// API that returns email preferences for a given email address.
|
||||
type ProfilePreferencesClient struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewProfilePreferencesClient creates a client that talks to the profile
|
||||
// service's UCP customer endpoint at baseURL (e.g. "http://profile:8080/ucp/v1/customers").
|
||||
func NewProfilePreferencesClient(baseURL string) *ProfilePreferencesClient {
|
||||
return &ProfilePreferencesClient{
|
||||
baseURL: strings.TrimRight(baseURL, "/"),
|
||||
httpClient: &http.Client{Timeout: 5 * time.Second},
|
||||
timeout: 5 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// emailPreferencesResponse mirrors ucp.EmailPreferencesResponse for decoding.
|
||||
type emailPreferencesResponse struct {
|
||||
OrderEmails *bool `json:"orderEmails,omitempty"`
|
||||
MarketingEmails *bool `json:"marketingEmails,omitempty"`
|
||||
}
|
||||
|
||||
// customerLookupResponse mirrors the UCP customer response, but we only need
|
||||
// email preferences.
|
||||
type customerLookupResponse struct {
|
||||
EmailPreferences *emailPreferencesResponse `json:"emailPreferences,omitempty"`
|
||||
}
|
||||
|
||||
// fetchPreferences calls GET /by-email/{email} on the profile service and
|
||||
// returns the parsed email preferences. Returns nil, nil when the profile
|
||||
// is not found (unknown email -> allow all).
|
||||
func (c *ProfilePreferencesClient) fetchPreferences(ctx context.Context, email string) (*emailPreferencesResponse, error) {
|
||||
url := fmt.Sprintf("%s/by-email/%s", c.baseURL, email)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("profile prefs: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil, nil // unknown email -> no preferences
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("profile prefs: unexpected status %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var data customerLookupResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
return nil, fmt.Errorf("profile prefs: decode: %w", err)
|
||||
}
|
||||
return data.EmailPreferences, nil
|
||||
}
|
||||
|
||||
// PreferencesChecker wraps ProfilePreferencesClient to implement
|
||||
// EmailPreferenceChecker.
|
||||
type PreferencesChecker struct {
|
||||
client *ProfilePreferencesClient
|
||||
}
|
||||
|
||||
// NewPreferencesChecker creates an EmailPreferenceChecker that looks up
|
||||
// preferences from the profile service at the given base URL.
|
||||
func NewPreferencesChecker(profileServiceURL string) *PreferencesChecker {
|
||||
return &PreferencesChecker{
|
||||
client: NewProfilePreferencesClient(profileServiceURL),
|
||||
}
|
||||
}
|
||||
|
||||
// Allowed returns true when the customer has not opted out of the given
|
||||
// category. Categories are "order" (order confirmations, shipping updates,
|
||||
// receipts) and "marketing" (promotional offers, newsletters). Returns true
|
||||
// when preferences are unknown (profile not found) — the safe default.
|
||||
func (c *PreferencesChecker) Allowed(ctx context.Context, email string, category string) bool {
|
||||
prefs, err := c.client.fetchPreferences(ctx, email)
|
||||
if err != nil || prefs == nil {
|
||||
return true // safe default: allow when unknown
|
||||
}
|
||||
|
||||
switch category {
|
||||
case "order":
|
||||
return prefs.OrderEmails == nil || *prefs.OrderEmails
|
||||
case "marketing":
|
||||
return prefs.MarketingEmails == nil || *prefs.MarketingEmails
|
||||
default:
|
||||
return true // unknown category -> allow
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user