285 lines
7.3 KiB
Go
285 lines
7.3 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/rwadurian/mpc-system/services/account/domain/entities"
|
|
"github.com/rwadurian/mpc-system/services/account/domain/repositories"
|
|
"github.com/rwadurian/mpc-system/services/account/domain/value_objects"
|
|
)
|
|
|
|
// AccountSharePostgresRepo implements AccountShareRepository using PostgreSQL
|
|
type AccountSharePostgresRepo struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
// NewAccountSharePostgresRepo creates a new AccountSharePostgresRepo
|
|
func NewAccountSharePostgresRepo(db *sql.DB) repositories.AccountShareRepository {
|
|
return &AccountSharePostgresRepo{db: db}
|
|
}
|
|
|
|
// Create creates a new account share
|
|
func (r *AccountSharePostgresRepo) Create(ctx context.Context, share *entities.AccountShare) error {
|
|
query := `
|
|
INSERT INTO account_shares (id, account_id, share_type, party_id, party_index,
|
|
device_type, device_id, created_at, last_used_at, is_active)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
`
|
|
|
|
_, err := r.db.ExecContext(ctx, query,
|
|
share.ID,
|
|
share.AccountID.UUID(),
|
|
share.ShareType.String(),
|
|
share.PartyID,
|
|
share.PartyIndex,
|
|
share.DeviceType,
|
|
share.DeviceID,
|
|
share.CreatedAt,
|
|
share.LastUsedAt,
|
|
share.IsActive,
|
|
)
|
|
|
|
return err
|
|
}
|
|
|
|
// GetByID retrieves a share by ID
|
|
func (r *AccountSharePostgresRepo) GetByID(ctx context.Context, id string) (*entities.AccountShare, error) {
|
|
shareID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return nil, entities.ErrShareNotFound
|
|
}
|
|
|
|
query := `
|
|
SELECT id, account_id, share_type, party_id, party_index,
|
|
device_type, device_id, created_at, last_used_at, is_active
|
|
FROM account_shares
|
|
WHERE id = $1
|
|
`
|
|
|
|
return r.scanShare(r.db.QueryRowContext(ctx, query, shareID))
|
|
}
|
|
|
|
// GetByAccountID retrieves all shares for an account
|
|
func (r *AccountSharePostgresRepo) GetByAccountID(ctx context.Context, accountID value_objects.AccountID) ([]*entities.AccountShare, error) {
|
|
query := `
|
|
SELECT id, account_id, share_type, party_id, party_index,
|
|
device_type, device_id, created_at, last_used_at, is_active
|
|
FROM account_shares
|
|
WHERE account_id = $1
|
|
ORDER BY party_index
|
|
`
|
|
|
|
return r.queryShares(ctx, query, accountID.UUID())
|
|
}
|
|
|
|
// GetActiveByAccountID retrieves active shares for an account
|
|
func (r *AccountSharePostgresRepo) GetActiveByAccountID(ctx context.Context, accountID value_objects.AccountID) ([]*entities.AccountShare, error) {
|
|
query := `
|
|
SELECT id, account_id, share_type, party_id, party_index,
|
|
device_type, device_id, created_at, last_used_at, is_active
|
|
FROM account_shares
|
|
WHERE account_id = $1 AND is_active = TRUE
|
|
ORDER BY party_index
|
|
`
|
|
|
|
return r.queryShares(ctx, query, accountID.UUID())
|
|
}
|
|
|
|
// GetByPartyID retrieves shares by party ID
|
|
func (r *AccountSharePostgresRepo) GetByPartyID(ctx context.Context, partyID string) ([]*entities.AccountShare, error) {
|
|
query := `
|
|
SELECT id, account_id, share_type, party_id, party_index,
|
|
device_type, device_id, created_at, last_used_at, is_active
|
|
FROM account_shares
|
|
WHERE party_id = $1
|
|
ORDER BY created_at DESC
|
|
`
|
|
|
|
return r.queryShares(ctx, query, partyID)
|
|
}
|
|
|
|
// Update updates a share
|
|
func (r *AccountSharePostgresRepo) Update(ctx context.Context, share *entities.AccountShare) error {
|
|
query := `
|
|
UPDATE account_shares
|
|
SET share_type = $2, party_id = $3, party_index = $4,
|
|
device_type = $5, device_id = $6, last_used_at = $7, is_active = $8
|
|
WHERE id = $1
|
|
`
|
|
|
|
result, err := r.db.ExecContext(ctx, query,
|
|
share.ID,
|
|
share.ShareType.String(),
|
|
share.PartyID,
|
|
share.PartyIndex,
|
|
share.DeviceType,
|
|
share.DeviceID,
|
|
share.LastUsedAt,
|
|
share.IsActive,
|
|
)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return entities.ErrShareNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Delete deletes a share
|
|
func (r *AccountSharePostgresRepo) Delete(ctx context.Context, id string) error {
|
|
shareID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
return entities.ErrShareNotFound
|
|
}
|
|
|
|
query := `DELETE FROM account_shares WHERE id = $1`
|
|
|
|
result, err := r.db.ExecContext(ctx, query, shareID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return entities.ErrShareNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeactivateByAccountID deactivates all shares for an account
|
|
func (r *AccountSharePostgresRepo) DeactivateByAccountID(ctx context.Context, accountID value_objects.AccountID) error {
|
|
query := `UPDATE account_shares SET is_active = FALSE WHERE account_id = $1`
|
|
|
|
_, err := r.db.ExecContext(ctx, query, accountID.UUID())
|
|
return err
|
|
}
|
|
|
|
// DeactivateByShareType deactivates shares of a specific type for an account
|
|
func (r *AccountSharePostgresRepo) DeactivateByShareType(ctx context.Context, accountID value_objects.AccountID, shareType value_objects.ShareType) error {
|
|
query := `UPDATE account_shares SET is_active = FALSE WHERE account_id = $1 AND share_type = $2`
|
|
|
|
_, err := r.db.ExecContext(ctx, query, accountID.UUID(), shareType.String())
|
|
return err
|
|
}
|
|
|
|
// scanShare scans a single share row
|
|
func (r *AccountSharePostgresRepo) scanShare(row *sql.Row) (*entities.AccountShare, error) {
|
|
var (
|
|
id uuid.UUID
|
|
accountID uuid.UUID
|
|
shareType string
|
|
partyID string
|
|
partyIndex int
|
|
deviceType sql.NullString
|
|
deviceID sql.NullString
|
|
share entities.AccountShare
|
|
)
|
|
|
|
err := row.Scan(
|
|
&id,
|
|
&accountID,
|
|
&shareType,
|
|
&partyID,
|
|
&partyIndex,
|
|
&deviceType,
|
|
&deviceID,
|
|
&share.CreatedAt,
|
|
&share.LastUsedAt,
|
|
&share.IsActive,
|
|
)
|
|
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, entities.ErrShareNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
share.ID = id
|
|
share.AccountID = value_objects.AccountIDFromUUID(accountID)
|
|
share.ShareType = value_objects.ShareType(shareType)
|
|
share.PartyID = partyID
|
|
share.PartyIndex = partyIndex
|
|
if deviceType.Valid {
|
|
share.DeviceType = &deviceType.String
|
|
}
|
|
if deviceID.Valid {
|
|
share.DeviceID = &deviceID.String
|
|
}
|
|
|
|
return &share, nil
|
|
}
|
|
|
|
// queryShares queries multiple shares
|
|
func (r *AccountSharePostgresRepo) queryShares(ctx context.Context, query string, args ...interface{}) ([]*entities.AccountShare, error) {
|
|
rows, err := r.db.QueryContext(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var shares []*entities.AccountShare
|
|
for rows.Next() {
|
|
var (
|
|
id uuid.UUID
|
|
accountID uuid.UUID
|
|
shareType string
|
|
partyID string
|
|
partyIndex int
|
|
deviceType sql.NullString
|
|
deviceID sql.NullString
|
|
share entities.AccountShare
|
|
)
|
|
|
|
err := rows.Scan(
|
|
&id,
|
|
&accountID,
|
|
&shareType,
|
|
&partyID,
|
|
&partyIndex,
|
|
&deviceType,
|
|
&deviceID,
|
|
&share.CreatedAt,
|
|
&share.LastUsedAt,
|
|
&share.IsActive,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
share.ID = id
|
|
share.AccountID = value_objects.AccountIDFromUUID(accountID)
|
|
share.ShareType = value_objects.ShareType(shareType)
|
|
share.PartyID = partyID
|
|
share.PartyIndex = partyIndex
|
|
if deviceType.Valid {
|
|
share.DeviceType = &deviceType.String
|
|
}
|
|
if deviceID.Valid {
|
|
share.DeviceID = &deviceID.String
|
|
}
|
|
|
|
shares = append(shares, &share)
|
|
}
|
|
|
|
return shares, rows.Err()
|
|
}
|