mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-20 03:44:25 +00:00
feat: Add ProfileRepository interface with in-memory implementation for eSIM profile storage and CRUD operations
- Add Profile struct with ICCID/EID/IMSI/MCC/MNC/ProfileType/State/TenantID/ActivationCode/CreatedAt/UpdatedAt fields - Add ListFilter struct with TenantID/State/Limit/Offset fields for query filtering - Add ProfileRepository interface with Create/Get/List/UpdateState/Delete methods - Add InMemoryProfileStore with sync.RWMutex for concurrent-safe in-memory storage - Add NewInMemoryProfileStore constructor
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrNotFound is returned when a profile lookup misses.
|
||||
var ErrNotFound = errors.New("profile not found")
|
||||
|
||||
// Profile is the stored representation of an eSIM profile.
|
||||
type Profile struct {
|
||||
ICCID string `json:"iccid"`
|
||||
EID string `json:"eid,omitempty"`
|
||||
IMSI string `json:"imsi"`
|
||||
MCC string `json:"mcc"`
|
||||
MNC string `json:"mnc"`
|
||||
ProfileType string `json:"profileType"`
|
||||
State string `json:"state"` // e.g. provisioned, active, disabled, deleted
|
||||
TenantID string `json:"tenantId,omitempty"`
|
||||
ActivationCode string `json:"activationCode,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// ListFilter narrows the List query.
|
||||
type ListFilter struct {
|
||||
TenantID string
|
||||
State string
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
||||
// ProfileRepository is the storage contract for eSIM profiles.
|
||||
type ProfileRepository interface {
|
||||
Create(ctx context.Context, p *Profile) error
|
||||
Get(ctx context.Context, iccid string) (*Profile, error)
|
||||
List(ctx context.Context, f ListFilter) ([]*Profile, int, error)
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user