Skip to content

Commit

Permalink
Added FecthAllByDomainId, fixed handler sync check (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
petruki authored Sep 15, 2024
1 parent 0133ccf commit e17d4d8
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 36 deletions.
14 changes: 14 additions & 0 deletions src/controller/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func NewAccountController(repo repository.AccountRepository, coreHandler *core.C
func (controller *AccountController) RegisterRoutes(r *mux.Router) http.Handler {
r.NewRoute().Path(controller.RouteAccountPath).Name("CreateAccount").HandlerFunc(controller.CreateAccountHandler).Methods(http.MethodPost)
r.NewRoute().Path(controller.RouteAccountPath).Name("UpdateAccount").HandlerFunc(controller.UpdateAccountHandler).Methods(http.MethodPut)
r.NewRoute().Path(controller.RouteAccountPath + "/{domainId}").Name("GelAllAccountsByDomainId").HandlerFunc(controller.FetchAllAccountsByDomainIdHandler).Methods(http.MethodGet)
r.NewRoute().Path(controller.RouteAccountPath + "/{domainId}/{enviroment}").Name("GetAccount").HandlerFunc(controller.FetchAccountHandler).Methods(http.MethodGet)
r.NewRoute().Path(controller.RouteAccountPath + "/{domainId}/{enviroment}").Name("DeleteAccount").HandlerFunc(controller.DeleteAccountHandler).Methods(http.MethodDelete)

Expand Down Expand Up @@ -80,6 +81,19 @@ func (controller *AccountController) FetchAccountHandler(w http.ResponseWriter,
utils.ResponseJSON(w, account, http.StatusOK)
}

func (controller *AccountController) FetchAllAccountsByDomainIdHandler(w http.ResponseWriter, r *http.Request) {
domainId := mux.Vars(r)["domainId"]

accounts := controller.AccountRepository.FetchAllByDomainId(domainId)
if accounts == nil {
utils.Log(utils.LogLevelError, "Not found accounts for domain: %s", domainId)
utils.ResponseJSON(w, ErrorResponse{Error: "Not found accounts for domain: " + domainId}, http.StatusNotFound)
return
}

utils.ResponseJSON(w, accounts, http.StatusOK)
}

func (controller *AccountController) UpdateAccountHandler(w http.ResponseWriter, r *http.Request) {
var accountRequest model.Account
err := json.NewDecoder(r.Body).Decode(&accountRequest)
Expand Down
40 changes: 38 additions & 2 deletions src/controller/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,53 @@ func TestFetchAccountHandler(t *testing.T) {
assert.Equal(t, http.StatusNotFound, response.Code)
assert.Equal(t, "{\"error\":\"Account not found\"}", response.Body.String())
})

t.Run("Should fetch all accounts by domain ID", func(t *testing.T) {
// Create an account
accountV1.Domain.ID = "123-controller-fetch-all-accounts"
accountController.CreateAccountHandler(givenAccountRequest(accountV1))
accountV1.Environment = "staging"
accountController.CreateAccountHandler(givenAccountRequest(accountV1))

// Test
payload := []byte("")
req, _ := http.NewRequest(http.MethodGet, accountController.RouteAccountPath+"/"+accountV1.Domain.ID, bytes.NewBuffer(payload))
response := executeRequest(req)

// Assert
var accountsResponse []model.Account
err := json.NewDecoder(response.Body).Decode(&accountsResponse)

assert.Equal(t, http.StatusOK, response.Code)
assert.Nil(t, err)
assert.Equal(t, 2, len(accountsResponse))
})

t.Run("Should not fetch all accounts by domain ID - not found", func(t *testing.T) {
// Test
payload := []byte("")
req, _ := http.NewRequest(http.MethodGet, accountController.RouteAccountPath+"/not-found", bytes.NewBuffer(payload))
response := executeRequest(req)

// Assert
assert.Equal(t, http.StatusNotFound, response.Code)
assert.Equal(t, "{\"error\":\"Not found accounts for domain: not-found\"}", response.Body.String())
})
}

func TestUpdateAccountHandler(t *testing.T) {
t.Run("Should update an account", func(t *testing.T) {
// Create an account
accountV1.Domain.ID = "123-controller-update-account"
accountV1.Environment = "default"
accountController.CreateAccountHandler(givenAccountRequest(accountV1))

// Test
accountV2.Domain.ID = accountV1.Domain.ID
// Update the account
accountV2.Domain.ID = "123-controller-update-account"
accountV2.Environment = "default"
accountV2.Domain.Message = "Updated successfully"

// Test
payload, _ := json.Marshal(accountV2)
req, _ := http.NewRequest(http.MethodPut, accountController.RouteAccountPath, bytes.NewBuffer(payload))
response := executeRequest(req)
Expand Down
47 changes: 29 additions & 18 deletions src/core/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func NewCoreHandler(accountRepository repository.AccountRepository, apiService I
}
}

func (c *CoreHandler) InitCoreHandlerCoroutine() (int, error) {
func (c *CoreHandler) InitCoreHandlerGoroutine() (int, error) {
// Check if core handler is already running
if c.Status == CoreHandlerStatusRunning {
return c.Status, nil
Expand All @@ -39,7 +39,7 @@ func (c *CoreHandler) InitCoreHandlerCoroutine() (int, error) {
c.Status = CoreHandlerStatusInit

// Load all accounts
accounts, _ := c.AccountRepository.FetchAllActiveAccounts()
accounts := c.AccountRepository.FetchAllActiveAccounts()

// Iterate over accounts and start account handlers
for _, account := range accounts {
Expand Down Expand Up @@ -70,7 +70,9 @@ func (c *CoreHandler) StartAccountHandler(accountId string, gitService IGitServi

// Wait for account to be active
if !account.Settings.Active {
utils.Log(utils.LogLevelInfo, "[%s - %s] Account is not active, waiting for activation", accountId, account.Domain.Name)
utils.Log(utils.LogLevelInfo, "[%s - %s (%s)] Account is not active, waiting for activation",
accountId, account.Domain.Name, account.Environment)

c.updateDomainStatus(*account, model.StatusPending, "Account was deactivated")
time.Sleep(1 * time.Minute)
continue
Expand All @@ -83,7 +85,9 @@ func (c *CoreHandler) StartAccountHandler(accountId string, gitService IGitServi
repositoryData, err := gitService.GetRepositoryData(account.Environment)

if err != nil {
utils.Log(utils.LogLevelError, "[%s - %s] Failed to fetch repository data - %s", accountId, account.Domain.Name, err.Error())
utils.Log(utils.LogLevelError, "[%s - %s (%s)] Failed to fetch repository data - %s",
accountId, account.Domain.Name, account.Environment, err.Error())

c.updateDomainStatus(*account, model.StatusError, "Failed to fetch repository data - "+err.Error())
time.Sleep(1 * time.Minute)
continue
Expand All @@ -93,7 +97,9 @@ func (c *CoreHandler) StartAccountHandler(accountId string, gitService IGitServi
snapshotVersionPayload, err := c.ApiService.FetchSnapshotVersion(account.Domain.ID, account.Environment)

if err != nil {
utils.Log(utils.LogLevelError, "[%s - %s] Failed to fetch snapshot version - %s", accountId, account.Domain.Name, err.Error())
utils.Log(utils.LogLevelError, "[%s - %s (%s)] Failed to fetch snapshot version - %s",
accountId, account.Domain.Name, account.Environment, err.Error())

c.updateDomainStatus(*account, model.StatusError, "Failed to fetch snapshot version - "+err.Error())
time.Sleep(1 * time.Minute)
continue
Expand All @@ -111,9 +117,11 @@ func (c *CoreHandler) StartAccountHandler(accountId string, gitService IGitServi
}

func (c *CoreHandler) syncUp(account model.Account, repositoryData *model.RepositoryData, gitService IGitService) {
utils.Log(utils.LogLevelInfo, "[%s - %s] Syncing up", account.ID.Hex(), account.Domain.Name)
utils.Log(utils.LogLevelInfo, "[%s - %s (%s)] Syncing up", account.ID.Hex(), account.Domain.Name, account.Environment)

// Update account status: Out of sync
account.Domain.LastCommit = repositoryData.CommitHash
account.Domain.LastDate = repositoryData.CommitDate
c.updateDomainStatus(account, model.StatusOutSync, model.MessageSyncingUp)

// Check for changes
Expand All @@ -124,17 +132,19 @@ func (c *CoreHandler) syncUp(account model.Account, repositoryData *model.Reposi
return
}

utils.Log(utils.LogLevelDebug, "[%s - %s] SnapshotAPI version: %s - SnapshotRepo version: %s",
account.ID.Hex(), account.Domain.Name, fmt.Sprint(snapshotApi.Domain.Version), fmt.Sprint(account.Domain.Version))
utils.Log(utils.LogLevelDebug, "[%s - %s (%s)] SnapshotAPI version: %s - SnapshotRepo version: %s",
account.ID.Hex(), account.Domain.Name, account.Environment, fmt.Sprint(snapshotApi.Domain.Version), fmt.Sprint(account.Domain.Version))

// Apply changes
changeSource := ""
if snapshotApi.Domain.Version > account.Domain.Version {
changeSource = "Repository"
if c.isRepositoryOutSync(account, repositoryData, diff) {
if c.isRepositoryOutSync(repositoryData, diff) {
account, err = c.applyChangesToRepository(account, snapshotApi, gitService)
} else {
utils.Log(utils.LogLevelInfo, "[%s - %s] Repository is up to date", account.ID.Hex(), account.Domain.Name)
utils.Log(utils.LogLevelInfo, "[%s - %s (%s)] Repository is up to date",
account.ID.Hex(), account.Domain.Name, account.Environment)

account.Domain.Version = snapshotApi.Domain.Version
account.Domain.LastCommit = repositoryData.CommitHash
}
Expand All @@ -144,7 +154,9 @@ func (c *CoreHandler) syncUp(account model.Account, repositoryData *model.Reposi
}

if err != nil {
utils.Log(utils.LogLevelError, "[%s - %s] Failed to apply changes [%s] - %s", account.ID.Hex(), account.Domain.Name, changeSource, err.Error())
utils.Log(utils.LogLevelError, "[%s - %s (%s)] Failed to apply changes [%s] - %s",
account.ID.Hex(), account.Domain.Name, account.Environment, changeSource, err.Error())

c.updateDomainStatus(account, model.StatusError, "Failed to apply changes ["+changeSource+"] - "+err.Error())
return
}
Expand Down Expand Up @@ -178,7 +190,7 @@ func (c *CoreHandler) checkForChanges(account model.Account, content string) (mo
}

func (c *CoreHandler) applyChangesToAPI(account model.Account, repositoryData *model.RepositoryData, diff model.DiffResult) model.Account {
utils.Log(utils.LogLevelInfo, "[%s - %s] Pushing changes to API", account.ID.Hex(), account.Domain.Name)
utils.Log(utils.LogLevelInfo, "[%s - %s (%s)] Pushing changes to API", account.ID.Hex(), account.Domain.Name, account.Environment)

// Removed deleted if force prune is disabled
if !account.Settings.ForcePrune {
Expand All @@ -195,7 +207,7 @@ func (c *CoreHandler) applyChangesToAPI(account model.Account, repositoryData *m
}

func (c *CoreHandler) applyChangesToRepository(account model.Account, snapshot model.Snapshot, gitService IGitService) (model.Account, error) {
utils.Log(utils.LogLevelInfo, "[%s - %s] Pushing changes to repository", account.ID.Hex(), account.Domain.Name)
utils.Log(utils.LogLevelInfo, "[%s - %s (%s)] Pushing changes to repository", account.ID.Hex(), account.Domain.Name, account.Environment)

// Remove version from domain
snapshotContent := snapshot
Expand All @@ -217,17 +229,16 @@ func (c *CoreHandler) applyChangesToRepository(account model.Account, snapshot m
func (c *CoreHandler) isOutSync(account model.Account, lastCommit string, snapshotVersionPayload string) bool {
snapshotVersion := c.ApiService.NewDataFromJson([]byte(snapshotVersionPayload)).Snapshot.Domain.Version

utils.Log(utils.LogLevelDebug, "[%s - %s] Checking account - Last commit: %s - Domain Version: %d - Snapshot Version: %d",
account.ID.Hex(), account.Domain.Name, account.Domain.LastCommit, account.Domain.Version, snapshotVersion)
utils.Log(utils.LogLevelDebug, "[%s - %s (%s)] Checking account - Last commit: %s - Domain Version: %d - Snapshot Version: %d",
account.ID.Hex(), account.Domain.Name, account.Environment, account.Domain.LastCommit, account.Domain.Version, snapshotVersion)

return account.Domain.LastCommit == "" || // First sync
account.Domain.LastCommit != lastCommit || // Repository out of sync
account.Domain.Version != snapshotVersion // API out of sync
}

func (c *CoreHandler) isRepositoryOutSync(account model.Account, repositoryData *model.RepositoryData, diff model.DiffResult) bool {
return account.Domain.Version == 0 || // First/Force-push sync
len(repositoryData.Content) <= 1 || // File is empty
func (c *CoreHandler) isRepositoryOutSync(repositoryData *model.RepositoryData, diff model.DiffResult) bool {
return len(repositoryData.Content) <= 1 || // File is empty
len(diff.Changes) > 0 // Changes detected
}

Expand Down
13 changes: 6 additions & 7 deletions src/core/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/switcherapi/switcher-gitops/src/model"
)

func TestInitCoreHandlerCoroutine(t *testing.T) {
func TestInitCoreHandlerGoroutine(t *testing.T) {
t.Run("Should start account handlers for all active accounts", func(t *testing.T) {
// Given
fakeApiService := NewFakeApiService()
Expand All @@ -23,7 +23,7 @@ func TestInitCoreHandlerCoroutine(t *testing.T) {
accountCreated, _ := coreHandler.AccountRepository.Create(&account)

// Test
status, err := coreHandler.InitCoreHandlerCoroutine()
status, err := coreHandler.InitCoreHandlerGoroutine()

// Terminate the goroutine
coreHandler.AccountRepository.DeleteByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment)
Expand All @@ -46,8 +46,8 @@ func TestInitCoreHandlerCoroutine(t *testing.T) {
accountCreated, _ := coreHandler.AccountRepository.Create(&account)

// Test
coreHandler.InitCoreHandlerCoroutine()
status, _ := coreHandler.InitCoreHandlerCoroutine()
coreHandler.InitCoreHandlerGoroutine()
status, _ := coreHandler.InitCoreHandlerGoroutine()

// Terminate the goroutine
coreHandler.AccountRepository.DeleteByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment)
Expand Down Expand Up @@ -211,7 +211,6 @@ func TestStartAccountHandler(t *testing.T) {

account := givenAccount()
account.Domain.ID = "123-up-to-date-not-synced"
account.Domain.Version = -1 // Different from the API version
accountCreated, _ := coreHandler.AccountRepository.Create(&account)

// Test
Expand Down Expand Up @@ -350,7 +349,7 @@ func TestStartAccountHandler(t *testing.T) {
accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment)
assert.Equal(t, model.StatusError, accountFromDb.Domain.Status)
assert.Contains(t, accountFromDb.Domain.Message, "Failed to check for changes")
assert.Equal(t, "", accountFromDb.Domain.LastCommit)
assert.Equal(t, "123", accountFromDb.Domain.LastCommit)
assert.NotEqual(t, "", accountFromDb.Domain.LastDate)

tearDown()
Expand Down Expand Up @@ -379,7 +378,7 @@ func TestStartAccountHandler(t *testing.T) {
assert.Equal(t, model.StatusError, accountFromDb.Domain.Status)
assert.Contains(t, accountFromDb.Domain.Message, "authorization failed")
assert.Contains(t, accountFromDb.Domain.Message, "Failed to apply changes [Repository]")
assert.Equal(t, "", accountFromDb.Domain.LastCommit)
assert.Equal(t, "123", accountFromDb.Domain.LastCommit)
assert.NotEqual(t, "", accountFromDb.Domain.LastDate)

tearDown()
Expand Down
28 changes: 24 additions & 4 deletions src/repository/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ type AccountRepository interface {
Create(account *model.Account) (*model.Account, error)
FetchByAccountId(accountId string) (*model.Account, error)
FetchByDomainIdEnvironment(domainId string, environment string) (*model.Account, error)
FetchAllActiveAccounts() ([]model.Account, error)
FetchAllByDomainId(domainId string) []model.Account
FetchAllActiveAccounts() []model.Account
Update(account *model.Account) (*model.Account, error)
DeleteByAccountId(accountId string) error
DeleteByDomainIdEnvironment(domainId string, environment string) error
Expand Down Expand Up @@ -69,7 +70,7 @@ func (repo *AccountRepositoryMongo) FetchByDomainIdEnvironment(domainId string,
defer cancel()

var account model.Account
filter := primitive.M{domainIdFilter: domainId}
filter := primitive.M{domainIdFilter: domainId, environmentFilter: environment}
err := collection.FindOne(ctx, filter).Decode(&account)
if err != nil {
return nil, err
Expand All @@ -78,7 +79,26 @@ func (repo *AccountRepositoryMongo) FetchByDomainIdEnvironment(domainId string,
return &account, nil
}

func (repo *AccountRepositoryMongo) FetchAllActiveAccounts() ([]model.Account, error) {
func (repo *AccountRepositoryMongo) FetchAllByDomainId(domainId string) []model.Account {
collection, ctx, cancel := getDbContext(repo)
defer cancel()

filter := primitive.M{domainIdFilter: domainId}
cursor, _ := collection.Find(ctx, filter)

var accounts []model.Account
for cursor.Next(ctx) {
var account model.Account
err := cursor.Decode(&account)
if err == nil {
accounts = append(accounts, account)
}
}

return accounts
}

func (repo *AccountRepositoryMongo) FetchAllActiveAccounts() []model.Account {
collection, ctx, cancel := getDbContext(repo)
defer cancel()

Expand All @@ -94,7 +114,7 @@ func (repo *AccountRepositoryMongo) FetchAllActiveAccounts() ([]model.Account, e
}
}

return accounts, nil
return accounts
}

func (repo *AccountRepositoryMongo) Update(account *model.Account) (*model.Account, error) {
Expand Down
25 changes: 23 additions & 2 deletions src/repository/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,33 @@ func TestFetchAccount(t *testing.T) {
accountRepository.Create(&account2)

// Test
accounts, err := accountRepository.FetchAllActiveAccounts()
accounts := accountRepository.FetchAllActiveAccounts()

// Assert
assert.Nil(t, err)
assert.NotNil(t, accounts)
assert.Equal(t, 1, len(accounts))
})

t.Run("Should fetch all accounts by domain ID", func(t *testing.T) {
// Drop collection
mongoDb.Collection("accounts").Drop(context.Background())

// Given
account1 := givenAccount(true)
account1.Domain.ID = "123-fetch-all-accounts-by-domain-id"
account2 := givenAccount(true)
account2.Domain.ID = "123-fetch-all-accounts-by-domain-id"

accountRepository.Create(&account1)
accountRepository.Create(&account2)

// Test
accounts := accountRepository.FetchAllByDomainId(account1.Domain.ID)

// Assert
assert.NotNil(t, accounts)
assert.Equal(t, 2, len(accounts))
})
}

func TestUpdateAccount(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion src/server/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func initCoreHandler(db *mongo.Database) *core.CoreHandler {
)

coreHandler := core.NewCoreHandler(accountRepository, apiService, comparatorService)
coreHandler.InitCoreHandlerCoroutine()
coreHandler.InitCoreHandlerGoroutine()

return coreHandler
}
3 changes: 1 addition & 2 deletions src/utils/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package utils

import (
"encoding/json"
"fmt"
"net/http"
)

Expand All @@ -12,7 +11,7 @@ func ResponseJSON(w http.ResponseWriter, data interface{}, status int) {

encodedData, err := json.Marshal(data)
if err != nil {
fmt.Println("Error encoding JSON:", err)
Log(LogLevelError, "Error encoding JSON: %s", err.Error())
return
}

Expand Down

0 comments on commit e17d4d8

Please sign in to comment.