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
5 changes: 5 additions & 0 deletions config/config-local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ Database:
Authentication:
Key: DoWithLogic!@#

JWT:
Key: DoWithLogic!@#
Expired: 60
Label: XXXXX

Observability:
Enable: false
Mode: "otlp/http"
1 change: 0 additions & 1 deletion config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ Server:
Debug: true
TimeZone: "Asia/Jakarta"


Database:
Host: 127.0.0.1
Port: 3306
Expand Down
17 changes: 11 additions & 6 deletions internal/app/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,25 @@ import (
userV1 "github.com/DoWithLogic/golang-clean-architecture/internal/users/delivery/http/v1"
userRepository "github.com/DoWithLogic/golang-clean-architecture/internal/users/repository"
userUseCase "github.com/DoWithLogic/golang-clean-architecture/internal/users/usecase"
"github.com/DoWithLogic/golang-clean-architecture/pkg/app_crypto"
"github.com/DoWithLogic/golang-clean-architecture/pkg/app_jwt"
"github.com/labstack/echo/v4"
)

func (app *App) startService() error {
userRepo := userRepository.NewRepository(app.db)
userUC := userUseCase.NewUseCase(userRepo, app.cfg)
userCTRL := userV1.NewHandlers(userUC)

domain := app.echo.Group("/api/v1/users")
domain.GET("/ping", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello Word 👋")
})

userCTRL.UserRoutes(domain, app.cfg)
var (
crypto = app_crypto.NewCrypto(app.cfg.Authentication.Key)
appJwt = app_jwt.NewJWT(app.cfg.JWT)

userRepo = userRepository.NewRepository(app.db)
userUC = userUseCase.NewUseCase(userRepo, appJwt, crypto)
userCTRL = userV1.NewHandlers(userUC)
)

return nil
return userCTRL.UserRoutes(domain, app.cfg)
}
4 changes: 3 additions & 1 deletion internal/users/delivery/http/v1/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import (
"github.com/labstack/echo/v4"
)

func (h *handlers) UserRoutes(domain *echo.Group, cfg config.Config) {
func (h *handlers) UserRoutes(domain *echo.Group, cfg config.Config) error {
domain.POST("", h.CreateUser)
domain.POST("/login", h.Login)
domain.GET("/detail", h.UserDetail, middleware.JWTMiddleware(cfg))
domain.PATCH("/update", h.UpdateUser, middleware.JWTMiddleware(cfg))
domain.PUT("/update/status", h.UpdateUserStatus, middleware.JWTMiddleware(cfg))

return nil
}
6 changes: 2 additions & 4 deletions internal/users/entities/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package entities
import (
"time"

"github.com/DoWithLogic/golang-clean-architecture/config"
"github.com/DoWithLogic/golang-clean-architecture/internal/users/dtos"
"github.com/DoWithLogic/golang-clean-architecture/pkg/app_crypto"
"github.com/DoWithLogic/golang-clean-architecture/pkg/constant"
)

Expand All @@ -27,11 +25,11 @@ type (
}
)

