diff --git a/.env.test b/.env.test index feb5e1a..b17dc9f 100644 --- a/.env.test +++ b/.env.test @@ -3,6 +3,7 @@ LOG_LEVEL=DEBUG MONGO_URI=mongodb://localhost:27017 MONGO_DB=switcher-gitops-test GIT_TOKEN_PRIVATE_KEY=SecretSecretSecretSecretSecretSe +HANDLER_WAITING_TIME=1m SWITCHER_API_URL=https://switcherapi.com/api SWITCHER_API_JWT_SECRET=SecretSecretSecretSecretSecretSe diff --git a/src/controller/account.go b/src/controller/account.go index d5c85f1..a3c4660 100644 --- a/src/controller/account.go +++ b/src/controller/account.go @@ -13,9 +13,9 @@ import ( ) type AccountController struct { - CoreHandler *core.CoreHandler - AccountRepository repository.AccountRepository - RouteAccountPath string + coreHandler *core.CoreHandler + accountRepository repository.AccountRepository + routeAccountPath string } type ErrorResponse struct { @@ -24,18 +24,18 @@ type ErrorResponse struct { func NewAccountController(repo repository.AccountRepository, coreHandler *core.CoreHandler) *AccountController { return &AccountController{ - CoreHandler: coreHandler, - AccountRepository: repo, - RouteAccountPath: "/account", + coreHandler: coreHandler, + accountRepository: repo, + routeAccountPath: "/account", } } 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) + 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) return r } @@ -53,7 +53,7 @@ func (controller *AccountController) CreateAccountHandler(w http.ResponseWriter, accountRequest.Token = utils.Encrypt(accountRequest.Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY")) } - accountCreated, err := controller.AccountRepository.Create(&accountRequest) + accountCreated, err := controller.accountRepository.Create(&accountRequest) if err != nil { utils.Log(utils.LogLevelError, "Error creating account: %s", err.Error()) utils.ResponseJSON(w, ErrorResponse{Error: "Error creating account"}, http.StatusInternalServerError) @@ -62,7 +62,7 @@ func (controller *AccountController) CreateAccountHandler(w http.ResponseWriter, // Initialize account handler gitService := core.NewGitService(accountCreated.Repository, accountCreated.Token, accountCreated.Branch) - go controller.CoreHandler.StartAccountHandler(accountCreated.ID.Hex(), gitService) + go controller.coreHandler.StartAccountHandler(accountCreated.ID.Hex(), gitService) utils.ResponseJSON(w, accountCreated, http.StatusCreated) } @@ -71,7 +71,7 @@ func (controller *AccountController) FetchAccountHandler(w http.ResponseWriter, domainId := mux.Vars(r)["domainId"] enviroment := mux.Vars(r)["enviroment"] - account, err := controller.AccountRepository.FetchByDomainIdEnvironment(domainId, enviroment) + account, err := controller.accountRepository.FetchByDomainIdEnvironment(domainId, enviroment) if err != nil { utils.Log(utils.LogLevelError, "Error fetching account: %s", err.Error()) utils.ResponseJSON(w, ErrorResponse{Error: "Account not found"}, http.StatusNotFound) @@ -84,7 +84,7 @@ func (controller *AccountController) FetchAccountHandler(w http.ResponseWriter, func (controller *AccountController) FetchAllAccountsByDomainIdHandler(w http.ResponseWriter, r *http.Request) { domainId := mux.Vars(r)["domainId"] - accounts := controller.AccountRepository.FetchAllByDomainId(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) @@ -108,7 +108,7 @@ func (controller *AccountController) UpdateAccountHandler(w http.ResponseWriter, accountRequest.Token = utils.Encrypt(accountRequest.Token, config.GetEnv("GIT_TOKEN_PRIVATE_KEY")) } - accountUpdated, err := controller.AccountRepository.Update(&accountRequest) + accountUpdated, err := controller.accountRepository.Update(&accountRequest) if err != nil { utils.Log(utils.LogLevelError, "Error updating account: %s", err.Error()) utils.ResponseJSON(w, ErrorResponse{Error: "Error updating account"}, http.StatusInternalServerError) @@ -122,7 +122,7 @@ func (controller *AccountController) DeleteAccountHandler(w http.ResponseWriter, domainId := mux.Vars(r)["domainId"] enviroment := mux.Vars(r)["enviroment"] - err := controller.AccountRepository.DeleteByDomainIdEnvironment(domainId, enviroment) + err := controller.accountRepository.DeleteByDomainIdEnvironment(domainId, enviroment) if err != nil { utils.Log(utils.LogLevelError, "Error deleting account: %s", err.Error()) utils.ResponseJSON(w, ErrorResponse{Error: "Error deleting account: " + err.Error()}, http.StatusInternalServerError) diff --git a/src/controller/account_test.go b/src/controller/account_test.go index 9e0f413..84547e7 100644 --- a/src/controller/account_test.go +++ b/src/controller/account_test.go @@ -19,7 +19,7 @@ func TestCreateAccountHandler(t *testing.T) { // Test accountV1.Domain.ID = "123-controller-create-account" payload, _ := json.Marshal(accountV1) - req, _ := http.NewRequest(http.MethodPost, accountController.RouteAccountPath, bytes.NewBuffer(payload)) + req, _ := http.NewRequest(http.MethodPost, accountController.routeAccountPath, bytes.NewBuffer(payload)) response := executeRequest(req) // Assert @@ -37,7 +37,7 @@ func TestCreateAccountHandler(t *testing.T) { t.Run("Should not create an account - invalid request", func(t *testing.T) { // Test payload := []byte("") - req, _ := http.NewRequest(http.MethodPost, accountController.RouteAccountPath, bytes.NewBuffer(payload)) + req, _ := http.NewRequest(http.MethodPost, accountController.routeAccountPath, bytes.NewBuffer(payload)) response := executeRequest(req) // Assert @@ -51,7 +51,7 @@ func TestCreateAccountHandler(t *testing.T) { // Test payload, _ := json.Marshal(accountV1) - req, _ := http.NewRequest(http.MethodPost, accountController.RouteAccountPath, bytes.NewBuffer(payload)) + req, _ := http.NewRequest(http.MethodPost, accountController.routeAccountPath, bytes.NewBuffer(payload)) response := executeRequest(req) // Assert @@ -68,7 +68,7 @@ func TestFetchAccountHandler(t *testing.T) { // Test payload := []byte("") - req, _ := http.NewRequest(http.MethodGet, accountController.RouteAccountPath+"/"+accountV1.Domain.ID+"/"+accountV1.Environment, bytes.NewBuffer(payload)) + req, _ := http.NewRequest(http.MethodGet, accountController.routeAccountPath+"/"+accountV1.Domain.ID+"/"+accountV1.Environment, bytes.NewBuffer(payload)) response := executeRequest(req) // Assert @@ -83,7 +83,7 @@ func TestFetchAccountHandler(t *testing.T) { t.Run("Should not fetch an account by domain ID / environment - not found", func(t *testing.T) { // Test payload := []byte("") - req, _ := http.NewRequest(http.MethodGet, accountController.RouteAccountPath+"/not-found/default", bytes.NewBuffer(payload)) + req, _ := http.NewRequest(http.MethodGet, accountController.routeAccountPath+"/not-found/default", bytes.NewBuffer(payload)) response := executeRequest(req) // Assert @@ -100,7 +100,7 @@ func TestFetchAccountHandler(t *testing.T) { // Test payload := []byte("") - req, _ := http.NewRequest(http.MethodGet, accountController.RouteAccountPath+"/"+accountV1.Domain.ID, bytes.NewBuffer(payload)) + req, _ := http.NewRequest(http.MethodGet, accountController.routeAccountPath+"/"+accountV1.Domain.ID, bytes.NewBuffer(payload)) response := executeRequest(req) // Assert @@ -115,7 +115,7 @@ func TestFetchAccountHandler(t *testing.T) { 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)) + req, _ := http.NewRequest(http.MethodGet, accountController.routeAccountPath+"/not-found", bytes.NewBuffer(payload)) response := executeRequest(req) // Assert @@ -138,7 +138,7 @@ func TestUpdateAccountHandler(t *testing.T) { // Test payload, _ := json.Marshal(accountV2) - req, _ := http.NewRequest(http.MethodPut, accountController.RouteAccountPath, bytes.NewBuffer(payload)) + req, _ := http.NewRequest(http.MethodPut, accountController.routeAccountPath, bytes.NewBuffer(payload)) response := executeRequest(req) // Assert @@ -164,7 +164,7 @@ func TestUpdateAccountHandler(t *testing.T) { accountRequest.Token = "new-token" payload, _ := json.Marshal(accountRequest) - req, _ := http.NewRequest(http.MethodPut, accountController.RouteAccountPath, bytes.NewBuffer(payload)) + req, _ := http.NewRequest(http.MethodPut, accountController.routeAccountPath, bytes.NewBuffer(payload)) response := executeRequest(req) // Assert @@ -185,7 +185,7 @@ func TestUpdateAccountHandler(t *testing.T) { t.Run("Should not update an account - invalid request", func(t *testing.T) { // Test payload := []byte("") - req, _ := http.NewRequest(http.MethodPut, accountController.RouteAccountPath, bytes.NewBuffer(payload)) + req, _ := http.NewRequest(http.MethodPut, accountController.routeAccountPath, bytes.NewBuffer(payload)) response := executeRequest(req) // Assert @@ -203,7 +203,7 @@ func TestUpdateAccountHandler(t *testing.T) { // Test payload, _ := json.Marshal(accountV1) - req, _ := http.NewRequest(http.MethodPut, accountController.RouteAccountPath, bytes.NewBuffer(payload)) + req, _ := http.NewRequest(http.MethodPut, accountController.routeAccountPath, bytes.NewBuffer(payload)) response := executeRequest(req) // Assert @@ -219,7 +219,7 @@ func TestDeleteAccountHandler(t *testing.T) { accountController.CreateAccountHandler(givenAccountRequest(accountV1)) // Test - req, _ := http.NewRequest(http.MethodDelete, accountController.RouteAccountPath+"/"+accountV1.Domain.ID+"/"+accountV1.Environment, nil) + req, _ := http.NewRequest(http.MethodDelete, accountController.routeAccountPath+"/"+accountV1.Domain.ID+"/"+accountV1.Environment, nil) response := executeRequest(req) // Assert @@ -228,7 +228,7 @@ func TestDeleteAccountHandler(t *testing.T) { t.Run("Should not delete an account by domain ID / environment - not found", func(t *testing.T) { // Test - req, _ := http.NewRequest(http.MethodDelete, accountController.RouteAccountPath+"/not-found/default", nil) + req, _ := http.NewRequest(http.MethodDelete, accountController.routeAccountPath+"/not-found/default", nil) response := executeRequest(req) // Assert @@ -241,7 +241,7 @@ func TestDeleteAccountHandler(t *testing.T) { func givenAccountRequest(data model.Account) (*httptest.ResponseRecorder, *http.Request) { w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodPost, accountController.RouteAccountPath, nil) + r := httptest.NewRequest(http.MethodPost, accountController.routeAccountPath, nil) // Encode the account request as JSON body, _ := json.Marshal(data) diff --git a/src/controller/api.go b/src/controller/api.go index afc7130..463b0f4 100644 --- a/src/controller/api.go +++ b/src/controller/api.go @@ -11,8 +11,8 @@ import ( ) type ApiController struct { - CoreHandler *core.CoreHandler - RouteCheckApiPath string + coreHandler *core.CoreHandler + routeCheckApiPath string } type ApiCheckResponse struct { @@ -32,13 +32,13 @@ type ApiSettingsResponse struct { func NewApiController(coreHandler *core.CoreHandler) *ApiController { return &ApiController{ - CoreHandler: coreHandler, - RouteCheckApiPath: "/api/check", + coreHandler: coreHandler, + routeCheckApiPath: "/api/check", } } func (controller *ApiController) RegisterRoutes(r *mux.Router) http.Handler { - r.NewRoute().Path(controller.RouteCheckApiPath).Name("CheckApi").HandlerFunc(controller.CheckApiHandler).Methods(http.MethodGet) + r.NewRoute().Path(controller.routeCheckApiPath).Name("CheckApi").HandlerFunc(controller.CheckApiHandler).Methods(http.MethodGet) return r } @@ -52,7 +52,7 @@ func (controller *ApiController) CheckApiHandler(w http.ResponseWriter, r *http. SwitcherURL: config.GetEnv("SWITCHER_API_URL"), SwitcherSecret: len(config.GetEnv("SWITCHER_API_JWT_SECRET")) > 0, GitTokenSecret: len(config.GetEnv("GIT_TOKEN_PRIVATE_KEY")) > 0, - CoreHandlerStatus: controller.CoreHandler.Status, + CoreHandlerStatus: controller.coreHandler.Status, NumGoroutines: runtime.NumGoroutine(), }, }, http.StatusOK) diff --git a/src/core/api.go b/src/core/api.go index 2d844ad..93e5a50 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -29,14 +29,14 @@ type IAPIService interface { } type ApiService struct { - ApiKey string - ApiUrl string + apiKey string + apiUrl string } func NewApiService(apiKey string, apiUrl string) *ApiService { return &ApiService{ - ApiKey: apiKey, - ApiUrl: apiUrl, + apiKey: apiKey, + apiUrl: apiUrl, } } @@ -70,7 +70,7 @@ func (a *ApiService) FetchSnapshot(domainId string, environment string) (string, func (a *ApiService) ApplyChangesToAPI(domainId string, environment string, diff model.DiffResult) (ApplyChangeResponse, error) { reqBody, _ := json.Marshal(diff) - responseBody, err := a.doPostRequest(a.ApiUrl+"/gitops/apply", domainId, reqBody) + responseBody, err := a.doPostRequest(a.apiUrl+"/gitops/apply", domainId, reqBody) if err != nil { return ApplyChangeResponse{}, err @@ -83,11 +83,11 @@ func (a *ApiService) ApplyChangesToAPI(domainId string, environment string, diff func (a *ApiService) doGraphQLRequest(domainId string, query string) (string, error) { // Generate a bearer token - token := generateBearerToken(a.ApiKey, domainId) + token := generateBearerToken(a.apiKey, domainId) // Create a new request reqBody, _ := json.Marshal(GraphQLRequest{Query: query}) - req, _ := http.NewRequest("POST", a.ApiUrl+"/gitops-graphql", bytes.NewBuffer(reqBody)) + req, _ := http.NewRequest("POST", a.apiUrl+"/gitops-graphql", bytes.NewBuffer(reqBody)) // Set the request headers setHeaders(req, token) @@ -106,7 +106,7 @@ func (a *ApiService) doGraphQLRequest(domainId string, query string) (string, er func (a *ApiService) doPostRequest(url string, domainId string, body []byte) (string, error) { // Generate a bearer token - token := generateBearerToken(a.ApiKey, domainId) + token := generateBearerToken(a.apiKey, domainId) // Create a new request req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body)) diff --git a/src/core/git.go b/src/core/git.go index 254e40a..4be1163 100644 --- a/src/core/git.go +++ b/src/core/git.go @@ -22,23 +22,23 @@ type IGitService interface { } type GitService struct { - RepoURL string - EncryptedToken string - BranchName string + repoURL string + encryptedToken string + branchName string } func NewGitService(repoURL string, encryptedToken string, branchName string) *GitService { return &GitService{ - RepoURL: repoURL, - EncryptedToken: encryptedToken, - BranchName: branchName, + repoURL: repoURL, + encryptedToken: encryptedToken, + branchName: branchName, } } func (g *GitService) UpdateRepositorySettings(repository string, encryptedToken string, branch string) { - g.RepoURL = repository - g.EncryptedToken = encryptedToken - g.BranchName = branch + g.repoURL = repository + g.encryptedToken = encryptedToken + g.branchName = branch } func (g *GitService) GetRepositoryData(environment string) (*model.RepositoryData, error) { @@ -140,14 +140,14 @@ func (g *GitService) getCommitObject() (*object.Commit, error) { func (g *GitService) getRepository(fs billy.Filesystem) (*git.Repository, error) { return git.Clone(memory.NewStorage(), fs, &git.CloneOptions{ - URL: g.RepoURL, - ReferenceName: plumbing.NewBranchReferenceName(g.BranchName), + URL: g.repoURL, + ReferenceName: plumbing.NewBranchReferenceName(g.branchName), Auth: g.getAuth(), }) } func (g *GitService) getAuth() *http.BasicAuth { - decryptedToken, err := utils.Decrypt(g.EncryptedToken, config.GetEnv("GIT_TOKEN_PRIVATE_KEY")) + decryptedToken, err := utils.Decrypt(g.encryptedToken, config.GetEnv("GIT_TOKEN_PRIVATE_KEY")) if err != nil || decryptedToken == "" { return nil diff --git a/src/core/git_test.go b/src/core/git_test.go index 789decc..a6f49cb 100644 --- a/src/core/git_test.go +++ b/src/core/git_test.go @@ -29,9 +29,9 @@ func TestNewGitService(t *testing.T) { gitService := NewGitService(repoURL, encryptedToken, branchName) // Assert - assert.Equal(t, repoURL, gitService.RepoURL) - assert.Equal(t, encryptedToken, gitService.EncryptedToken) - assert.Equal(t, branchName, gitService.BranchName) + assert.Equal(t, repoURL, gitService.repoURL) + assert.Equal(t, encryptedToken, gitService.encryptedToken) + assert.Equal(t, branchName, gitService.branchName) } func TestUpdateRepositorySettings(t *testing.T) { @@ -45,9 +45,9 @@ func TestUpdateRepositorySettings(t *testing.T) { gitService.UpdateRepositorySettings("newRepoURL", "newEncryptedToken", "newBranch") // Assert - assert.Equal(t, "newRepoURL", gitService.RepoURL) - assert.Equal(t, "newEncryptedToken", gitService.EncryptedToken) - assert.Equal(t, "newBranch", gitService.BranchName) + assert.Equal(t, "newRepoURL", gitService.repoURL) + assert.Equal(t, "newEncryptedToken", gitService.encryptedToken) + assert.Equal(t, "newBranch", gitService.branchName) } func TestGetRepositoryData(t *testing.T) { diff --git a/src/core/handler.go b/src/core/handler.go index 008cdda..ff9e2b5 100644 --- a/src/core/handler.go +++ b/src/core/handler.go @@ -4,28 +4,36 @@ import ( "fmt" "time" + "github.com/switcherapi/switcher-gitops/src/config" "github.com/switcherapi/switcher-gitops/src/model" "github.com/switcherapi/switcher-gitops/src/repository" "github.com/switcherapi/switcher-gitops/src/utils" ) const ( + CoreHandlerStatusCreated = -1 CoreHandlerStatusInit = 0 CoreHandlerStatusRunning = 1 ) type CoreHandler struct { - AccountRepository repository.AccountRepository - ApiService IAPIService - ComparatorService IComparatorService + accountRepository repository.AccountRepository + apiService IAPIService + comparatorService IComparatorService + waitingTime time.Duration Status int } func NewCoreHandler(accountRepository repository.AccountRepository, apiService IAPIService, comparatorService IComparatorService) *CoreHandler { + timeWindow, unitWindow := getTimeWindow(config.GetEnv("HANDLER_WAITING_TIME")) + waitingTime := time.Duration(timeWindow) * unitWindow + return &CoreHandler{ - AccountRepository: accountRepository, - ApiService: apiService, - ComparatorService: comparatorService, + accountRepository: accountRepository, + apiService: apiService, + comparatorService: comparatorService, + waitingTime: waitingTime, + Status: CoreHandlerStatusCreated, } } @@ -39,7 +47,7 @@ func (c *CoreHandler) InitCoreHandlerGoroutine() (int, error) { c.Status = CoreHandlerStatusInit // Load all accounts - accounts := c.AccountRepository.FetchAllActiveAccounts() + accounts := c.accountRepository.FetchAllAccounts() // Iterate over accounts and start account handlers for _, account := range accounts { @@ -60,7 +68,7 @@ func (c *CoreHandler) StartAccountHandler(accountId string, gitService IGitServi for { // Refresh account settings - account, _ := c.AccountRepository.FetchByAccountId(accountId) + account, _ := c.accountRepository.FetchByAccountId(accountId) if account == nil { // Terminate the goroutine (account was deleted) @@ -74,7 +82,7 @@ func (c *CoreHandler) StartAccountHandler(accountId string, gitService IGitServi accountId, account.Domain.Name, account.Environment) c.updateDomainStatus(*account, model.StatusPending, "Account was deactivated") - time.Sleep(1 * time.Minute) + time.Sleep(time.Duration(c.waitingTime)) continue } @@ -89,19 +97,19 @@ func (c *CoreHandler) StartAccountHandler(accountId string, gitService IGitServi 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) + time.Sleep(time.Duration(c.waitingTime)) continue } // Fetch snapshot version from API - snapshotVersionPayload, err := c.ApiService.FetchSnapshotVersion(account.Domain.ID, account.Environment) + snapshotVersionPayload, err := c.apiService.FetchSnapshotVersion(account.Domain.ID, account.Environment) if err != nil { 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) + time.Sleep(time.Duration(c.waitingTime)) continue } @@ -132,26 +140,8 @@ func (c *CoreHandler) syncUp(account model.Account, repositoryData *model.Reposi return } - 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(repositoryData, diff) { - account, err = c.applyChangesToRepository(account, snapshotApi, gitService) - } else { - 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 - } - } else if len(diff.Changes) > 0 { - changeSource = "API" - account = c.applyChangesToAPI(account, repositoryData, diff) - } + changeSource, account, err := c.applyChanges(snapshotApi, account, repositoryData, diff, gitService) if err != nil { utils.Log(utils.LogLevelError, "[%s - %s (%s)] Failed to apply changes [%s] - %s", @@ -167,26 +157,52 @@ func (c *CoreHandler) syncUp(account model.Account, repositoryData *model.Reposi func (c *CoreHandler) checkForChanges(account model.Account, content string) (model.DiffResult, model.Snapshot, error) { // Get Snapshot from API - snapshotJsonFromApi, err := c.ApiService.FetchSnapshot(account.Domain.ID, account.Environment) + snapshotJsonFromApi, err := c.apiService.FetchSnapshot(account.Domain.ID, account.Environment) if err != nil { return model.DiffResult{}, model.Snapshot{}, err } // Convert API JSON to model.Snapshot - snapshotApi := c.ApiService.NewDataFromJson([]byte(snapshotJsonFromApi)) + snapshotApi := c.apiService.NewDataFromJson([]byte(snapshotJsonFromApi)) fromApi := snapshotApi.Snapshot // Convert content to model.Snapshot jsonRight := []byte(content) - fromRepo := c.ComparatorService.NewSnapshotFromJson(jsonRight) + fromRepo := c.comparatorService.NewSnapshotFromJson(jsonRight) // Compare Snapshots and get diff - diffNew := c.ComparatorService.CheckSnapshotDiff(fromRepo, fromApi, NEW) - diffChanged := c.ComparatorService.CheckSnapshotDiff(fromApi, fromRepo, CHANGED) - diffDeleted := c.ComparatorService.CheckSnapshotDiff(fromApi, fromRepo, DELETED) + diffNew := c.comparatorService.CheckSnapshotDiff(fromRepo, fromApi, NEW) + diffChanged := c.comparatorService.CheckSnapshotDiff(fromApi, fromRepo, CHANGED) + diffDeleted := c.comparatorService.CheckSnapshotDiff(fromApi, fromRepo, DELETED) + + return c.comparatorService.MergeResults([]model.DiffResult{diffNew, diffChanged, diffDeleted}), snapshotApi.Snapshot, nil +} + +func (c *CoreHandler) applyChanges(snapshotApi model.Snapshot, account model.Account, + repositoryData *model.RepositoryData, diff model.DiffResult, gitService IGitService) (string, model.Account, error) { + 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)) + + err := error(nil) + + changeSource := "" + if snapshotApi.Domain.Version > account.Domain.Version { + changeSource = "Repository" + if c.isRepositoryOutSync(repositoryData, diff) { + account, err = c.applyChangesToRepository(account, snapshotApi, gitService) + } else { + 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 + } + } else if len(diff.Changes) > 0 { + changeSource = "API" + account = c.applyChangesToAPI(account, repositoryData, diff) + } - return c.ComparatorService.MergeResults([]model.DiffResult{diffNew, diffChanged, diffDeleted}), snapshotApi.Snapshot, nil + return changeSource, account, err } func (c *CoreHandler) applyChangesToAPI(account model.Account, repositoryData *model.RepositoryData, diff model.DiffResult) model.Account { @@ -194,7 +210,7 @@ func (c *CoreHandler) applyChangesToAPI(account model.Account, repositoryData *m // Removed deleted if force prune is disabled if !account.Settings.ForcePrune { - c.ComparatorService.RemoveDeleted(diff) + c.comparatorService.RemoveDeleted(diff) } // Push changes to API @@ -227,7 +243,7 @@ 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 + snapshotVersion := c.apiService.NewDataFromJson([]byte(snapshotVersionPayload)).Snapshot.Domain.Version 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) @@ -246,7 +262,7 @@ func (c *CoreHandler) updateDomainStatus(account model.Account, status string, m account.Domain.Status = status account.Domain.Message = message account.Domain.LastDate = time.Now().Format(time.ANSIC) - c.AccountRepository.Update(&account) + c.accountRepository.Update(&account) } func getTimeWindow(window string) (int, time.Duration) { diff --git a/src/core/handler_test.go b/src/core/handler_test.go index e667e19..447b591 100644 --- a/src/core/handler_test.go +++ b/src/core/handler_test.go @@ -16,17 +16,17 @@ func TestInitCoreHandlerGoroutine(t *testing.T) { t.Run("Should start account handlers for all active accounts", func(t *testing.T) { // Given fakeApiService := NewFakeApiService() - coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeApiService, NewComparatorService()) + coreHandler = NewCoreHandler(coreHandler.accountRepository, fakeApiService, NewComparatorService()) account := givenAccount() account.Domain.ID = "123-init-core-handler" - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test status, err := coreHandler.InitCoreHandlerGoroutine() // Terminate the goroutine - coreHandler.AccountRepository.DeleteByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + coreHandler.accountRepository.DeleteByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) time.Sleep(1 * time.Second) // Assert @@ -39,18 +39,18 @@ func TestInitCoreHandlerGoroutine(t *testing.T) { t.Run("Should not start account handlers when core handler is already running", func(t *testing.T) { // Given fakeApiService := NewFakeApiService() - coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeApiService, NewComparatorService()) + coreHandler = NewCoreHandler(coreHandler.accountRepository, fakeApiService, NewComparatorService()) account := givenAccount() account.Domain.ID = "123-not-init-core-handler" - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test coreHandler.InitCoreHandlerGoroutine() status, _ := coreHandler.InitCoreHandlerGoroutine() // Terminate the goroutine - coreHandler.AccountRepository.DeleteByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + coreHandler.accountRepository.DeleteByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) time.Sleep(1 * time.Second) // Assert @@ -68,7 +68,7 @@ func TestStartAccountHandler(t *testing.T) { account := givenAccount() account.Domain.ID = "123-not-active" account.Settings.Active = false - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test go coreHandler.StartAccountHandler(accountCreated.ID.Hex(), fakeGitService) @@ -76,7 +76,7 @@ func TestStartAccountHandler(t *testing.T) { time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + accountFromDb, _ := coreHandler.accountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) assert.Equal(t, model.StatusPending, accountFromDb.Domain.Status) assert.Equal(t, "Account was deactivated", accountFromDb.Domain.Message) assert.Equal(t, "", accountFromDb.Domain.LastCommit) @@ -91,7 +91,7 @@ func TestStartAccountHandler(t *testing.T) { account := givenAccount() account.Domain.ID = "123-error-get-repo-data" - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test go coreHandler.StartAccountHandler(accountCreated.ID.Hex(), fakeGitService) @@ -99,7 +99,7 @@ func TestStartAccountHandler(t *testing.T) { time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + 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 fetch repository data") assert.Equal(t, "", accountFromDb.Domain.LastCommit) @@ -113,11 +113,11 @@ func TestStartAccountHandler(t *testing.T) { fakeApiService := NewFakeApiService() fakeApiService.throwErrorVersion = true - coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeApiService, NewComparatorService()) + coreHandler = NewCoreHandler(coreHandler.accountRepository, fakeApiService, NewComparatorService()) account := givenAccount() account.Domain.ID = "123-error-fetch-snapshot-version" - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test go coreHandler.StartAccountHandler(accountCreated.ID.Hex(), fakeGitService) @@ -125,7 +125,7 @@ func TestStartAccountHandler(t *testing.T) { time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + 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 fetch snapshot version") assert.Equal(t, "", accountFromDb.Domain.LastCommit) @@ -139,19 +139,19 @@ func TestStartAccountHandler(t *testing.T) { account := givenAccount() account.Domain.ID = "123-deleted" - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test go coreHandler.StartAccountHandler(accountCreated.ID.Hex(), fakeGitService) numGoroutinesBefore := runtime.NumGoroutine() // Terminate the goroutine - coreHandler.AccountRepository.DeleteByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + coreHandler.accountRepository.DeleteByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) time.Sleep(1 * time.Second) numGoroutinesAfter := runtime.NumGoroutine() // Assert - _, err := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + _, err := coreHandler.accountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) assert.LessOrEqual(t, numGoroutinesAfter, numGoroutinesBefore) assert.NotNil(t, err) @@ -162,11 +162,11 @@ func TestStartAccountHandler(t *testing.T) { // Given fakeGitService := NewFakeGitService() fakeApiService := NewFakeApiService() - coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeApiService, NewComparatorService()) + coreHandler = NewCoreHandler(coreHandler.accountRepository, fakeApiService, NewComparatorService()) account := givenAccount() account.Domain.ID = "123-out-sync" - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test go coreHandler.StartAccountHandler(accountCreated.ID.Hex(), fakeGitService) @@ -175,7 +175,7 @@ func TestStartAccountHandler(t *testing.T) { time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + accountFromDb, _ := coreHandler.accountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) assert.Equal(t, model.StatusSynced, accountFromDb.Domain.Status) assert.Contains(t, accountFromDb.Domain.Message, model.MessageSynced) assert.Equal(t, "123", accountFromDb.Domain.LastCommit) @@ -207,11 +207,11 @@ func TestStartAccountHandler(t *testing.T) { } }` fakeApiService := NewFakeApiService() - coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeApiService, NewComparatorService()) + coreHandler = NewCoreHandler(coreHandler.accountRepository, fakeApiService, NewComparatorService()) account := givenAccount() account.Domain.ID = "123-up-to-date-not-synced" - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test go coreHandler.StartAccountHandler(accountCreated.ID.Hex(), fakeGitService) @@ -220,7 +220,7 @@ func TestStartAccountHandler(t *testing.T) { time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + accountFromDb, _ := coreHandler.accountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) assert.Equal(t, model.StatusSynced, accountFromDb.Domain.Status) assert.Contains(t, accountFromDb.Domain.Message, model.MessageSynced) assert.Equal(t, "123", accountFromDb.Domain.LastCommit) @@ -239,13 +239,13 @@ func TestStartAccountHandler(t *testing.T) { } }` fakeApiService := NewFakeApiService() - coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeApiService, NewComparatorService()) + coreHandler = NewCoreHandler(coreHandler.accountRepository, fakeApiService, NewComparatorService()) account := givenAccount() account.Domain.ID = "123-out-sync-prune" account.Domain.Version = 1 account.Settings.ForcePrune = true - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test go coreHandler.StartAccountHandler(accountCreated.ID.Hex(), fakeGitService) @@ -254,7 +254,7 @@ func TestStartAccountHandler(t *testing.T) { time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + accountFromDb, _ := coreHandler.accountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) assert.Equal(t, model.StatusSynced, accountFromDb.Domain.Status) assert.Contains(t, accountFromDb.Domain.Message, model.MessageSynced) assert.Equal(t, "123", accountFromDb.Domain.LastCommit) @@ -273,13 +273,13 @@ func TestStartAccountHandler(t *testing.T) { } }` fakeApiService := NewFakeApiService() - coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeApiService, NewComparatorService()) + coreHandler = NewCoreHandler(coreHandler.accountRepository, fakeApiService, NewComparatorService()) account := givenAccount() account.Domain.ID = "123-out-sync-not-prune" account.Domain.Version = 1 account.Settings.ForcePrune = false - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test go coreHandler.StartAccountHandler(accountCreated.ID.Hex(), fakeGitService) @@ -288,7 +288,7 @@ func TestStartAccountHandler(t *testing.T) { time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + accountFromDb, _ := coreHandler.accountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) assert.Equal(t, model.StatusSynced, accountFromDb.Domain.Status) assert.Contains(t, accountFromDb.Domain.Message, model.MessageSynced) assert.Equal(t, "123", accountFromDb.Domain.LastCommit) @@ -304,11 +304,11 @@ func TestStartAccountHandler(t *testing.T) { fakeGitService.lastCommit = "111" fakeApiService := NewFakeApiService() - coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeApiService, NewComparatorService()) + coreHandler = NewCoreHandler(coreHandler.accountRepository, fakeApiService, NewComparatorService()) account := givenAccount() account.Domain.ID = "123-newer-version" - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test go coreHandler.StartAccountHandler(accountCreated.ID.Hex(), fakeGitService) @@ -317,7 +317,7 @@ func TestStartAccountHandler(t *testing.T) { time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByAccountId(string(accountCreated.ID.Hex())) + accountFromDb, _ := coreHandler.accountRepository.FetchByAccountId(string(accountCreated.ID.Hex())) assert.Equal(t, model.StatusSynced, accountFromDb.Domain.Status) assert.Contains(t, accountFromDb.Domain.Message, model.MessageSynced) assert.Equal(t, "111", accountFromDb.Domain.LastCommit) @@ -333,11 +333,11 @@ func TestStartAccountHandler(t *testing.T) { fakeApiService := NewFakeApiService() fakeApiService.throwErrorSnapshot = true - coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeApiService, NewComparatorService()) + coreHandler = NewCoreHandler(coreHandler.accountRepository, fakeApiService, NewComparatorService()) account := givenAccount() account.Domain.ID = "123-api-error" - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test go coreHandler.StartAccountHandler(accountCreated.ID.Hex(), fakeGitService) @@ -346,7 +346,7 @@ func TestStartAccountHandler(t *testing.T) { time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + 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, "123", accountFromDb.Domain.LastCommit) @@ -361,11 +361,11 @@ func TestStartAccountHandler(t *testing.T) { fakeGitService.errorPushChanges = "authorization failed" fakeApiService := NewFakeApiService() - coreHandler = NewCoreHandler(coreHandler.AccountRepository, fakeApiService, NewComparatorService()) + coreHandler = NewCoreHandler(coreHandler.accountRepository, fakeApiService, NewComparatorService()) account := givenAccount() account.Domain.ID = "123-no-permission" - accountCreated, _ := coreHandler.AccountRepository.Create(&account) + accountCreated, _ := coreHandler.accountRepository.Create(&account) // Test go coreHandler.StartAccountHandler(accountCreated.ID.Hex(), fakeGitService) @@ -374,7 +374,7 @@ func TestStartAccountHandler(t *testing.T) { time.Sleep(1 * time.Second) // Assert - accountFromDb, _ := coreHandler.AccountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) + accountFromDb, _ := coreHandler.accountRepository.FetchByDomainIdEnvironment(accountCreated.Domain.ID, accountCreated.Environment) 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]") diff --git a/src/repository/account.go b/src/repository/account.go index 147bf7b..f1263bd 100644 --- a/src/repository/account.go +++ b/src/repository/account.go @@ -18,7 +18,7 @@ type AccountRepository interface { FetchByAccountId(accountId string) (*model.Account, error) FetchByDomainIdEnvironment(domainId string, environment string) (*model.Account, error) FetchAllByDomainId(domainId string) []model.Account - FetchAllActiveAccounts() []model.Account + FetchAllAccounts() []model.Account Update(account *model.Account) (*model.Account, error) DeleteByAccountId(accountId string) error DeleteByDomainIdEnvironment(domainId string, environment string) error @@ -30,7 +30,6 @@ type AccountRepositoryMongo struct { const domainIdFilter = "domain.id" const environmentFilter = "environment" -const settingsActiveFilter = "settings.active" func NewAccountRepositoryMongo(db *mongo.Database) *AccountRepositoryMongo { registerAccountRepositoryValidators(db) @@ -98,12 +97,11 @@ func (repo *AccountRepositoryMongo) FetchAllByDomainId(domainId string) []model. return accounts } -func (repo *AccountRepositoryMongo) FetchAllActiveAccounts() []model.Account { +func (repo *AccountRepositoryMongo) FetchAllAccounts() []model.Account { collection, ctx, cancel := getDbContext(repo) defer cancel() - filter := primitive.M{settingsActiveFilter: true} - cursor, _ := collection.Find(ctx, filter) + cursor, _ := collection.Find(ctx, primitive.M{}) var accounts []model.Account for cursor.Next(ctx) { diff --git a/src/repository/account_test.go b/src/repository/account_test.go index 0a7e769..045aa0c 100644 --- a/src/repository/account_test.go +++ b/src/repository/account_test.go @@ -101,7 +101,7 @@ func TestFetchAccount(t *testing.T) { assert.NotNil(t, err) }) - t.Run("Should fetch all active accounts", func(t *testing.T) { + t.Run("Should fetch all accounts", func(t *testing.T) { // Drop collection mongoDb.Collection("accounts").Drop(context.Background()) @@ -115,11 +115,11 @@ func TestFetchAccount(t *testing.T) { accountRepository.Create(&account2) // Test - accounts := accountRepository.FetchAllActiveAccounts() + accounts := accountRepository.FetchAllAccounts() // Assert assert.NotNil(t, accounts) - assert.Equal(t, 1, len(accounts)) + assert.Equal(t, 2, len(accounts)) }) t.Run("Should fetch all accounts by domain ID", func(t *testing.T) {