Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 47 additions & 2 deletions src/controller/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package controller

import (
"encoding/json"
"log"
"net/http"

"github.com/switcherapi/switcher-gitops/src/model"
Expand All @@ -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)
}
105 changes: 83 additions & 22 deletions src/controller/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
10 changes: 9 additions & 1 deletion src/controller/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
49 changes: 42 additions & 7 deletions src/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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,
},
}
2 changes: 1 addition & 1 deletion src/model/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down
43 changes: 40 additions & 3 deletions src/repository/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,60 @@ 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 {
Db *mongo.Database
}

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
}

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
}
10 changes: 6 additions & 4 deletions src/server/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}