func NewCreateUser(data dtos.CreateUserRequest, cfg config.Config) User {
func NewCreateUser(data dtos.CreateUserRequest) User {
return User{
Fullname: data.FullName,
Email: data.Email,
Password: app_crypto.NewCrypto(cfg.Authentication.Key).EncodeSHA256(data.Password),
Password: data.Password,
PhoneNumber: data.PhoneNumber,
UserType: constant.UserTypeRegular,
IsActive: true,
Expand Down
26 changes: 11 additions & 15 deletions internal/users/usecase/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,26 @@ import (
"strings"
"time"

"github.com/DoWithLogic/golang-clean-architecture/config"
"github.com/DoWithLogic/golang-clean-architecture/internal/middleware"
"github.com/DoWithLogic/golang-clean-architecture/internal/users"
"github.com/DoWithLogic/golang-clean-architecture/internal/users/dtos"
"github.com/DoWithLogic/golang-clean-architecture/internal/users/entities"
"github.com/DoWithLogic/golang-clean-architecture/pkg/app_crypto"
"github.com/DoWithLogic/golang-clean-architecture/pkg/app_jwt"
"github.com/DoWithLogic/golang-clean-architecture/pkg/apperror"
"github.com/golang-jwt/jwt"
)

type (
usecase struct {
repo users.Repository
cfg config.Config
repo users.Repository
appJwt *app_jwt.JWT
crypto *app_crypto.Crypto
}
)

func NewUseCase(repo users.Repository, cfg config.Config) users.Usecase {
return &usecase{repo, cfg}
func NewUseCase(repo users.Repository, appJwt *app_jwt.JWT, crypto *app_crypto.Crypto) users.Usecase {
return &usecase{repo, appJwt, crypto}
}

func (uc *usecase) Login(ctx context.Context, request dtos.UserLoginRequest) (response dtos.UserLoginResponse, err error) {
Expand All @@ -33,7 +34,7 @@ func (uc *usecase) Login(ctx context.Context, request dtos.UserLoginRequest) (re
return response, apperror.InternalServerError(err)
}

if !strings.EqualFold(dataLogin.Password, app_crypto.NewCrypto(uc.cfg.Authentication.Key).EncodeSHA256(request.Password)) {
if !strings.EqualFold(dataLogin.Password, uc.crypto.EncodeSHA256(request.Password)) {
return response, apperror.Unauthorized(apperror.ErrInvalidPassword)
}

Expand All @@ -47,27 +48,22 @@ func (uc *usecase) Login(ctx context.Context, request dtos.UserLoginRequest) (re
},
}

// Calculate the expiration time in seconds
expiresIn := claims.ExpiresAt - time.Now().Unix()

// Create token with claims
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

// Generate encoded token and send it as response.
tokenString, err := token.SignedString([]byte(uc.cfg.JWT.Key))
jwtToken, err := uc.appJwt.GenerateToken(ctx, claims)
if err != nil {
return response, apperror.InternalServerError(err)
}

return dtos.UserLoginResponse{AccessToken: tokenString, ExpiredAt: expiresIn}, nil
return dtos.UserLoginResponse{AccessToken: jwtToken, ExpiredAt: claims.ExpiresAt - time.Now().Unix()}, nil
}

func (uc *usecase) Create(ctx context.Context, payload dtos.CreateUserRequest) (userID int64, err error) {
if exist := uc.repo.IsUserExist(ctx, payload.Email); exist {
return userID, apperror.Conflict(apperror.ErrEmailAlreadyExist)
}

userID, err = uc.repo.SaveNewUser(ctx, entities.NewCreateUser(payload, uc.cfg))
payload.Password = uc.crypto.EncodeSHA256(payload.Password)
userID, err = uc.repo.SaveNewUser(ctx, entities.NewCreateUser(payload))
if err != nil {
return userID, apperror.InternalServerError(err)
}
Expand Down
55 changes: 35 additions & 20 deletions internal/users/usecase/usecase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@ import (
mocks "github.com/DoWithLogic/golang-clean-architecture/internal/users/mock"
"github.com/DoWithLogic/golang-clean-architecture/internal/users/usecase"
"github.com/DoWithLogic/golang-clean-architecture/pkg/app_crypto"
"github.com/DoWithLogic/golang-clean-architecture/pkg/app_jwt"
"github.com/DoWithLogic/golang-clean-architecture/pkg/apperror"
"github.com/DoWithLogic/golang-clean-architecture/pkg/constant"
"github.com/go-faker/faker/v4"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)

const (
KeyUnitTest = "DoWithLogic!@#"
)

func createUserMatcher(user entities.User) gomock.Matcher {
return eqUserMatcher{
users: user,
Expand Down Expand Up @@ -51,15 +56,14 @@ func Test_usecase_CreateUser(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

crypto := app_crypto.NewCrypto(KeyUnitTest)
appJwt := app_jwt.NewJWT(config.JWTConfig{Key: KeyUnitTest, Expired: 60, Label: "XXXX"})
ctx := context.Background()
repo := mocks.NewMockRepository(ctrl)
uc := usecase.NewUseCase(
repo,
config.Config{
Authentication: config.AuthenticationConfig{
Key: "DoWithLogic!@#",
},
},
appJwt,
crypto,
)

newUser := dtos.CreateUserRequest{
Expand Down Expand Up @@ -123,11 +127,14 @@ func Test_usecase_UpdateUserStatus(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

crypto := app_crypto.NewCrypto(KeyUnitTest)
appJwt := app_jwt.NewJWT(config.JWTConfig{Key: KeyUnitTest, Expired: 60, Label: "XXXX"})
ctx := context.Background()
repo := mocks.NewMockRepository(ctrl)
uc := usecase.NewUseCase(
repo,
config.Config{Authentication: config.AuthenticationConfig{Key: "secret-key"}},
appJwt,
crypto,
)

args := dtos.UpdateUserStatusRequest{
Expand Down Expand Up @@ -178,9 +185,15 @@ func Test_usecase_Detail(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

crypto := app_crypto.NewCrypto(KeyUnitTest)
appJwt := app_jwt.NewJWT(config.JWTConfig{Key: KeyUnitTest, Expired: 60, Label: "XXXX"})
ctx := context.Background()
repo := mocks.NewMockRepository(ctrl)
uc := usecase.NewUseCase(repo, config.Config{})
uc := usecase.NewUseCase(
repo,
appJwt,
crypto,
)

var id int64 = 1

Expand Down Expand Up @@ -219,20 +232,22 @@ func Test_usecase_Login(t *testing.T) {
var (
password = "testing"
email = "[email protected]"

config = config.Config{
Authentication: config.AuthenticationConfig{Key: "DoWithLogic!@#"},
}
)

crypto := app_crypto.NewCrypto(KeyUnitTest)
appJwt := app_jwt.NewJWT(config.JWTConfig{Key: KeyUnitTest, Expired: 60, Label: "XXXX"})
ctx := context.Background()
repo := mocks.NewMockRepository(ctrl)
uc := usecase.NewUseCase(repo, config)
uc := usecase.NewUseCase(
repo,
appJwt,
crypto,
)

returnedUser := entities.User{
UserID: 1,
Email: email,
Password: app_crypto.NewCrypto(config.Authentication.Key).EncodeSHA256(password),
Password: crypto.EncodeSHA256(password),
}

t.Run("login_positive", func(t *testing.T) {
Expand Down Expand Up @@ -276,16 +291,16 @@ func Test_usecase_PartialUpdate(t *testing.T) {
},
UserID: 1,
}

config = config.Config{
Authentication: config.AuthenticationConfig{
Key: "DoWithLogic!@#",
},
}
)

crypto := app_crypto.NewCrypto(KeyUnitTest)
appJwt := app_jwt.NewJWT(config.JWTConfig{Key: KeyUnitTest, Expired: 60, Label: "XXXX"})
repo := mocks.NewMockRepository(ctrl)
uc := usecase.NewUseCase(repo, config)
uc := usecase.NewUseCase(
repo,
appJwt,
crypto,
)

repo.EXPECT().Atomic(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)

Expand Down
21 changes: 21 additions & 0 deletions pkg/app_jwt/app_jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package app_jwt

import (
"context"

"github.com/DoWithLogic/golang-clean-architecture/config"
"github.com/DoWithLogic/golang-clean-architecture/internal/middleware"
"github.com/golang-jwt/jwt"
)

type JWT struct {
cfg config.JWTConfig
}

func NewJWT(cfg config.JWTConfig) *JWT {
return &JWT{cfg: cfg}
}

func (j *JWT) GenerateToken(ctx context.Context, request middleware.PayloadToken) (token string, err error) {
return jwt.NewWithClaims(jwt.SigningMethodHS256, request).SignedString([]byte(j.cfg.Key))
}
60 changes: 0 additions & 60 deletions pkg/utils/crypto.go

This file was deleted.

7 changes: 0 additions & 7 deletions pkg/utils/utils.go

This file was deleted.