aboutsummaryrefslogtreecommitdiff
path: root/hotline/account_manager.go
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2024-07-09 21:36:27 -0700
committerJeff Halter <868228+jhalter@users.noreply.github.com>2024-07-09 21:42:05 -0700
commitd9bc63a10d0978d9a5222cf7be74044e55f409b7 (patch)
tree1797c9593c279bf1334ed8285de8054a92a28dcb /hotline/account_manager.go
parent8fa166777cbcd92e871e937d9557f0f1a732c04d (diff)
Extensive refactor and clean up
Diffstat (limited to 'hotline/account_manager.go')
-rw-r--r--hotline/account_manager.go188
1 files changed, 188 insertions, 0 deletions
diff --git a/hotline/account_manager.go b/hotline/account_manager.go
new file mode 100644
index 0000000..769d12c
--- /dev/null
+++ b/hotline/account_manager.go
@@ -0,0 +1,188 @@
+package hotline
+
+import (
+ "fmt"
+ "github.com/stretchr/testify/mock"
+ "gopkg.in/yaml.v3"
+ "os"
+ "path"
+ "path/filepath"
+ "sync"
+)
+
+type AccountManager interface {
+ Create(account Account) error
+ Update(account Account, newLogin string) error
+ Get(login string) *Account
+ List() []Account
+ Delete(login string) error
+}
+
+type YAMLAccountManager struct {
+ accounts map[string]Account
+ accountDir string
+
+ mu sync.Mutex
+}
+
+func NewYAMLAccountManager(accountDir string) (*YAMLAccountManager, error) {
+ accountMgr := YAMLAccountManager{
+ accountDir: accountDir,
+ accounts: make(map[string]Account),
+ }
+
+ matches, err := filepath.Glob(filepath.Join(accountDir, "*.yaml"))
+ if err != nil {
+ return nil, err
+ }
+
+ if len(matches) == 0 {
+ return nil, fmt.Errorf("no accounts found in directory: %s", accountDir)
+ }
+
+ for _, file := range matches {
+ var account Account
+ if err = loadFromYAMLFile(file, &account); err != nil {
+ return nil, fmt.Errorf("error loading account %s: %w", file, err)
+ }
+
+ accountMgr.accounts[account.Login] = account
+ }
+
+ return &accountMgr, nil
+}
+
+func (am *YAMLAccountManager) Create(account Account) error {
+ am.mu.Lock()
+ defer am.mu.Unlock()
+
+ // Create account file, returning an error if one already exists.
+ file, err := os.OpenFile(
+ filepath.Join(am.accountDir, path.Join("/", account.Login+".yaml")),
+ os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644,
+ )
+ if err != nil {
+ return fmt.Errorf("error creating account file: %w", err)
+ }
+ defer file.Close()
+
+ b, err := yaml.Marshal(account)
+ if err != nil {
+ return fmt.Errorf("marshal account to YAML: %v", err)
+ }
+
+ _, err = file.Write(b)
+ if err != nil {
+ return fmt.Errorf("write account file: %w", err)
+ }
+
+ am.accounts[account.Login] = account
+
+ return nil
+}
+
+func (am *YAMLAccountManager) Update(account Account, newLogin string) error {
+ am.mu.Lock()
+ defer am.mu.Unlock()
+
+ // If the login has changed, rename the account file.
+ if account.Login != newLogin {
+ err := os.Rename(
+ filepath.Join(am.accountDir, path.Join("/", account.Login)+".yaml"),
+ filepath.Join(am.accountDir, path.Join("/", newLogin)+".yaml"),
+ )
+ if err != nil {
+ return fmt.Errorf("error renaming account file: %w", err)
+ }
+
+ account.Login = newLogin
+ am.accounts[newLogin] = account
+
+ delete(am.accounts, account.Login)
+ }
+
+ out, err := yaml.Marshal(&account)
+ if err != nil {
+ return err
+ }
+
+ if err := os.WriteFile(filepath.Join(am.accountDir, newLogin+".yaml"), out, 0644); err != nil {
+ return fmt.Errorf("error writing account file: %w", err)
+ }
+
+ am.accounts[account.Login] = account
+
+ return nil
+}
+
+func (am *YAMLAccountManager) Get(login string) *Account {
+ am.mu.Lock()
+ defer am.mu.Unlock()
+
+ account, ok := am.accounts[login]
+ if !ok {
+ return nil
+ }
+
+ return &account
+}
+
+func (am *YAMLAccountManager) List() []Account {
+ am.mu.Lock()
+ defer am.mu.Unlock()
+
+ var accounts []Account
+ for _, account := range am.accounts {
+ accounts = append(accounts, account)
+ }
+
+ return accounts
+}
+
+func (am *YAMLAccountManager) Delete(login string) error {
+ am.mu.Lock()
+ defer am.mu.Unlock()
+
+ err := os.Remove(filepath.Join(am.accountDir, path.Join("/", login+".yaml")))
+ if err != nil {
+ return fmt.Errorf("delete account file: %v", err)
+ }
+
+ delete(am.accounts, login)
+
+ return nil
+}
+
+type MockAccountManager struct {
+ mock.Mock
+}
+
+func (m *MockAccountManager) Create(account Account) error {
+ args := m.Called(account)
+
+ return args.Error(0)
+}
+
+func (m *MockAccountManager) Update(account Account, newLogin string) error {
+ args := m.Called(account, newLogin)
+
+ return args.Error(0)
+}
+
+func (m *MockAccountManager) Get(login string) *Account {
+ args := m.Called(login)
+
+ return args.Get(0).(*Account)
+}
+
+func (m *MockAccountManager) List() []Account {
+ args := m.Called()
+
+ return args.Get(0).([]Account)
+}
+
+func (m *MockAccountManager) Delete(login string) error {
+ args := m.Called(login)
+
+ return args.Error(0)
+}