diff --git a/src/controller/account.go b/src/controller/account.go index cc897ce..523d866 100644 --- a/src/controller/account.go +++ b/src/controller/account.go @@ -2,6 +2,7 @@ package controller import ( "encoding/json" + "log" "net/http" "github.com/switcherapi/switcher-gitops/src/model" @@ -11,21 +12,65 @@ import ( type AccountController struct { AccountRepository repository.AccountRepository + RouteAccountPath string +} + +type ErrorResponse struct { + Error string `json:"error"` +} + +func NewAccountController(repo repository.AccountRepository) *AccountController { + return &AccountController{ + AccountRepository: repo, + RouteAccountPath: "/account", + } } func (controller *AccountController) CreateAccountHandler(w http.ResponseWriter, r *http.Request) { var accountRequest model.Account err := json.NewDecoder(r.Body).Decode(&accountRequest) if err != nil { - utils.ResponseJSON(w, err.Error(), http.StatusBadRequest) + utils.ResponseJSON(w, ErrorResponse{Error: "Invalid request"}, http.StatusBadRequest) return } accountCreated, err := controller.AccountRepository.Create(&accountRequest) if err != nil { - utils.ResponseJSON(w, err.Error(), http.StatusInternalServerError) + log.Println(err) + utils.ResponseJSON(w, ErrorResponse{Error: "Error creating account"}, http.StatusInternalServerError) return } utils.ResponseJSON(w, accountCreated, http.StatusCreated) } + +func (controller *AccountController) FetchAccountHandler(w http.ResponseWriter, r *http.Request) { + domainId := r.URL.Path[len(controller.RouteAccountPath+"/"):] + account, err := controller.AccountRepository.FetchByDomainId(domainId) + if err != nil { + log.Println(err) + utils.ResponseJSON(w, ErrorResponse{Error: "Account not found"}, http.StatusNotFound) + return + } + + utils.ResponseJSON(w, account, http.StatusOK) +} + +func (controller *AccountController) UpdateAccountHandler(w http.ResponseWriter, r *http.Request) { + var accountRequest model.Account + err := json.NewDecoder(r.Body).Decode(&accountRequest) + if err != nil { + log.Println(err) + utils.ResponseJSON(w, ErrorResponse{Error: "Invalid request"}, http.StatusBadRequest) + return + } + + accountUpdated, err := controller.AccountRepository.Update(&accountRequest) + if err != nil { + log.Println(err) + utils.ResponseJSON(w, ErrorResponse{Error: "Error updating account"}, http.StatusInternalServerError) + return + } + + utils.ResponseJSON(w, accountUpdated, http.StatusOK) +} diff --git a/src/controller/account_test.go b/src/controller/account_test.go index dd8fb28..024aa6a 100644 --- a/src/controller/account_test.go +++ b/src/controller/account_test.go @@ -13,27 +13,8 @@ import ( ) func TestCreateAccountHandler(t *testing.T) { - // Create a sample account request - accountRequest := model.Account{ - Repository: "switcherapi/switcher-gitops", - Branch: "master", - Domain: model.DomainDetails{ - ID: "123", - Name: "Switcher GitOps", - Version: "123", - LastCommit: "123", - Status: "active", - Message: "Synced successfully", - }, - Settings: model.Settings{ - Active: true, - Window: "10m", - ForcePrune: false, - }, - } - // Create a request and response recorder - w, r := givenAccountRequest(accountRequest) + w, r := givenAccountRequest(accountV1) // Test accountController.CreateAccountHandler(w, r) @@ -44,12 +25,92 @@ func TestCreateAccountHandler(t *testing.T) { assert.Equal(t, http.StatusCreated, w.Code) assert.Nil(t, err) - assert.Equal(t, accountRequest.Repository, accountResponse.Repository) + assert.Equal(t, accountV1.Repository, accountResponse.Repository) +} + +func TestCreateAccountHandlerInvalidRequest(t *testing.T) { + // Create a request and response recorder + w := httptest.NewRecorder() + r := httptest.NewRequest("POST", accountController.RouteAccountPath, nil) + + // Test + accountController.CreateAccountHandler(w, r) + + // Assert + assert.Equal(t, http.StatusBadRequest, w.Code) + assert.Equal(t, "{\"error\":\"Invalid request\"}", w.Body.String()) +} + +func TestFetchAccountHandlerByDomainId(t *testing.T) { + // Create an account + accountController.CreateAccountHandler(givenAccountRequest(accountV1)) + + // Create a request and response recorder + w := httptest.NewRecorder() + r := httptest.NewRequest("GET", accountController.RouteAccountPath+"/123", nil) + + // Test + accountController.FetchAccountHandler(w, r) + + // Assert + var accountResponse model.Account + err := json.NewDecoder(w.Body).Decode(&accountResponse) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Nil(t, err) + assert.Equal(t, accountV1.Repository, accountResponse.Repository) +} + +func TestFetchAccountHandlerByDomainIdNotFound(t *testing.T) { + // Create a request and response recorder + w := httptest.NewRecorder() + r := httptest.NewRequest("GET", accountController.RouteAccountPath+"/111", nil) + + // Test + accountController.FetchAccountHandler(w, r) + + // Assert + assert.Equal(t, http.StatusNotFound, w.Code) + assert.Equal(t, "{\"error\":\"Account not found\"}", w.Body.String()) +} + +func TestUpdateAccountHandler(t *testing.T) { + // Create an account + accountController.CreateAccountHandler(givenAccountRequest(accountV1)) + + // Create a request and response recorder + w, r := givenAccountRequest(accountV2) + + // Test + accountController.UpdateAccountHandler(w, r) + + // Assert + var accountResponse model.Account + err := json.NewDecoder(w.Body).Decode(&accountResponse) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Nil(t, err) + assert.Equal(t, accountV2.Repository, accountResponse.Repository) } +func TestUpdateAccountHandlerInvalidRequest(t *testing.T) { + // Create a request and response recorder + w := httptest.NewRecorder() + r := httptest.NewRequest("PUT", accountController.RouteAccountPath, nil) + + // Test + accountController.UpdateAccountHandler(w, r) + + // Assert + assert.Equal(t, http.StatusBadRequest, w.Code) + assert.Equal(t, "{\"error\":\"Invalid request\"}", w.Body.String()) +} + +// Helpers + func givenAccountRequest(data model.Account) (*httptest.ResponseRecorder, *http.Request) { w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/api/account", nil) + r := httptest.NewRequest("POST", 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 de8e6bc..f2050ae 100644 --- a/src/controller/api.go +++ b/src/controller/api.go @@ -6,12 +6,20 @@ import ( "github.com/switcherapi/switcher-gitops/src/utils" ) -type ApiController struct{} +type ApiController struct { + RouteCheckApiPath string +} type ApiCheckResponse struct { Message string `json:"message"` } +func NewApiController() *ApiController { + return &ApiController{ + RouteCheckApiPath: "/api/check", + } +} + func (controller *ApiController) CheckApiHandler(w http.ResponseWriter, r *http.Request) { utils.ResponseJSON(w, ApiCheckResponse{Message: "API is working"}, http.StatusOK) } diff --git a/src/controller/controller_test.go b/src/controller/controller_test.go index 53d7923..a2eb41c 100644 --- a/src/controller/controller_test.go +++ b/src/controller/controller_test.go @@ -7,13 +7,14 @@ import ( "github.com/switcherapi/switcher-gitops/src/config" "github.com/switcherapi/switcher-gitops/src/db" + "github.com/switcherapi/switcher-gitops/src/model" "github.com/switcherapi/switcher-gitops/src/repository" "go.mongodb.org/mongo-driver/mongo" ) var mongoDb *mongo.Database -var accountController AccountController -var apiController ApiController +var accountController *AccountController +var apiController *ApiController func TestMain(m *testing.M) { setup() @@ -27,14 +28,48 @@ func setup() { config.InitEnv() mongoDb = db.InitDb() - apiController = ApiController{} - accountController = AccountController{ - AccountRepository: &repository.AccountRepositoryMongo{Db: mongoDb}, - } + apiController = NewApiController() + accountController = NewAccountController(&repository.AccountRepositoryMongo{Db: mongoDb}) } func shutdown() { - // Drop the database after all tests have run mongoDb.Drop(context.Background()) mongoDb.Client().Disconnect(context.Background()) } + +// Fixtures + +var accountV1 = model.Account{ + Repository: "switcherapi/switcher-gitops", + Branch: "master", + Domain: model.DomainDetails{ + ID: "123", + Name: "Switcher GitOps", + Version: "123", + LastCommit: "123", + Status: "active", + Message: "Synced successfully", + }, + Settings: model.Settings{ + Active: true, + Window: "10m", + ForcePrune: false, + }, +} + +var accountV2 = model.Account{ + Repository: "switcherapi/switcher-gitops", + Branch: "master", + Domain: model.DomainDetails{ + ID: "123", + Name: "Switcher GitOps", + Version: "123", + LastCommit: "123", + Status: "active", + }, + Settings: model.Settings{ + Active: false, + Window: "5m", + ForcePrune: true, + }, +} diff --git a/src/model/account.go b/src/model/account.go index d09af44..56e8dda 100644 --- a/src/model/account.go +++ b/src/model/account.go @@ -7,7 +7,7 @@ const ( ) type Account struct { - ID primitive.ObjectID `bson:"id,omitempty"` + ID primitive.ObjectID `bson:"_id,omitempty"` Repository string `json:"repository"` Branch string `json:"branch"` Domain DomainDetails `json:"domain"` diff --git a/src/repository/account.go b/src/repository/account.go index 4f4d826..9b148bc 100644 --- a/src/repository/account.go +++ b/src/repository/account.go @@ -11,6 +11,8 @@ import ( type AccountRepository interface { Create(account *model.Account) (*model.Account, error) + FetchByDomainId(domainId string) (*model.Account, error) + Update(account *model.Account) (*model.Account, error) } type AccountRepositoryMongo struct { @@ -18,12 +20,10 @@ type AccountRepositoryMongo struct { } func (repo *AccountRepositoryMongo) Create(account *model.Account) (*model.Account, error) { - collection := repo.Db.Collection(model.CollectionName) - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + collection, ctx, cancel := getDbContext(repo) defer cancel() result, err := collection.InsertOne(ctx, account) - if err != nil { return nil, err } @@ -31,3 +31,40 @@ func (repo *AccountRepositoryMongo) Create(account *model.Account) (*model.Accou account.ID = result.InsertedID.(primitive.ObjectID) return account, nil } + +func (repo *AccountRepositoryMongo) FetchByDomainId(domainId string) (*model.Account, error) { + collection, ctx, cancel := getDbContext(repo) + defer cancel() + + var account model.Account + filter := primitive.M{"domain.id": domainId} + err := collection.FindOne(ctx, filter).Decode(&account) + if err != nil { + return nil, err + } + + return &account, nil +} + +func (repo *AccountRepositoryMongo) Update(account *model.Account) (*model.Account, error) { + collection, ctx, cancel := getDbContext(repo) + defer cancel() + + filter := primitive.M{"domain.id": account.Domain.ID} + update := primitive.M{ + "$set": account, + } + + result := collection.FindOneAndUpdate(ctx, filter, update) + if result.Err() != nil { + return nil, result.Err() + } + + return account, nil +} + +func getDbContext(repo *AccountRepositoryMongo) (*mongo.Collection, context.Context, context.CancelFunc) { + collection := repo.Db.Collection(model.CollectionName) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + return collection, ctx, cancel +} diff --git a/src/server/app.go b/src/server/app.go index b538baa..9670e84 100644 --- a/src/server/app.go +++ b/src/server/app.go @@ -58,12 +58,14 @@ func (app *App) Start() error { } func initRoutes(db *mongo.Database) *mux.Router { - ApiController := controller.ApiController{} - AccountController := controller.AccountController{AccountRepository: &repository.AccountRepositoryMongo{Db: db}} + apiController := controller.NewApiController() + accountController := controller.NewAccountController(&repository.AccountRepositoryMongo{Db: db}) r := mux.NewRouter() - r.HandleFunc("/api/check", ApiController.CheckApiHandler).Methods("GET") - r.HandleFunc("/accounts", AccountController.CreateAccountHandler).Methods("POST") + r.HandleFunc(apiController.RouteCheckApiPath, apiController.CheckApiHandler).Methods("GET") + r.HandleFunc(accountController.RouteAccountPath, accountController.CreateAccountHandler).Methods("POST") + r.HandleFunc(accountController.RouteAccountPath+"/{domainId}", accountController.FetchAccountHandler).Methods("GET") + r.HandleFunc(accountController.RouteAccountPath+"/{domainId}", accountController.UpdateAccountHandler).Methods("PUT") return r }