From d27eb871e3af1094663b9190690aad92246549bd Mon Sep 17 00:00:00 2001 From: nutcas3 Date: Fri, 24 Apr 2026 08:59:44 +0300 Subject: [PATCH] feat: Add DatabaseConfig to carrier-connector config and remove InMemoryProfileStore implementation for Postgres migration - Add DatabaseConfig struct with DSN field for Postgres connection string - Add package documentation comments for ES2Config/DatabaseConfig/LoadES2Config - Normalize ES2Config field alignment for BaseURL/APIKey/InsecureSkipVerify/FunctionalityRequesterID - Remove InMemoryProfileStore struct and all methods (Create/Get/List/UpdateState/Delete) from profile.go - Remove sync package import --- .../internal/config/config.go | 19 +++- .../internal/repository/profile.go | 103 +----------------- 2 files changed, 17 insertions(+), 105 deletions(-) diff --git a/apps/carrier-connector/internal/config/config.go b/apps/carrier-connector/internal/config/config.go index d9184c7..d1685b3 100644 --- a/apps/carrier-connector/internal/config/config.go +++ b/apps/carrier-connector/internal/config/config.go @@ -1,17 +1,24 @@ package config +// ES2Config configures the upstream SM-DP+ ES2+ connection. type ES2Config struct { - BaseURL string `json:"base_url"` - APIKey string `json:"api_key"` - InsecureSkipVerify bool `json:"insecure_skip_verify"` + BaseURL string `json:"base_url"` + APIKey string `json:"api_key"` + InsecureSkipVerify bool `json:"insecure_skip_verify"` FunctionalityRequesterID string `json:"functionality_requester_id"` } +// DatabaseConfig configures the Postgres connection for the profile repository. +type DatabaseConfig struct { + DSN string `json:"dsn"` // e.g. "host=localhost user=telecom password=... dbname=carrier port=5432 sslmode=disable" +} + +// LoadES2Config returns default ES2+ configuration (override via env in main). func LoadES2Config() *ES2Config { return &ES2Config{ - BaseURL: "https://smdp.example.com", - APIKey: "", - InsecureSkipVerify: true, + BaseURL: "https://smdp.example.com", + APIKey: "", + InsecureSkipVerify: true, FunctionalityRequesterID: "telecom-platform", } } diff --git a/apps/carrier-connector/internal/repository/profile.go b/apps/carrier-connector/internal/repository/profile.go index abf1a46..698706a 100644 --- a/apps/carrier-connector/internal/repository/profile.go +++ b/apps/carrier-connector/internal/repository/profile.go @@ -1,10 +1,9 @@ package repository import ( - "context" - "errors" - "sync" - "time" +"context" +"errors" +"time" ) // ErrNotFound is returned when a profile lookup misses. @@ -18,7 +17,7 @@ type Profile struct { MCC string `json:"mcc"` MNC string `json:"mnc"` ProfileType string `json:"profileType"` - State string `json:"state"` // e.g. provisioned, active, disabled, deleted + State string `json:"state"` TenantID string `json:"tenantId,omitempty"` ActivationCode string `json:"activationCode,omitempty"` CreatedAt time.Time `json:"createdAt"` @@ -41,97 +40,3 @@ type ProfileRepository interface { UpdateState(ctx context.Context, iccid, state string) (*Profile, error) Delete(ctx context.Context, iccid string) error } - -// InMemoryProfileStore is an in-memory ProfileRepository. -// Safe for concurrent use. Intended as a development default; production -// deployments should swap in a Postgres-backed implementation. -type InMemoryProfileStore struct { - mu sync.RWMutex - profiles map[string]*Profile -} - -// NewInMemoryProfileStore constructs an empty InMemoryProfileStore. -func NewInMemoryProfileStore() *InMemoryProfileStore { - return &InMemoryProfileStore{profiles: make(map[string]*Profile)} -} - -// Create inserts a profile. Returns an error if the ICCID already exists. -func (s *InMemoryProfileStore) Create(_ context.Context, p *Profile) error { - s.mu.Lock() - defer s.mu.Unlock() - if _, exists := s.profiles[p.ICCID]; exists { - return errors.New("profile already exists") - } - now := time.Now().UTC() - p.CreatedAt = now - p.UpdatedAt = now - cp := *p - s.profiles[p.ICCID] = &cp - return nil -} - -// Get returns a profile by ICCID or ErrNotFound. -func (s *InMemoryProfileStore) Get(_ context.Context, iccid string) (*Profile, error) { - s.mu.RLock() - defer s.mu.RUnlock() - p, ok := s.profiles[iccid] - if !ok { - return nil, ErrNotFound - } - cp := *p - return &cp, nil -} - -// List returns a filtered, paginated snapshot of profiles. -func (s *InMemoryProfileStore) List(_ context.Context, f ListFilter) ([]*Profile, int, error) { - s.mu.RLock() - defer s.mu.RUnlock() - - matched := make([]*Profile, 0, len(s.profiles)) - for _, p := range s.profiles { - if f.TenantID != "" && p.TenantID != f.TenantID { - continue - } - if f.State != "" && p.State != f.State { - continue - } - cp := *p - matched = append(matched, &cp) - } - - total := len(matched) - start := f.Offset - if start > total { - start = total - } - end := total - if f.Limit > 0 && start+f.Limit < total { - end = start + f.Limit - } - return matched[start:end], total, nil -} - -// UpdateState updates a profile's state and UpdatedAt timestamp. -func (s *InMemoryProfileStore) UpdateState(_ context.Context, iccid, state string) (*Profile, error) { - s.mu.Lock() - defer s.mu.Unlock() - p, ok := s.profiles[iccid] - if !ok { - return nil, ErrNotFound - } - p.State = state - p.UpdatedAt = time.Now().UTC() - cp := *p - return &cp, nil -} - -// Delete removes a profile. -func (s *InMemoryProfileStore) Delete(_ context.Context, iccid string) error { - s.mu.Lock() - defer s.mu.Unlock() - if _, ok := s.profiles[iccid]; !ok { - return ErrNotFound - } - delete(s.profiles, iccid) - return nil -}