diff --git a/Makefile b/Makefile index 2e653a6..56cbe86 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,10 @@ app: migrate ui-app: docker-compose up -d ui-app -test: +test: migratetestdb docker-compose run --rm test -testcli: +testcli: migratetestdb docker-compose run --rm test bash bounce: @@ -16,6 +16,9 @@ bounce: migrate: db docker-compose run --rm app bash -c "goose -dir migrations postgres postgres://keygo:keygo@db:5432/keygo?sslmode=disable up" +migratetestdb: + docker-compose run --rm test bash -c "goose -dir migrations postgres postgres://keygo:keygo@testdb:5432/keygo?sslmode=disable up" + migratedown: db docker-compose run --rm app bash -c "goose -dir migrations postgres postgres://keygo:keygo@db:5432/keygo?sslmode=disable down" @@ -39,4 +42,4 @@ install-js-deps: proxy: docker-compose up -d proxy -.PHONY: app ui-app test bounce migrate migratedown new-migration db fresh adminer install-js-deps proxy +.PHONY: app ui-app test bounce migrate migratetestdb migratedown new-migration db fresh adminer install-js-deps proxy diff --git a/app/app.go b/app/app.go deleted file mode 100644 index ea278de..0000000 --- a/app/app.go +++ /dev/null @@ -1,7 +0,0 @@ -package app - -type DataServices struct { - TenantService - TokenService - UserService -} diff --git a/app/tenant.go b/app/tenant.go index 9b44676..b06b81c 100644 --- a/app/tenant.go +++ b/app/tenant.go @@ -2,33 +2,10 @@ package app import ( "time" - - "github.com/labstack/echo/v4" ) const minTenantNameLength = 3 -// TenantService is a service for managing tenants -type TenantService interface { - // FindTenantByID retrieves a tenant by ID - FindTenantByID(ctx echo.Context, id string) (Tenant, error) - - // FindTenants retrieves a list of tenants by filter - FindTenants(ctx echo.Context, filter TenantFilter) ([]Tenant, int, error) - - // CreateTenant creates a new tenant - CreateTenant(ctx echo.Context, input TenantCreateInput) (Tenant, error) - - // UpdateTenant updates a tenant object - UpdateTenant(ctx echo.Context, id string, input TenantUpdateInput) (Tenant, error) - - // DeleteTenant permanently deletes a tenant and all child objects - DeleteTenant(ctx echo.Context, id string) error - - // CreateTenantUser creates a new tenant user - CreateTenantUser(ctx echo.Context, input TenantUserCreateInput) error -} - // Tenant is the full model that identifies an app Tenant type Tenant struct { ID string diff --git a/app/token.go b/app/token.go index 7da1065..220d4c7 100644 --- a/app/token.go +++ b/app/token.go @@ -2,30 +2,8 @@ package app import ( "time" - - "github.com/labstack/echo/v4" ) -// TokenService is a service for managing tokens -type TokenService interface { - // FindToken looks up a token object by raw, unhashed token, and returns the Token object - // with associated User - // Returns ERR_NOTFOUND if token does not exist - FindToken(ctx echo.Context, raw string) (Token, error) - - // CreateToken creates a new token object - // - // On success, the token.ID is set to the new token ID - CreateToken(ctx echo.Context, input TokenCreateInput) (Token, error) - - // DeleteToken permanently deletes a token object from the system by ID. - // The parent user object is not removed. - DeleteToken(ctx echo.Context, id string) error - - // UpdateToken extends a token's ExpiresAt - UpdateToken(ctx echo.Context, id string, input TokenUpdateInput) error -} - type Token struct { // TODO: remove private fields not appropriate for the API. (May require architecture changes.) ID string diff --git a/app/user.go b/app/user.go index 37e603b..7e71551 100644 --- a/app/user.go +++ b/app/user.go @@ -2,33 +2,10 @@ package app import ( "time" - - "github.com/labstack/echo/v4" ) const UserRoleAdmin = "Admin" -// UserService is a service for managing users -type UserService interface { - // FindUserByID retrieves a user by ID - FindUserByID(ctx echo.Context, id string) (User, error) - - // FindUsers retrieves a list of users by filter - FindUsers(ctx echo.Context, filter UserFilter) ([]User, int, error) - - // CreateUser creates a new user - CreateUser(ctx echo.Context, input UserCreateInput) (User, error) - - // UpdateUser updates a user object - UpdateUser(ctx echo.Context, id string, input UserUpdateInput) (User, error) - - // DeleteUser permanently deletes a user and all child objects - DeleteUser(ctx echo.Context, id string) error - - // TouchLastLoginAt sets the LastLoginAt field to the current time - TouchLastLoginAt(ctx echo.Context, id string) error -} - // UserFilter is a filter passed to FindUsers() type UserFilter struct { // Filtering fields. diff --git a/cmd/api/main.go b/cmd/api/main.go index 00cc236..5577b84 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -5,7 +5,6 @@ import ( "github.com/labstack/gommon/log" - "github.com/briskt/keygo/app" "github.com/briskt/keygo/db" "github.com/briskt/keygo/server" ) @@ -14,12 +13,7 @@ func main() { fmt.Println("starting API") dbConnection := db.OpenDB() - services := app.DataServices{ - TenantService: db.NewTenantService(), - TokenService: db.NewTokenService(), - UserService: db.NewUserService(), - } - e := server.New(server.WithDataBase(dbConnection), server.WithDataServices(services)) + e := server.New(server.WithDataBase(dbConnection)) if l, ok := e.Logger.(*log.Logger); ok { l.SetHeader("${time_rfc3339} ${level}") diff --git a/db/db_test.go b/db/db_test.go index dfbcb77..5a188a0 100644 --- a/db/db_test.go +++ b/db/db_test.go @@ -22,7 +22,6 @@ type TestSuite struct { *require.Assertions ctx echo.Context DB *gorm.DB - app.DataServices } // SetupTest runs before every test function @@ -33,8 +32,6 @@ func (ts *TestSuite) SetupTest() { migrations.Fresh(sqlDB) } ts.Assertions = require.New(ts.T()) - ts.TokenService = db.NewTokenService() - ts.UserService = db.NewUserService() } func Test_RunSuite(t *testing.T) { diff --git a/db/tenant.go b/db/tenant.go index 7edf2ec..5b8f85e 100644 --- a/db/tenant.go +++ b/db/tenant.go @@ -22,49 +22,22 @@ func (u *Tenant) BeforeCreate(_ *gorm.DB) error { return nil } -// Ensure service implements interface. -var _ app.TenantService = (*TenantService)(nil) - -// TenantService is a service for managing tenants. -type TenantService struct{} - -// NewTenantService returns a new instance of TenantService. -func NewTenantService() *TenantService { - return &TenantService{} -} - -// FindTenantByID retrieves a tenant by ID along with their associated auth objects. -func (s *TenantService) FindTenantByID(ctx echo.Context, id string) (app.Tenant, error) { - tenant, err := findTenantByID(ctx, id) - if err != nil { - return app.Tenant{}, err - } - return convertTenant(ctx, tenant) -} - // FindTenants retrieves a list of tenants by filter. Also returns total count of // matching tenants which may differ from returned results if filter.Limit is specified. -func (s *TenantService) FindTenants(ctx echo.Context, _ app.TenantFilter) ([]app.Tenant, int, error) { +func FindTenants(ctx echo.Context, _ app.TenantFilter) ([]Tenant, error) { var tenants []Tenant result := Tx(ctx).Find(&tenants) if result.Error != nil { - return []app.Tenant{}, 0, result.Error - } - appTenants := make([]app.Tenant, len(tenants)) - for i := range tenants { - tenant, err := convertTenant(ctx, tenants[i]) - if err != nil { - return nil, 0, err - } - appTenants[i] = tenant + return []Tenant{}, result.Error } - return appTenants, len(tenants), nil + + return tenants, nil } // CreateTenant creates a new tenant. -func (s *TenantService) CreateTenant(ctx echo.Context, input app.TenantCreateInput) (app.Tenant, error) { +func CreateTenant(ctx echo.Context, input app.TenantCreateInput) (Tenant, error) { if err := input.Validate(); err != nil { - return app.Tenant{}, err + return Tenant{}, err } newTenant := Tenant{ @@ -72,21 +45,21 @@ func (s *TenantService) CreateTenant(ctx echo.Context, input app.TenantCreateInp } err := Tx(ctx).Create(&newTenant).Error if err != nil { - return app.Tenant{}, err + return Tenant{}, err } - return convertTenant(ctx, newTenant) + return newTenant, nil } // UpdateTenant updates a tenant object. -func (s *TenantService) UpdateTenant(ctx echo.Context, id string, input app.TenantUpdateInput) (app.Tenant, error) { +func UpdateTenant(ctx echo.Context, id string, input app.TenantUpdateInput) (Tenant, error) { if err := input.Validate(); err != nil { - return app.Tenant{}, err + return Tenant{}, err } - tenant, err := findTenantByID(ctx, id) + tenant, err := FindTenantByID(ctx, id) if err != nil { - return app.Tenant{}, err + return Tenant{}, err } if input.Name != nil { @@ -95,14 +68,14 @@ func (s *TenantService) UpdateTenant(ctx echo.Context, id string, input app.Tena result := Tx(ctx).Save(&tenant) if err != nil { - return app.Tenant{}, result.Error + return Tenant{}, result.Error } - return convertTenant(ctx, tenant) + return tenant, nil } // DeleteTenant permanently deletes a tenant and all child objects -func (s *TenantService) DeleteTenant(ctx echo.Context, id string) error { +func DeleteTenant(ctx echo.Context, id string) error { result := Tx(ctx).Where("id = ?", id).Delete(&Tenant{}) if err := result.Error; err != nil { return err @@ -111,29 +84,29 @@ func (s *TenantService) DeleteTenant(ctx echo.Context, id string) error { } // CreateTenantUser permanently deletes a tenant and all child objects -func (s *TenantService) CreateTenantUser(ctx echo.Context, input app.TenantUserCreateInput) error { +func CreateTenantUser(ctx echo.Context, input app.TenantUserCreateInput) error { return nil } -// findTenantByID is a helper function to fetch a tenant by ID. -func findTenantByID(ctx echo.Context, id string) (Tenant, error) { +// FindTenantByID is a function to fetch a tenant by ID. +func FindTenantByID(ctx echo.Context, id string) (Tenant, error) { var tenant Tenant result := Tx(ctx).First(&tenant, "id = ?", id) return tenant, result.Error } -func convertTenant(c echo.Context, t Tenant) (app.Tenant, error) { +func ConvertTenant(c echo.Context, t Tenant) (app.Tenant, error) { tenant := app.Tenant{ ID: t.ID, Name: t.Name, CreatedAt: t.CreatedAt, UpdatedAt: t.UpdatedAt, } - users, n, err := findUsers(c, app.UserFilter{TenantID: &t.ID}) + users, err := FindUsers(c, app.UserFilter{TenantID: &t.ID}) if err != nil { return app.Tenant{}, err } - tenant.UserIDs = make([]string, n) + tenant.UserIDs = make([]string, len(users)) for i, user := range users { tenant.UserIDs[i] = user.ID } diff --git a/db/token.go b/db/token.go index bc525f6..1939b62 100644 --- a/db/token.go +++ b/db/token.go @@ -40,7 +40,9 @@ func (t *Token) BeforeCreate(_ *gorm.DB) error { // create a new token object in the database. On success, the ID is set to the new database // ID & timestamp fields are set to the current time func (t *Token) create(ctx echo.Context) error { - t.PlainText = randomString() + if t.PlainText == "" { + t.PlainText = randomString() + } t.Hash = hashToken(t.PlainText) err := Tx(ctx).Omit("User").Create(t).Error @@ -120,29 +122,18 @@ func (t *Token) loadUser(ctx echo.Context) (err error) { return nil } -// Ensure service implements interface. -var _ app.TokenService = (*TokenService)(nil) - -// TokenService is a service for managing API auth tokens -type TokenService struct{} - -// NewTokenService returns a new instance of TokenService -func NewTokenService() *TokenService { - return &TokenService{} -} - -func (t TokenService) FindToken(ctx echo.Context, raw string) (app.Token, error) { +func FindToken(ctx echo.Context, raw string) (Token, error) { token, err := findToken(ctx, raw) if err != nil { - return app.Token{}, err + return Token{}, err } - return convertToken(ctx, token) + return token, nil } // CreateToken creates a new token object. -func (t TokenService) CreateToken(ctx echo.Context, input app.TokenCreateInput) (app.Token, error) { +func CreateToken(ctx echo.Context, input app.TokenCreateInput) (Token, error) { if err := input.Validate(); err != nil { - return app.Token{}, err + return Token{}, err } token := Token{ @@ -153,17 +144,17 @@ func (t TokenService) CreateToken(ctx echo.Context, input app.TokenCreateInput) err := token.create(ctx) if err != nil { - return app.Token{}, err + return Token{}, err } - return convertToken(ctx, token) + return token, nil } -func (t TokenService) DeleteToken(ctx echo.Context, id string) error { +func DeleteToken(ctx echo.Context, id string) error { return deleteToken(ctx, id) } -func (t TokenService) UpdateToken(ctx echo.Context, id string, input app.TokenUpdateInput) error { +func UpdateToken(ctx echo.Context, id string, input app.TokenUpdateInput) error { if err := input.Validate(); err != nil { return err } @@ -176,12 +167,12 @@ func (t TokenService) UpdateToken(ctx echo.Context, id string, input app.TokenUp return err } -func convertToken(ctx echo.Context, token Token) (app.Token, error) { +func ConvertToken(ctx echo.Context, token Token) (app.Token, error) { if err := token.loadUser(ctx); err != nil { return app.Token{}, err } - user, err := convertUser(ctx, token.User) + user, err := ConvertUser(ctx, token.User) if err != nil { return app.Token{}, err } diff --git a/db/token_test.go b/db/token_test.go index e864cbb..1e82c34 100644 --- a/db/token_test.go +++ b/db/token_test.go @@ -4,15 +4,16 @@ import ( "time" "github.com/briskt/keygo/app" + "github.com/briskt/keygo/db" ) -func (ts *TestSuite) TestTokenService_CreateToken() { - user, err := ts.UserService.CreateUser(ts.ctx, app.UserCreateInput{Email: "a@b.com"}) +func (ts *TestSuite) Test_CreateToken() { + user, err := db.CreateUser(ts.ctx, app.UserCreateInput{Email: "a@b.com"}) ts.NoError(err) // Create new record and check generated fields exp := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) - newToken, err := ts.TokenService.CreateToken(ts.ctx, app.TokenCreateInput{AuthID: "a", UserID: user.ID, ExpiresAt: exp}) + newToken, err := db.CreateToken(ts.ctx, app.TokenCreateInput{AuthID: "a", UserID: user.ID, ExpiresAt: exp}) ts.NoError(err) ts.NotEmpty(newToken.ID, "ID is not set") @@ -23,13 +24,13 @@ func (ts *TestSuite) TestTokenService_CreateToken() { ts.Equal(exp, newToken.ExpiresAt) // Query database and compare - fromDB, err := ts.TokenService.FindToken(ts.ctx, newToken.PlainText) + fromDB, err := db.FindToken(ts.ctx, newToken.PlainText) ts.NoError(err, "couldn't find created token %s", newToken.PlainText) ts.Equal(newToken.ID, fromDB.ID) ts.Equal(newToken.UserID, fromDB.UserID) // Expect validation error - _, err = ts.TokenService.CreateToken(ts.ctx, app.TokenCreateInput{}) + _, err = db.CreateToken(ts.ctx, app.TokenCreateInput{}) ts.Error(err, "expected validation error") ts.Equal(app.ERR_INVALID, app.ErrorCode(err)) } diff --git a/db/user.go b/db/user.go index ae0ef22..9a7afff 100644 --- a/db/user.go +++ b/db/user.go @@ -28,84 +28,89 @@ func (u *User) BeforeCreate(_ *gorm.DB) error { return nil } -// Ensure service implements interface. -var _ app.UserService = (*UserService)(nil) - -// UserService is a service for managing users. -type UserService struct{} - -// NewUserService returns a new instance of UserService. -func NewUserService() *UserService { - return &UserService{} -} - // FindUserByID retrieves a user by ID along with their associated auth objects. -func (s *UserService) FindUserByID(ctx echo.Context, id string) (app.User, error) { +func FindUserByID(ctx echo.Context, id string) (User, error) { user, err := findUserByID(ctx, id) if err != nil { - return app.User{}, err + return User{}, err } - return convertUser(ctx, user) + return user, nil } // FindUsers retrieves a list of users by filter. Also returns total count of // matching users which may differ from returned results if filter.Limit is specified. -func (s *UserService) FindUsers(ctx echo.Context, filter app.UserFilter) ([]app.User, int, error) { - users, n, err := findUsers(ctx, filter) - if err != nil { - return []app.User{}, 0, err +func FindUsers(ctx echo.Context, filter app.UserFilter) ([]User, error) { + var users []User + q := Tx(ctx) + if filter.Email != nil { + q = q.Where("email = ?", filter.Email) } - keygoUsers := make([]app.User, len(users)) - for i := range users { - u, err := convertUser(ctx, users[i]) - if err != nil { - return nil, 0, err - } - keygoUsers[i] = u + if filter.TenantID != nil { + q = q.Where("tenant_id = ?", filter.TenantID) } - return keygoUsers, n, nil + result := q.Find(&users) + return users, result.Error } // CreateUser creates a new user. -func (s *UserService) CreateUser(ctx echo.Context, userCreate app.UserCreateInput) (app.User, error) { +func CreateUser(ctx echo.Context, userCreate app.UserCreateInput) (User, error) { if err := userCreate.Validate(); err != nil { - return app.User{}, err + return User{}, err } - newUser, err := createUser(ctx, User{ + + user := User{ FirstName: userCreate.FirstName, LastName: userCreate.LastName, Email: userCreate.Email, AvatarURL: userCreate.AvatarURL, Role: userCreate.Role, - }) - if err != nil { - return app.User{}, err } - return convertUser(ctx, newUser) + + // TODO: remove this when ready + user.Role = app.UserRoleAdmin + + result := Tx(ctx).Create(&user) + if result.Error != nil { + return User{}, result.Error + } + return user, nil } // UpdateUser updates a user object. -func (s *UserService) UpdateUser(ctx echo.Context, id string, input app.UserUpdateInput) (app.User, error) { +func UpdateUser(ctx echo.Context, id string, input app.UserUpdateInput) (User, error) { if err := input.Validate(); err != nil { - return app.User{}, err + return User{}, err } - user, err := updateUser(ctx, id, input) + user, err := findUserByID(ctx, id) if err != nil { - return app.User{}, err + return User{}, err + } + + if input.Email != nil { + user.Email = *input.Email } - return convertUser(ctx, user) + if input.FirstName != nil { + user.FirstName = *input.FirstName + } + if input.LastName != nil { + user.LastName = *input.LastName + } + + result := Tx(ctx).Save(&user) + if result.Error != nil { + return User{}, result.Error + } + return user, nil } // DeleteUser permanently deletes a user and all child objects -func (s *UserService) DeleteUser(ctx echo.Context, id string) error { - if err := deleteUser(ctx, id); err != nil { - return err - } - return nil +func DeleteUser(ctx echo.Context, id string) error { + result := Tx(ctx).Where("id = ?", id).Delete(&User{}) + return result.Error } // TouchLastLoginAt sets the LastLoginAt field to the current time -func (s *UserService) TouchLastLoginAt(ctx echo.Context, id string) error { +func TouchLastLoginAt(ctx echo.Context, id string) error { result := Tx(ctx).Model(&User{}).Where("id = ?", id).Update("last_login_at", time.Now()) return result.Error } @@ -117,58 +122,7 @@ func findUserByID(ctx echo.Context, id string) (User, error) { return user, result.Error } -// findUsers returns a list of users. Also returns a count of -// total matching users which may differ if filter.Limit is set. -func findUsers(ctx echo.Context, filter app.UserFilter) ([]User, int, error) { - var users []User - q := Tx(ctx) - if filter.Email != nil { - q = q.Where("email = ?", filter.Email) - } - if filter.TenantID != nil { - q = q.Where("tenant_id = ?", filter.TenantID) - } - result := q.Find(&users) - return users, len(users), result.Error -} - -// createUser creates a new user. Sets the new database ID to user.ID and sets -// the timestamps to the current time. -func createUser(ctx echo.Context, user User) (User, error) { - // TODO: remove this when ready - user.Role = app.UserRoleAdmin - result := Tx(ctx).Create(&user) - return user, result.Error -} - -// updateUser updates fields on a user object. -func updateUser(ctx echo.Context, id string, upd app.UserUpdateInput) (User, error) { - user, err := findUserByID(ctx, id) - if err != nil { - return User{}, err - } - - if upd.Email != nil { - user.Email = *upd.Email - } - if upd.FirstName != nil { - user.FirstName = *upd.FirstName - } - if upd.LastName != nil { - user.LastName = *upd.LastName - } - - result := Tx(ctx).Save(&user) - return user, result.Error -} - -// deleteUser permanently removes a user by ID. -func deleteUser(ctx echo.Context, id string) error { - result := Tx(ctx).Where("id = ?", id).Delete(&User{}) - return result.Error -} - -func convertUser(_ echo.Context, u User) (app.User, error) { +func ConvertUser(_ echo.Context, u User) (app.User, error) { return app.User{ ID: u.ID, FirstName: u.FirstName, diff --git a/db/user_test.go b/db/user_test.go index ab5a972..3b7def8 100644 --- a/db/user_test.go +++ b/db/user_test.go @@ -8,9 +8,7 @@ import ( "github.com/briskt/keygo/db" ) -func (ts *TestSuite) TestUserService_CreateUser() { - s := db.NewUserService() - +func (ts *TestSuite) Test_CreateUser() { u := app.UserCreateInput{ FirstName: "susy", LastName: "smith", @@ -18,35 +16,35 @@ func (ts *TestSuite) TestUserService_CreateUser() { } // Create new record and check generated fields - newUser, err := s.CreateUser(ts.ctx, u) + newUser, err := db.CreateUser(ts.ctx, u) ts.NoError(err) ts.False(newUser.ID == "", "ID is not set") ts.False(newUser.CreatedAt.IsZero(), "expected CreatedAt") ts.False(newUser.UpdatedAt.IsZero(), "expected UpdatedAt") // Query database and compare - fromDB, err := s.FindUserByID(ts.ctx, newUser.ID) + fromDB, err := db.FindUserByID(ts.ctx, newUser.ID) ts.NoError(err) ts.SameUser(newUser, fromDB) // Expect a validation error - _, err = s.CreateUser(ts.ctx, app.UserCreateInput{}) + _, err = db.CreateUser(ts.ctx, app.UserCreateInput{}) ts.Error(err) ts.Equal(app.ErrorCode(err), app.ERR_INVALID) ts.Contains(app.ErrorMessage(err), "Email") } // SameUser verifies two User objects are the same except for the timestamps -func (ts *TestSuite) SameUser(expected app.User, actual app.User, msgAndArgs ...interface{}) { +func (ts *TestSuite) SameUser(expected db.User, actual db.User, msgAndArgs ...interface{}) { actual.CreatedAt = expected.CreatedAt actual.UpdatedAt = expected.UpdatedAt ts.Equal(expected, actual, msgAndArgs...) } // CreateUser creates a user in the database. Fatal on error. -func (ts *TestSuite) CreateUser(user app.UserCreateInput) app.User { +func (ts *TestSuite) CreateUser(user app.UserCreateInput) db.User { ts.T().Helper() - newUser, err := db.NewUserService().CreateUser(ts.ctx, user) + newUser, err := db.CreateUser(ts.ctx, user) if err != nil { ts.Fail("failed to create user: " + err.Error()) } @@ -54,12 +52,10 @@ func (ts *TestSuite) CreateUser(user app.UserCreateInput) app.User { } func (ts *TestSuite) Test_FindUserByID() { - s := db.NewUserService() - - user, err := s.CreateUser(ts.ctx, app.UserCreateInput{FirstName: "joe", Email: "joe@example.com"}) + user, err := db.CreateUser(ts.ctx, app.UserCreateInput{FirstName: "joe", Email: "joe@example.com"}) ts.NoError(err) - found, err := s.FindUserByID(ts.ctx, user.ID) + found, err := db.FindUserByID(ts.ctx, user.ID) ts.NoError(err) ts.Equal(user.ID, found.ID) @@ -68,12 +64,10 @@ func (ts *TestSuite) Test_FindUserByID() { } func (ts *TestSuite) Test_FindUsers() { - s := db.NewUserService() - joeEmail := "joe@example.com" - joe, err := s.CreateUser(ts.ctx, app.UserCreateInput{FirstName: "joe", Email: joeEmail}) + joe, err := db.CreateUser(ts.ctx, app.UserCreateInput{FirstName: "joe", Email: joeEmail}) ts.NoError(err) - sally, err := s.CreateUser(ts.ctx, app.UserCreateInput{FirstName: "sally", Email: "sally@example.com"}) + sally, err := db.CreateUser(ts.ctx, app.UserCreateInput{FirstName: "sally", Email: "sally@example.com"}) ts.NoError(err) notFindableEmail := "nobody@example.com" @@ -105,13 +99,12 @@ func (ts *TestSuite) Test_FindUsers() { } for _, tt := range tests { ts.T().Run(tt.name, func(t *testing.T) { - found, n, err := s.FindUsers(ts.ctx, tt.filter) + found, err := db.FindUsers(ts.ctx, tt.filter) if tt.wantError { ts.Error(err) return } ts.NoError(err) - ts.Equal(n, len(found)) foundIDs := make([]string, len(found)) for i := range found { foundIDs[i] = found[i].ID @@ -122,9 +115,7 @@ func (ts *TestSuite) Test_FindUsers() { } func (ts *TestSuite) Test_TouchLastLoginAt() { - s := db.NewUserService() - - joe, err := s.CreateUser(ts.ctx, app.UserCreateInput{FirstName: "joe", Email: "joe@example.com"}) + joe, err := db.CreateUser(ts.ctx, app.UserCreateInput{FirstName: "joe", Email: "joe@example.com"}) ts.NoError(err) now := time.Now().UTC() @@ -132,10 +123,10 @@ func (ts *TestSuite) Test_TouchLastLoginAt() { err = ts.DB.Exec("update users set last_login_at = ? where id = ?", yesterday, joe.ID).Error ts.NoError(err) - err = s.TouchLastLoginAt(ts.ctx, joe.ID) + err = db.TouchLastLoginAt(ts.ctx, joe.ID) ts.NoError(err) - found, err := s.FindUserByID(ts.ctx, joe.ID) + found, err := db.FindUserByID(ts.ctx, joe.ID) ts.NoError(err) ts.WithinDuration(now, *found.LastLoginAt, time.Second) diff --git a/docker-compose.yml b/docker-compose.yml index 255bd1a..1fd30ac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,8 @@ services: GO_ENV: development SUPPORT_EMAIL: forget_about_it@example.com depends_on: - - db + db: + condition: service_healthy working_dir: /src ui-app: @@ -41,32 +42,39 @@ services: environment: DATABASE_URL: postgres://keygo:keygo@testdb:5432/keygo?sslmode=disable GO_ENV: test + HISTCONTROL: ignoreboth depends_on: - - testdb + testdb: + condition: service_healthy working_dir: /src - command: go test ./... + command: go test -p 1 ./... - db: + db_base: image: postgres:11.6 volumes: - ./db-init.sh:/docker-entrypoint-initdb.d/db-init.sh - ports: - - "5433:5432" environment: POSTGRES_USER: keygo POSTGRES_PASSWORD: keygo POSTGRES_DB: keygo + healthcheck: + test: ["CMD-SHELL", "pg_isready", "-d", "keygo"] + start_period: 1s + interval: 500ms + timeout: 100ms + retries: 3 + + db: + extends: + service: db_base + ports: + - "5433:5432" testdb: - image: postgres:11.6 - volumes: - - ./db-init.sh:/docker-entrypoint-initdb.d/db-init.sh + extends: + service: db_base ports: - - "5432:5432" - environment: - POSTGRES_USER: keygo - POSTGRES_PASSWORD: keygo - POSTGRES_DB: keygo + - "5432:5432" # http://localhost:8000/?pgsql=db&username=keygo&db=keygo&ns=public adminer: diff --git a/internal/mock/mock.go b/internal/mock/mock.go deleted file mode 100644 index 54fbb80..0000000 --- a/internal/mock/mock.go +++ /dev/null @@ -1,16 +0,0 @@ -package mock - -import ( - "strconv" -) - -var newID func() string - -var lastID = 0 - -func init() { - newID = func() string { - lastID++ - return strconv.Itoa(lastID) - } -} diff --git a/internal/mock/tenantmock.go b/internal/mock/tenantmock.go deleted file mode 100644 index 36a9a8e..0000000 --- a/internal/mock/tenantmock.go +++ /dev/null @@ -1,86 +0,0 @@ -package mock - -import ( - "fmt" - "time" - - "github.com/labstack/echo/v4" - - "github.com/briskt/keygo/app" -) - -type TenantService struct { - Tenants map[string]app.Tenant - - FindTenantsFn func(ctx echo.Context, filter app.TenantFilter) ([]app.Tenant, int, error) -} - -// Ensure service implements interface. -var _ app.TenantService = (*TenantService)(nil) - -func NewTenantService() TenantService { - return TenantService{ - Tenants: map[string]app.Tenant{}, - } -} - -func (m *TenantService) DeleteAllTenants() { - m.Tenants = map[string]app.Tenant{} -} - -func (m *TenantService) FindTenantByID(context echo.Context, id string) (app.Tenant, error) { - // TODO: decide if this is a better API than using the ID in TenantFilter passed to FindTenants - t, ok := m.Tenants[id] - if !ok { - return app.Tenant{}, fmt.Errorf("no Tenant found by ID %q", id) - } - return t, nil -} - -func (m *TenantService) FindTenants(context echo.Context, filter app.TenantFilter) ([]app.Tenant, int, error) { - if m.FindTenantsFn != nil { - return m.FindTenantsFn(context, filter) - } - var Tenants []app.Tenant - for _, t := range m.Tenants { - if filter.Name != nil && *filter.Name != t.Name { - continue - } - if filter.ID != nil && *filter.ID != t.ID { - continue - } - - Tenants = append(Tenants, t) - } - return Tenants, len(Tenants), nil -} - -func (m *TenantService) CreateTenant(context echo.Context, input app.TenantCreateInput) (app.Tenant, error) { - if err := input.Validate(); err != nil { - return app.Tenant{}, err - } - now := time.Now() - Tenant := app.Tenant{ - ID: newID(), - Name: input.Name, - CreatedAt: now, - UpdatedAt: now, - } - m.Tenants[Tenant.ID] = Tenant - return Tenant, nil -} - -func (m *TenantService) UpdateTenant(context echo.Context, id string, input app.TenantUpdateInput) (app.Tenant, error) { - if err := input.Validate(); err != nil { - return app.Tenant{}, err - } - panic("implement mock TenantService UpdateTenant") -} - -func (m *TenantService) DeleteTenant(context echo.Context, id string) error { - panic("implement mock TenantService DeleteTenant") -} - -func (m *TenantService) CreateTenantUser(context echo.Context, input app.TenantUserCreateInput) error { - panic("implement mock TenantService CreateTenantUser") -} diff --git a/internal/mock/tokenmock.go b/internal/mock/tokenmock.go deleted file mode 100644 index a370342..0000000 --- a/internal/mock/tokenmock.go +++ /dev/null @@ -1,96 +0,0 @@ -package mock - -import ( - "fmt" - "strconv" - "time" - - "github.com/labstack/echo/v4" - - "github.com/briskt/keygo/app" -) - -type TokenService struct { - tokens map[string]app.Token // key is timestamp - - FindTokenFn func(ctx echo.Context, raw string) (app.Token, error) - UpdateTokenFn func(ctx echo.Context, id string, input app.TokenUpdateInput) error -} - -func NewTokenService() TokenService { - return TokenService{} -} - -func (m *TokenService) DeleteAllTokens() { - m.tokens = map[string]app.Token{} -} - -// Init preloads the mock "database" with tokens -func (m *TokenService) Init(fakeTokens []app.Token) { - m.tokens = make(map[string]app.Token, len(fakeTokens)) - for i := range fakeTokens { - m.tokens[fakeTokens[i].PlainText] = fakeTokens[i] - } -} - -func (m *TokenService) FindToken(ctx echo.Context, raw string) (app.Token, error) { - if m.FindTokenFn != nil { - return m.FindTokenFn(ctx, raw) - } - if t, ok := m.tokens[raw]; ok { - return t, nil - } - return app.Token{}, fmt.Errorf("token %s not found", raw) -} - -func (m *TokenService) CreateToken(ctx echo.Context, input app.TokenCreateInput) (app.Token, error) { - if err := input.Validate(); err != nil { - return app.Token{}, err - } - if m.tokens == nil { - m.tokens = make(map[string]app.Token) - } - mockRandomToken := strconv.Itoa(int(time.Now().Unix())) - newToken := app.Token{ - AuthID: input.AuthID, - UserID: input.UserID, - User: app.User{ID: input.UserID}, - PlainText: mockRandomToken, - ExpiresAt: input.ExpiresAt, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - } - m.tokens[mockRandomToken] = newToken - return newToken, nil -} - -func (m *TokenService) DeleteToken(ctx echo.Context, tokenID string) error { - panic("implement TokenService DeleteToken") -} - -func (m *TokenService) UpdateToken(ctx echo.Context, id string, input app.TokenUpdateInput) error { - if err := input.Validate(); err != nil { - return err - } - - if m.UpdateTokenFn != nil { - return m.UpdateTokenFn(ctx, id, input) - } - - t, ok := m.tokens[id] - if !ok { - return &app.Error{Code: app.ERR_NOTFOUND, Message: "Token not found"} - } - - if input.ExpiresAt != nil { - t.ExpiresAt = *input.ExpiresAt - } - if input.LastUsedAt != nil { - t.LastUsedAt = input.LastUsedAt - } - - now := time.Now() - t.UpdatedAt = now - m.tokens[id] = t - return nil -} diff --git a/internal/mock/usermock.go b/internal/mock/usermock.go deleted file mode 100644 index f4d2a72..0000000 --- a/internal/mock/usermock.go +++ /dev/null @@ -1,83 +0,0 @@ -package mock - -import ( - "time" - - "github.com/labstack/echo/v4" - - "github.com/briskt/keygo/app" -) - -type UserService struct { - users map[string]app.User - - FindUsersFn func(ctx echo.Context, filter app.UserFilter) ([]app.User, int, error) -} - -func NewUserService() UserService { - return UserService{ - users: map[string]app.User{}, - } -} - -func (m *UserService) DeleteAllUsers() { - m.users = map[string]app.User{} -} - -func (m *UserService) FindUserByID(context echo.Context, id string) (app.User, error) { - panic("implement UserService FindUserByID") -} - -func (m *UserService) FindUsers(context echo.Context, filter app.UserFilter) ([]app.User, int, error) { - if m.FindUsersFn != nil { - return m.FindUsersFn(context, filter) - } - var users []app.User - for _, u := range m.users { - if filter.Email != nil && *filter.Email != u.Email { - continue - } - // TODO: implement (or remove) other filter fields - - users = append(users, u) - } - return users, len(users), nil -} - -func (m *UserService) CreateUser(context echo.Context, input app.UserCreateInput) (app.User, error) { - if err := input.Validate(); err != nil { - return app.User{}, err - } - now := time.Now() - user := app.User{ - ID: newID(), - FirstName: input.FirstName, - LastName: input.LastName, - Email: input.Email, - AvatarURL: input.AvatarURL, - Role: input.Role, - CreatedAt: now, - UpdatedAt: now, - } - m.users[user.ID] = user - return user, nil -} - -func (m *UserService) UpdateUser(context echo.Context, id string, input app.UserUpdateInput) (app.User, error) { - if err := input.Validate(); err != nil { - return app.User{}, err - } - panic("implement UserService UpdateUser") -} - -func (m *UserService) DeleteUser(context echo.Context, id string) error { - panic("implement UserService DeleteUser") -} - -func (m *UserService) TouchLastLoginAt(context echo.Context, s string) error { - now := time.Now() - u := m.users[s] - u.LastLoginAt = &now - m.users[s] = u - return nil -} diff --git a/migrations/20211212181149_add_user_table.sql b/migrations/20211212181149_add_user_table.sql index ea89e6d..f5e466e 100644 --- a/migrations/20211212181149_add_user_table.sql +++ b/migrations/20211212181149_add_user_table.sql @@ -11,9 +11,11 @@ CREATE TABLE "users" ( created_at timestamp NOT NULL, updated_at timestamp NOT NULL, deleted timestamp, - PRIMARY KEY(id), - UNIQUE (email, deleted) + PRIMARY KEY(id) ); +CREATE UNIQUE INDEX “users_email_unique” + ON users(email) + WHERE deleted IS NULL; -- +goose StatementEnd -- +goose Down diff --git a/server/auth.go b/server/auth.go index 077f07c..b1f1379 100644 --- a/server/auth.go +++ b/server/auth.go @@ -15,6 +15,7 @@ import ( "github.com/labstack/echo/v4" "github.com/briskt/keygo/app" + "github.com/briskt/keygo/db" "github.com/briskt/keygo/server/oauth" ) @@ -128,7 +129,7 @@ func (s *Server) authLogout(c echo.Context) error { if err != nil { s.Logger.Error(err.Error()) } - if err := s.TokenService.DeleteToken(c, token.ID); err != nil { + if err := db.DeleteToken(c, token.ID); err != nil { s.Logger.Errorf("failed to delete user token: %s", err) } return c.Redirect(http.StatusTemporaryRedirect, DefaultUIPath) @@ -167,7 +168,7 @@ func (s *Server) authCallback(c echo.Context) error { return echo.NewHTTPError(http.StatusInternalServerError, AuthError{Error: err.Error()}) } - token, err := s.TokenService.CreateToken(c, app.TokenCreateInput{ + token, err := db.CreateToken(c, app.TokenCreateInput{ AuthID: profile.ID, UserID: user.ID, ExpiresAt: time.Now().Add(app.AuthTokenLifetime), @@ -178,7 +179,7 @@ func (s *Server) authCallback(c echo.Context) error { s.Logger.Infof("created token: %s", token.ID) - if err := s.UserService.TouchLastLoginAt(c, token.UserID); err != nil { + if err := db.TouchLastLoginAt(c, token.UserID); err != nil { return echo.NewHTTPError(http.StatusInternalServerError, AuthError{Error: err.Error()}) } @@ -206,12 +207,20 @@ func (s *Server) AuthnMiddleware(next echo.HandlerFunc) echo.HandlerFunc { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("getTokenFromSession: %s", err)) } + status := http.StatusUnauthorized + authError := AuthError{"not authorized"} + if token.ID == "" { - token, _ = s.TokenService.FindToken(c, getBearerToken(c)) + t, err := db.FindToken(c, getBearerToken(c)) + if err != nil { + return echo.NewHTTPError(status, authError) // TODO: should this be a different error? + } + token, err = db.ConvertToken(c, t) + if err != nil { + return echo.NewHTTPError(status, authError) // TODO: should this be a different error? + } } - status := http.StatusUnauthorized - authError := AuthError{"not authorized"} if token.ID == "" { return echo.NewHTTPError(status, authError) } @@ -222,7 +231,7 @@ func (s *Server) AuthnMiddleware(next echo.HandlerFunc) echo.HandlerFunc { now := time.Now() tokenExpiry := now.Add(app.AuthTokenLifetime) - if err := s.TokenService.UpdateToken(c, token.ID, app.TokenUpdateInput{ + if err := db.UpdateToken(c, token.ID, app.TokenUpdateInput{ ExpiresAt: &tokenExpiry, LastUsedAt: &now, }); err != nil { @@ -274,12 +283,12 @@ func (s *Server) getTokenFromSession(c echo.Context) (app.Token, error) { return app.Token{}, fmt.Errorf("token in session is not a string\n") } - token, err := s.TokenService.FindToken(c, tokenPlainText) + token, err := db.FindToken(c, tokenPlainText) if err != nil { return app.Token{}, fmt.Errorf("could not find token in DB: %w\n", err) } - return token, nil + return db.ConvertToken(c, token) } func env(key string, required bool) string { @@ -293,22 +302,22 @@ func env(key string, required bool) string { // TODO: move this to the app package func (s *Server) FindOrCreateUser(ctx echo.Context, email string) (app.User, error) { // Look up the user by email address. If no user can be found then create a new user - users, n, err := s.UserService.FindUsers(ctx, app.UserFilter{Email: &email}) + users, err := db.FindUsers(ctx, app.UserFilter{Email: &email}) if err != nil { return app.User{}, fmt.Errorf("error searching users by email: %w", err) } - if n > 1 { + if len(users) > 1 { err = errors.New("should only find a single user with a matching email address") return app.User{}, err } - if n == 1 { - return users[0], nil + if len(users) == 1 { + return db.ConvertUser(ctx, users[0]) } // user does not exist with the given email address -- create a new user - if user, err := s.UserService.CreateUser(ctx, app.UserCreateInput{Email: email}); err != nil { + if user, err := db.CreateUser(ctx, app.UserCreateInput{Email: email}); err != nil { return app.User{}, fmt.Errorf("failed to create a new user: %w", err) } else { - return user, nil + return db.ConvertUser(ctx, user) } } diff --git a/server/server.go b/server/server.go index 1b0c425..8887c72 100644 --- a/server/server.go +++ b/server/server.go @@ -8,13 +8,10 @@ import ( "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "gorm.io/gorm" - - "github.com/briskt/keygo/app" ) type Server struct { *echo.Echo - app.DataServices db *gorm.DB } @@ -24,12 +21,6 @@ var svr *Server type Option func(*Server) -func WithDataServices(services app.DataServices) Option { - return func(s *Server) { - s.DataServices = services - } -} - func WithDataBase(db *gorm.DB) Option { return func(s *Server) { s.db = db diff --git a/server/server_test.go b/server/server_test.go index 056e3af..77abcd3 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -1,6 +1,7 @@ package server_test import ( + "fmt" "net/http" "net/http/httptest" "strings" @@ -12,7 +13,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/briskt/keygo/app" - "github.com/briskt/keygo/internal/mock" + "github.com/briskt/keygo/db" "github.com/briskt/keygo/server" ) @@ -21,41 +22,27 @@ type TestSuite struct { suite.Suite *require.Assertions - server *server.Server - ctx echo.Context - mockTenantService *mock.TenantService // TODO: replace this with server.TenantService - mockTokenService *mock.TokenService - mockUserService *mock.UserService + server *server.Server + ctx echo.Context } // SetupTest runs before every test function func (ts *TestSuite) SetupTest() { ts.Assertions = require.New(ts.T()) - ts.server.TenantService.(*mock.TenantService).DeleteAllTenants() - ts.server.TokenService.(*mock.TokenService).DeleteAllTokens() - ts.server.UserService.(*mock.UserService).DeleteAllUsers() + + deleteAll(ts.ctx, &db.Tenant{}) + deleteAll(ts.ctx, &db.Token{}) + deleteAll(ts.ctx, &db.User{}) } func Test_RunSuite(t *testing.T) { - mockTenantService := mock.NewTenantService() - mockTokenService := mock.NewTokenService() - mockUserService := mock.NewUserService() - mockTokenService.UpdateTokenFn = func(ctx echo.Context, id string, input app.TokenUpdateInput) error { - return nil - } - - s := app.DataServices{ - TenantService: &mockTenantService, - TokenService: &mockTokenService, - UserService: &mockUserService, - } - svr := server.New(server.WithDataServices(s)) + db := db.OpenDB() + svr := server.New(server.WithDataBase(db)) + ctx := testContext() + ctx.Set(app.ContextKeyTx, db) suite.Run(t, &TestSuite{ - server: svr, - ctx: testContext(), - mockTenantService: &mockTenantService, - mockTokenService: &mockTokenService, - mockUserService: &mockUserService, + server: svr, + ctx: ctx, }) } @@ -71,7 +58,7 @@ func (ts *TestSuite) createUserFixture() Fixtures { Email: "test@example.com", Role: app.UserRoleAdmin, } - createdUser, err := ts.server.UserService.CreateUser(ts.ctx, fakeUserCreate) + createdUser, err := db.CreateUser(ts.ctx, fakeUserCreate) ts.NoError(err) fakeToken := app.Token{ @@ -81,13 +68,24 @@ func (ts *TestSuite) createUserFixture() Fixtures { Email: "test@example.com", Role: app.UserRoleAdmin, }, - PlainText: "12345", - ExpiresAt: time.Now().Add(time.Minute), + ExpiresAt: time.Now().Add(time.Hour * 24), + } + newToken, err := db.CreateToken(ts.ctx, app.TokenCreateInput{ + UserID: createdUser.ID, + AuthID: createdUser.ID, + ExpiresAt: fakeToken.ExpiresAt, + }) + ts.NoError(err) + if err != nil { + return Fixtures{} } - ts.server.TokenService.(*mock.TokenService).Init([]app.Token{fakeToken}) + fakeToken.PlainText = newToken.PlainText + + u, err := db.ConvertUser(ts.ctx, createdUser) + ts.NoError(err) return Fixtures{ - Users: []app.User{createdUser}, + Users: []app.User{u}, Tokens: []app.Token{fakeToken}, } } @@ -96,11 +94,14 @@ func (ts *TestSuite) createTenantFixture() Fixtures { fakeTenantCreate := app.TenantCreateInput{ Name: "Test Tenant", } - createdTenant, err := ts.server.TenantService.CreateTenant(ts.ctx, fakeTenantCreate) + createdTenant, err := db.CreateTenant(ts.ctx, fakeTenantCreate) + ts.NoError(err) + + t, err := db.ConvertTenant(ts.ctx, createdTenant) ts.NoError(err) return Fixtures{ - Tenants: []app.Tenant{createdTenant}, + Tenants: []app.Tenant{t}, } } @@ -109,3 +110,10 @@ type Fixtures struct { Tokens []app.Token Users []app.User } + +func deleteAll(c echo.Context, i any) { + result := db.Tx(c).Where("TRUE").Delete(i) + if result.Error != nil { + panic(fmt.Sprintf("failed to delete all %T: %s", i, result.Error)) + } +} diff --git a/server/tenant.go b/server/tenant.go index 46b5880..ee4b90c 100644 --- a/server/tenant.go +++ b/server/tenant.go @@ -6,6 +6,7 @@ import ( "github.com/labstack/echo/v4" "github.com/briskt/keygo/app" + "github.com/briskt/keygo/db" ) func (s *Server) tenantsCreateHandler(c echo.Context) error { @@ -20,7 +21,7 @@ func (s *Server) tenantsCreateHandler(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, "bad request") } - tenant, err := s.TenantService.CreateTenant(c, input) + tenant, err := db.CreateTenant(c, input) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, AuthError{Error: err.Error()}) } @@ -36,12 +37,12 @@ func (s *Server) tenantsListHandler(c echo.Context) error { return echo.NewHTTPError(http.StatusNotFound, AuthError{Error: "not found"}) } - tenants, n, err := s.TenantService.FindTenants(c, app.TenantFilter{}) + tenants, err := db.FindTenants(c, app.TenantFilter{}) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, AuthError{Error: err.Error()}) } - s.Logger.Infof("found %d tenants", n) + s.Logger.Infof("found %d tenants", len(tenants)) return c.JSON(http.StatusOK, tenants) } @@ -54,7 +55,7 @@ func (s *Server) tenantHandler(c echo.Context) error { } id := c.Param("id") - tenant, err := s.TenantService.FindTenantByID(c, id) + tenant, err := db.FindTenantByID(c, id) if err != nil { return echo.NewHTTPError(http.StatusNotFound, err) } diff --git a/server/tenant_test.go b/server/tenant_test.go index 3945073..50828c3 100644 --- a/server/tenant_test.go +++ b/server/tenant_test.go @@ -16,9 +16,6 @@ func (ts *TestSuite) Test_GetTenant() { token := f.Tokens[0] tenant := ts.createTenantFixture().Tenants[0] - ts.mockTokenService.FindTokenFn = func(_ echo.Context, raw string) (app.Token, error) { - return token, nil - } req := httptest.NewRequest(http.MethodGet, "/api/tenants/"+tenant.ID, nil) req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) req.Header.Set(echo.HeaderAuthorization, "Bearer "+token.PlainText) diff --git a/server/user.go b/server/user.go index 1aa39bf..485af84 100644 --- a/server/user.go +++ b/server/user.go @@ -6,6 +6,7 @@ import ( "github.com/labstack/echo/v4" "github.com/briskt/keygo/app" + "github.com/briskt/keygo/db" ) func (s *Server) usersListHandler(c echo.Context) error { @@ -14,12 +15,12 @@ func (s *Server) usersListHandler(c echo.Context) error { return echo.NewHTTPError(http.StatusOK, []app.User{}) } - users, n, err := s.UserService.FindUsers(c, app.UserFilter{}) + users, err := db.FindUsers(c, app.UserFilter{}) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, AuthError{Error: err.Error()}) } - s.Logger.Infof("found %d users", n) + s.Logger.Infof("found %d users", len(users)) return c.JSON(http.StatusOK, users) } diff --git a/server/user_test.go b/server/user_test.go index 6333649..48266aa 100644 --- a/server/user_test.go +++ b/server/user_test.go @@ -16,9 +16,6 @@ func (ts *TestSuite) Test_GetUser() { user := f.Users[0] token := f.Tokens[0] - ts.mockTokenService.FindTokenFn = func(_ echo.Context, raw string) (app.Token, error) { - return token, nil - } req := httptest.NewRequest(http.MethodGet, "/api/users/"+user.ID, nil) req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) req.Header.Set(echo.HeaderAuthorization, "Bearer "+token.PlainText) @@ -43,10 +40,6 @@ func (ts *TestSuite) Test_GetUserList() { user := f.Users[0] token := f.Tokens[0] - ts.mockUserService.FindUsersFn = func(_ echo.Context, _ app.UserFilter) ([]app.User, int, error) { - return f.Users, len(f.Users), nil - } - req := httptest.NewRequest(http.MethodGet, "/api/users", nil) req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) req.Header.Set(echo.HeaderAuthorization, "Bearer "+token.PlainText)