diff --git a/.env.example b/.env.example
index 4045b22..0d75e08 100644
--- a/.env.example
+++ b/.env.example
@@ -1,14 +1,13 @@
APP_NAME=Gost - Go Fiber Project Starter
APP_IN_PRODUCTION=false
-APP_SECRET_KEY=RandomStringHere1234
APP_ACCESS_TOKEN_TTL=14320m
APP_PORT=9009
APP_TIME_ZONE=Asia/Jakarta
-DB_HOST=Host
+DB_HOST=db.secret.supabase.co
DB_PORT=5432
DB_USERNAME=postgres
-DB_PASSWORD=Password
+DB_PASSWORD=secret
DB_DATABASE=postgres
REDIS_URI=redis://user:@127.0.0.1:6379/1
@@ -18,6 +17,11 @@ PRIVATE_KEY=./keys/private.key
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
-SMTP_EMAIL=youremail@gmail.com
-SMTP_PASSWORD=passwd
-CLIENT_URL=https://important.client
\ No newline at end of file
+SMTP_EMAIL=secret@gmail.com
+SMTP_PASSWORD=secret
+CLIENT_URL=https://important.client
+
+# supabase storage
+SUPABASE_BUCKET_NAME=user_upload_public
+SUPABASE_URL=https://secret.supabase.co
+SUPABASE_TOKEN=SECRET
\ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..d9ee07b
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,51 @@
+name: Go Build
+
+on:
+ push:
+ branches:
+ - "*"
+ - "*/*"
+ pull_request:
+ branches:
+ - "*"
+ - "*/*"
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ go-version: ["1.19.x", "1.20.x", "1.21.x"]
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup Go ${{ matrix.go-version }}
+ uses: actions/setup-go@v3
+ with:
+ go-version: ${{ matrix.go-version }}
+
+ - name: Display Go version
+ run: go version
+
+ - name: Verify dependencies
+ run: go mod verify
+
+ - name: Create Keys Directory
+ run: mkdir -p keys
+ working-directory: ${{ github.workspace }}
+
+ - name: Generate Self-Signed TLS Certificate
+ run: |
+ openssl req -x509 -newkey rsa:4096 -keyout keys/private.key -out keys/publickey.crt -days 365 -nodes -subj "/CN=localhost"
+ working-directory: ${{ github.workspace }}
+
+ - name: Show Certificate
+ run: |
+ cat keys/publickey.crt
+ cat keys/private.key
+
+ - name: Create .env file
+ run: echo "${{ secrets.ENV }}" > .env
+
+ - name: Build
+ run: go build -o main main.go
diff --git a/.github/workflows/snyk.yml b/.github/workflows/security.yml
similarity index 65%
rename from .github/workflows/snyk.yml
rename to .github/workflows/security.yml
index 59230bb..150a0f4 100644
--- a/.github/workflows/snyk.yml
+++ b/.github/workflows/security.yml
@@ -1,21 +1,25 @@
-name: Go Snyk
+name: Go Security
on:
push:
branches:
- "*"
- "*/*"
- - "*/*/*"
pull_request:
branches:
- "*"
- "*/*"
- - "*/*/*"
jobs:
security:
runs-on: ubuntu-latest
steps:
+ - name: Fetch Repository
+ uses: actions/checkout@v2
+
+ - name: Run GoSec Security Scanner
+ uses: securego/gosec@master
+
- uses: actions/checkout@master
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/golang@master
diff --git a/.github/workflows/builder.yml b/.github/workflows/test.yml
similarity index 67%
rename from .github/workflows/builder.yml
rename to .github/workflows/test.yml
index 25c7161..534237d 100644
--- a/.github/workflows/builder.yml
+++ b/.github/workflows/test.yml
@@ -1,4 +1,4 @@
-name: Go
+name: Go Test
on:
push:
@@ -11,7 +11,7 @@ on:
- "*/*"
jobs:
- build:
+ test:
runs-on: ubuntu-latest
services:
@@ -27,7 +27,7 @@ jobs:
strategy:
matrix:
- go-version: ["1.18.x", "1.19.x", "1.20.x", "1.21.x"]
+ go-version: ["1.20.x"]
steps:
- uses: actions/checkout@v3
@@ -39,12 +39,6 @@ jobs:
- name: Display Go version
run: go version
- - name: Install golint
- run: go install golang.org/x/lint/golint@latest
-
- - name: Run golint
- run: golint ./...
-
- name: Verify dependencies
run: go mod verify
@@ -62,11 +56,18 @@ jobs:
cat keys/publickey.crt
cat keys/private.key
- - name: Copy env
- run: cp .env.example .env
+ - name: Create .env file
+ run: echo "${{ secrets.ENV }}" > .env
+
+ - name: Go Get
+ run: |
+ go get ./...
- - name: Test
- run: go test -race -timeout 20s ./internal/...
+ - name: Run migration
+ run: |
+ go run database/migration/main.go
- - name: Build
- run: go build -o main main.go
+ - name: Run Test
+ run: |
+ go test -p 1 -timeout 600s ./application/...
+ go test -race -timeout 600s ./controller/... ./database/... ./domain/.. ./internal/... ./repository/... ./service/...
diff --git a/.gitignore b/.gitignore
index 35c39c5..e4f7754 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,10 @@
.env
+.env.test
/log
/keys
cover.out
cover.html
-/.cover
\ No newline at end of file
+/.cover
+.dockerignore
+docker-compose.yml
+Dockerfile
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e6631fd
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Lukman Ernandi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
index 942b6b9..0f0c337 100644
--- a/Makefile
+++ b/Makefile
@@ -1,45 +1,31 @@
-test-clear:
- go clean -testcache
-
-test:
- go test -p 1 -timeout 240s -coverprofile=cover.out ./...
-
-test-per-dir:
- go test -p 1 -timeout 120s ./application/...
- go test -p 1 -timeout 120s ./controller/...
- go test -p 1 -timeout 120s ./database/...
- go test -p 1 -timeout 120s ./domain/...
- go test -p 1 -timeout 120s ./internal/...
- go test -p 1 -timeout 120s ./repository/...
- go test -p 1 -timeout 120s ./service/...
- go test -p 1 -timeout 120s ./tests/...
-
-# without ./application and ./tests
-# go test -race -timeout 200s ./controller/... ./database/... ./internal/... ./repository/... ./service/...
-test-race:
- go clean -testcache
- go test -race -timeout 120s ./controller/...
- go test -race -timeout 120s ./database/...
- go test -race -timeout 120s ./domain/...
- go test -race -timeout 120s ./internal/...
- go test -race -timeout 120s ./repository/...
- go test -race -timeout 120s ./service/...
-
migrate:
go run database/migration/main.go
run:
go run .
-test-report:
+test-clear:
+ go clean -testcache
+
+test:
+ go clean -testcache
+ go test -p 1 -timeout 330s -coverprofile=cover.out ./...
go tool cover -html cover.out -o cover.html
+ del cover.out
+
+test-race:
+ go clean -testcache
+ go test -race -timeout 200s ./controller/... ./database/... ./internal/... ./repository/... ./service/...
migrate-test-report:
go run database/migration/main.go
timeout 5
- go test -p 1 -timeout 240s -coverprofile=cover.out ./...
+ go clean -testcache
+ go test -race -timeout 200s ./controller/... ./database/... ./internal/... ./repository/... ./service/...
+ go test -p 1 -timeout 330s -coverprofile=cover.out ./...
go tool cover -html cover.out -o cover.html
+ del cover.out
-# !!windows only!! run redis in background
+# windowsOS only
st-redis:
redis-server.exe --service-start
\ No newline at end of file
diff --git a/README.md b/README.md
index df341b7..e443402 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,42 @@
-## Gost Project
+
Gost: Go Starter
-Go-Fiber project starter, with jwt-auth and highly effective RBAC implementation.
+
+
+Golang project starter with Fiber Framework, jwt-auth and highly effective bit-manipulation RBAC implementation to build a robust RestAPI Application.
+
+
+
+## :rocket: Technologies And :wrench: Tools
+
+Techs and tools were used in this project:
+
+- [Go](https://go.dev)
+- [Fiber Framework](https://docs.gofiber.io/) → Framework for routing & HTTP handler.
+- [GORM](https://gorm.io/) → Database logics & queries.
+- [Supabase Services: PostgreSQL & Bucket](https://www.supabase.com) → Free database & storage (bucket).
+- [Github CLI](https://cli.github.com/) → Github management for repository's secrets & etc.
+- [Github Action](https://github.com/features/actions) → Automated testing and building across multiple versions of Go.
+- [Snyk](https://app.snyk.io/) → Dependency scanning.
+- [SonarLint, VSCode ext.](https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarlint-vscode) → Detects & highlights issues that can lead to bugs & vulnerabilities.
+- [GoLint](https://github.com/golang/lint) → CLI static code analytic for code-styling & many more.
+
+
+
+## :closed_book: Read List
+
+- [Database Connection Configuration](https://www.alexedwards.net/blog/configuring-sqldb)
+- [Go-Fiber Testing](https://dev.to/koddr/go-fiber-by-examples-testing-the-application-1ldf)
+- [Production Checklist 1](https://aleksei-kornev.medium.com/production-readiness-checklist-for-backend-applications-8d2b0c57ccec/)
+- [Production Checklist 2](https://github.com/gorrion-io/production-readiness-checklist/)
+- [Production Checklist 3](https://www.cockroachlabs.com/docs/cockroachcloud/production-checklist/)
+- [Deployment Checklist](https://last9.io/blog/deployment-readiness-checklists/)
+- [CI with Github Actions](https://www.alexedwards.net/blog/ci-with-go-and-github-actions)
+- [RestAPI Security Checklist](https://roadmap.sh/best-practices/api-security/)
+
+
+
+## :memo: License
+
+This project is under license from MIT. For more details, see the [LICENSE](LICENSE) file.
+
+
diff --git a/application/app.go b/application/app.go
index ac25798..7ca4f30 100644
--- a/application/app.go
+++ b/application/app.go
@@ -1,3 +1,11 @@
+// 📌 Origin Github Repository: https://github.com/Lukmanerngost
+
+// 🔍 README
+// Application package configures middleware, error management, and
+// handles OS signals for gracefully stopping the server when receiving
+// an interrupt signal. This package provides routes related to user
+// management and role-based access control (RBAC). And so on.
+
package application
import (
@@ -17,6 +25,8 @@ import (
)
var (
+ port int
+
// Create a new fiber instance with custom config
router = fiber.New(fiber.Config{
// Override default error handler
@@ -24,7 +34,8 @@ var (
// Status code defaults to 500
code := fiber.StatusInternalServerError
- // Retrieve the custom status code if it's a *fiber.Error
+ // Retrieve the custom status code
+ // if it's a *fiber.Error
var e *fiber.Error
if errors.As(err, &e) {
code = e.Code
@@ -35,14 +46,14 @@ var (
"message": e.Message,
})
if err != nil {
- // In case the SendFile fails
- return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
+ return ctx.Status(fiber.StatusInternalServerError).
+ SendString("Internal Server Error")
}
-
- // Return from handler
return nil
},
- ReadBufferSize: 12000,
+ // memory management
+ // ReduceMemoryUsage: true,
+ // ReadBufferSize: 5120,
})
)
@@ -50,15 +61,15 @@ func setup() {
// Check env and database
env.ReadConfig("./.env")
config := env.Configuration()
- dbURI := config.GetDatabaseURI()
privKey := config.GetPrivateKey()
pubKey := config.GetPublicKey()
- if dbURI == "" || privKey == nil || pubKey == nil {
- log.Fatal("Database URI or keys aren't valid")
+ if privKey == nil || pubKey == nil {
+ log.Fatal("private and public keys are not valid or not found")
}
+ port = config.AppPort
connector.LoadDatabase()
- connector.LoadRedisDatabase()
+ connector.LoadRedisCache()
}
func RunApp() {
@@ -69,7 +80,8 @@ func RunApp() {
router.Use(logger.New())
// Custom File Writer
_ = os.MkdirAll("./log", os.ModePerm)
- file, err := os.OpenFile(fmt.Sprintf("./log/%s.log", time.Now().Format("20060102")), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
+ fileName := fmt.Sprintf("./log/%s.log", time.Now().Format("20060102"))
+ file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Fatalf("error opening file: %v", err)
}
@@ -96,13 +108,12 @@ func RunApp() {
close(idleConnsClosed)
}()
- getDevopmentRouter(router) // don't use for production
- getUserManagementRoutes(router) // don't use for production
-
- getUserRoutes(router)
- getRbacRoutes(router)
+ getUserManagementRoutes(router) // user CRUD without auth ⚠️
+ getDevopmentRouter(router) // experimental without auth ⚠️
+ getUserRoutes(router) // user with auth
+ getRolePermissionRoutes(router) // RBAC CRUD with auth
- if err := router.Listen(fmt.Sprintf(":%d", 9009)); err != nil {
+ if err := router.Listen(fmt.Sprintf(":%d", port)); err != nil {
log.Printf("Oops... Server is not running! Reason: %v", err)
}
diff --git a/application/app_test.go b/application/app_test.go
index 6b41f1a..a90bdca 100644
--- a/application/app_test.go
+++ b/application/app_test.go
@@ -21,13 +21,13 @@ var (
timeNow time.Time
userRepo repository.UserRepository
ctx context.Context
- appUrl string
+ appURL string
)
func init() {
env.ReadConfig("./../.env")
c := env.Configuration()
- appUrl = c.AppUrl
+ appURL = c.AppURL
jwtHandler = middleware.NewJWTHandler()
timeNow = time.Now()
@@ -35,7 +35,7 @@ func init() {
ctx = context.Background()
}
-func Test_RunApp(t *testing.T) {
+func TestRunApp(t *testing.T) {
defer func() {
r := recover()
if r != nil {
@@ -47,7 +47,7 @@ func Test_RunApp(t *testing.T) {
time.Sleep(3 * time.Second)
}
-func Test_app_router(t *testing.T) {
+func TestAppRouter(t *testing.T) {
defer func() {
r := recover()
if r != nil {
@@ -90,7 +90,7 @@ func TestRoutes(t *testing.T) {
})
t.Run("getRBACAuthRoutes", func(t *testing.T) {
- getRbacRoutes(router)
+ getRolePermissionRoutes(router)
})
t.Run("getUserAuthRoutes", func(t *testing.T) {
diff --git a/application/development_router.go b/application/development_router.go
index 338c0d0..cc6b89e 100644
--- a/application/development_router.go
+++ b/application/development_router.go
@@ -1,6 +1,11 @@
-// don't use this for production
-// use this file just for testing
-// and testing management.
+// 📌 Origin Github Repository: https://github.com/Lukmanerngost
+
+// 🔍 README
+// Development Routes provides experimental/ developing/ testing
+// for routes, middleware, connection and many more without JWT
+// authentication in header. ⚠️ So, don't forget to commented
+// on the line of code that routes getDevopmentRouter
+// in the app.go file.
package application
@@ -8,7 +13,6 @@ import (
"github.com/gofiber/fiber/v2"
controller "github.com/Lukmanern/gost/controller/development"
- "github.com/Lukmanern/gost/internal/middleware"
)
var (
@@ -16,7 +20,6 @@ var (
)
func getDevopmentRouter(router fiber.Router) {
- jwtHandler := middleware.NewJWTHandler()
devController = controller.NewDevControllerImpl()
// Developement 'helper' Process
devRouter := router.Group("development")
@@ -31,9 +34,10 @@ func getDevopmentRouter(router fiber.Router) {
// you should create new role named new-role-001 and new permission
// named new-permission-001 from RBAC-endpoints to test these endpoints
- devRouterAuth := devRouter.Use(jwtHandler.IsAuthenticated)
- devRouterAuth.Get("test-new-role",
- jwtHandler.CheckHasRole("new-role-001"), devController.CheckNewRole)
- devRouterAuth.Get("test-new-permission",
- jwtHandler.CheckHasPermission(21), devController.CheckNewPermission)
+ // jwtHandler := middleware.NewJWTHandler()
+ // devRouterAuth := devRouter.Use(jwtHandler.IsAuthenticated)
+ // devRouterAuth.Get("test-new-role",
+ // jwtHandler.CheckHasRole("new-role-001"), devController.CheckNewRole)
+ // devRouterAuth.Get("test-new-permission",
+ // jwtHandler.CheckHasPermission(21), devController.CheckNewPermission)
}
diff --git a/application/rbac_router.go b/application/role_permission_router.go
similarity index 71%
rename from application/rbac_router.go
rename to application/role_permission_router.go
index 21b412f..168df60 100644
--- a/application/rbac_router.go
+++ b/application/role_permission_router.go
@@ -1,3 +1,10 @@
+// 📌 Origin Github Repository: https://github.com/Lukmanerngost
+
+// 🔍 README
+// Role-Permission Routes provides des create, read (get & getAll), update, and
+// delete functionalities for Role and Permission entities including connecting
+// both of them. This routes can be access by user that has admin-role (see database/migration).
+
package application
import (
@@ -8,21 +15,22 @@ import (
permCtr "github.com/Lukmanern/gost/controller/permission"
roleCtr "github.com/Lukmanern/gost/controller/role"
- service "github.com/Lukmanern/gost/service/rbac"
+ permSvc "github.com/Lukmanern/gost/service/permission"
+ roleSvc "github.com/Lukmanern/gost/service/role"
)
var (
- roleService service.RoleService
+ roleService roleSvc.RoleService
roleController roleCtr.RoleController
- permissionService service.PermissionService
+ permissionService permSvc.PermissionService
permissionController permCtr.PermissionController
)
-func getRbacRoutes(router fiber.Router) {
+func getRolePermissionRoutes(router fiber.Router) {
jwtHandler := middleware.NewJWTHandler()
- permissionService = service.NewPermissionService()
+ permissionService = permSvc.NewPermissionService()
permissionController = permCtr.NewPermissionController(permissionService)
permissionRouter := router.Group("permission").Use(jwtHandler.IsAuthenticated)
@@ -33,7 +41,7 @@ func getRbacRoutes(router fiber.Router) {
permissionRouter.Put(":id", jwtHandler.CheckHasPermission(rbac.PermUpdatePermission.ID), permissionController.Update)
permissionRouter.Delete(":id", jwtHandler.CheckHasPermission(rbac.PermDeletePermission.ID), permissionController.Delete)
- roleService = service.NewRoleService(permissionService)
+ roleService = roleSvc.NewRoleService(permissionService)
roleController = roleCtr.NewRoleController(roleService)
roleRouter := router.Group("role").Use(jwtHandler.IsAuthenticated)
diff --git a/application/user_management_router.go b/application/user_management_router.go
index ae2d965..9110d57 100644
--- a/application/user_management_router.go
+++ b/application/user_management_router.go
@@ -1,6 +1,10 @@
-// don't use this for production
-// use this file just for testing
-// and testing management.
+// 📌 Origin Github Repository: https://github.com/Lukmanerngost
+
+// 🔍 README
+// User Management Routes provides create, read (get & getAll), update, and
+// delete functionalities for user data management without JWT authentication
+// in header. ⚠️ So, don't forget to commented on the line of code that routes
+// getUserManagementRoutes in the app.go file.
package application
diff --git a/application/user_router.go b/application/user_router.go
index 787c886..66bc972 100644
--- a/application/user_router.go
+++ b/application/user_router.go
@@ -1,3 +1,11 @@
+// 📌 Origin Github Repository: https://github.com/Lukmanerngost
+
+// 🔍 README
+// User Routes provides some features and action that user can use.
+// User Routes provide the typical web application authentication flow,
+// such as registration, sending verification codes, and verifying accounts
+// with a verification code.
+
package application
import (
@@ -8,19 +16,20 @@ import (
controller "github.com/Lukmanern/gost/controller/user"
service "github.com/Lukmanern/gost/service/user"
- rbacService "github.com/Lukmanern/gost/service/rbac"
+ permSvc "github.com/Lukmanern/gost/service/permission"
+ roleSvc "github.com/Lukmanern/gost/service/role"
)
var (
- userPermService rbacService.PermissionService
- userRoleService rbacService.RoleService
+ userPermService permSvc.PermissionService
+ userRoleService roleSvc.RoleService
userService service.UserService
userController controller.UserController
)
func getUserRoutes(router fiber.Router) {
- userPermService = rbacService.NewPermissionService()
- userRoleService = rbacService.NewRoleService(userPermService)
+ userPermService = permSvc.NewPermissionService()
+ userRoleService = roleSvc.NewRoleService(userPermService)
userService = service.NewUserService(userRoleService)
userController = controller.NewUserController(userService)
jwtHandler := middleware.NewJWTHandler()
diff --git a/controller/development/dev_controller.go b/controller/development/dev_controller.go
index beef701..602bc85 100644
--- a/controller/development/dev_controller.go
+++ b/controller/development/dev_controller.go
@@ -11,26 +11,51 @@ import (
"gorm.io/gorm"
"github.com/Lukmanern/gost/database/connector"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/response"
fileService "github.com/Lukmanern/gost/service/file"
)
type DevController interface {
+ // PingDatabase func Ping database 5 times
PingDatabase(c *fiber.Ctx) error
+
+ // PingRedis func Ping redis 5 times
PingRedis(c *fiber.Ctx) error
+
+ // Panic func handles panic with defer func
Panic(c *fiber.Ctx) error
+
+ // StoringToRedis func stores data{key:value} to redis
StoringToRedis(c *fiber.Ctx) error
+
+ // GetFromRedis func gets data from redis
GetFromRedis(c *fiber.Ctx) error
+
+ // CheckNewRole func gives result for checking
+ // middleware for new role
CheckNewRole(c *fiber.Ctx) error
+
+ // CheckNewPermission func gives result for
+ // checking middleware for new permission
CheckNewPermission(c *fiber.Ctx) error
+
+ // UploadFile func uploads a new file into Supabase Bucket
+ // See : https://supabase.com/docs/guides/storage
UploadFile(c *fiber.Ctx) error
+
+ // RemoveFile func removes file from Supabase Bucket
+ // See : https://supabase.com/docs/guides/storage
RemoveFile(c *fiber.Ctx) error
+
+ // GetFilesList func gets list file/s from Supabase Bucket
+ // See : https://supabase.com/docs/guides/storage
GetFilesList(c *fiber.Ctx) error
}
type DevControllerImpl struct {
- fileSvc fileService.UploadFile
+ fileSvc fileService.FileService
redis *redis.Client
db *gorm.DB
}
@@ -44,7 +69,7 @@ func NewDevControllerImpl() DevController {
devImplOnce.Do(func() {
devImpl = &DevControllerImpl{
fileSvc: fileService.NewFileService(),
- redis: connector.LoadRedisDatabase(),
+ redis: connector.LoadRedisCache(),
db: connector.LoadDatabase(),
}
})
@@ -52,7 +77,7 @@ func NewDevControllerImpl() DevController {
return devImpl
}
-func (ctr DevControllerImpl) PingDatabase(c *fiber.Ctx) error {
+func (ctr *DevControllerImpl) PingDatabase(c *fiber.Ctx) error {
db := ctr.db
if db == nil {
return response.Error(c, "failed db is nil")
@@ -71,10 +96,10 @@ func (ctr DevControllerImpl) PingDatabase(c *fiber.Ctx) error {
return response.CreateResponse(c, fiber.StatusOK, true, "success ping-sql-db", nil)
}
-func (ctr DevControllerImpl) PingRedis(c *fiber.Ctx) error {
+func (ctr *DevControllerImpl) PingRedis(c *fiber.Ctx) error {
redis := ctr.redis
if redis == nil {
- return response.Error(c, "redis nil value")
+ return response.Error(c, constants.RedisNil)
}
for i := 0; i < 5; i++ {
status := redis.Ping()
@@ -86,7 +111,7 @@ func (ctr DevControllerImpl) PingRedis(c *fiber.Ctx) error {
return response.CreateResponse(c, fiber.StatusOK, true, "success ping-redis", nil)
}
-func (ctr DevControllerImpl) Panic(c *fiber.Ctx) error {
+func (ctr *DevControllerImpl) Panic(c *fiber.Ctx) error {
defer func() error {
r := recover()
if r != nil {
@@ -95,13 +120,13 @@ func (ctr DevControllerImpl) Panic(c *fiber.Ctx) error {
}
return nil
}()
- panic("Panic message") // message should string
+ panic("your panic message") // should string
}
-func (ctr DevControllerImpl) StoringToRedis(c *fiber.Ctx) error {
+func (ctr *DevControllerImpl) StoringToRedis(c *fiber.Ctx) error {
redis := ctr.redis
if redis == nil {
- return response.Error(c, "redis nil value")
+ return response.Error(c, constants.RedisNil)
}
redisStatus := redis.Set("example-key", "example-value", 50*time.Minute)
if redisStatus.Err() != nil {
@@ -112,10 +137,10 @@ func (ctr DevControllerImpl) StoringToRedis(c *fiber.Ctx) error {
return response.SuccessCreated(c, nil)
}
-func (ctr DevControllerImpl) GetFromRedis(c *fiber.Ctx) error {
+func (ctr *DevControllerImpl) GetFromRedis(c *fiber.Ctx) error {
redis := ctr.redis
if redis == nil {
- return response.Error(c, "redis nil value")
+ return response.Error(c, constants.RedisNil)
}
redisStatus := redis.Get("example-key")
if redisStatus.Err() != nil {
@@ -131,15 +156,15 @@ func (ctr DevControllerImpl) GetFromRedis(c *fiber.Ctx) error {
return response.SuccessLoaded(c, res)
}
-func (ctr DevControllerImpl) CheckNewRole(c *fiber.Ctx) error {
+func (ctr *DevControllerImpl) CheckNewRole(c *fiber.Ctx) error {
return response.CreateResponse(c, fiber.StatusOK, true, "success check new role", nil)
}
-func (ctr DevControllerImpl) CheckNewPermission(c *fiber.Ctx) error {
+func (ctr *DevControllerImpl) CheckNewPermission(c *fiber.Ctx) error {
return response.CreateResponse(c, fiber.StatusOK, true, "success check new permission", nil)
}
-func (ctr DevControllerImpl) UploadFile(c *fiber.Ctx) error {
+func (ctr *DevControllerImpl) UploadFile(c *fiber.Ctx) error {
file, err := c.FormFile("file")
if err != nil {
return response.BadRequest(c, "failed to parse form file: "+err.Error())
@@ -147,7 +172,7 @@ func (ctr DevControllerImpl) UploadFile(c *fiber.Ctx) error {
if file == nil {
return response.BadRequest(c, "file is nil or not found")
}
- mimeType := file.Header.Get("Content-Type")
+ mimeType := file.Header.Get(fiber.HeaderContentType)
if mimeType != "application/pdf" {
return response.BadRequest(c, "only PDF file are allowed for upload")
}
@@ -156,29 +181,29 @@ func (ctr DevControllerImpl) UploadFile(c *fiber.Ctx) error {
return response.BadRequest(c, "file size exceeds the maximum allowed (3MB)")
}
- fileUrl, uploadErr := ctr.fileSvc.UploadFile(file)
+ fileURL, uploadErr := ctr.fileSvc.UploadFile(file)
if uploadErr != nil {
fiberErr, ok := uploadErr.(*fiber.Error)
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+uploadErr.Error())
+ return response.Error(c, constants.ServerErr+uploadErr.Error())
}
return response.SuccessCreated(c, map[string]any{
- "file_url": fileUrl,
+ "file_url": fileURL,
})
}
-func (ctr DevControllerImpl) RemoveFile(c *fiber.Ctx) error {
+func (ctr *DevControllerImpl) RemoveFile(c *fiber.Ctx) error {
var fileName struct {
FileName string `validate:"required,min=4,max=150" json:"file_name"`
}
if err := c.BodyParser(&fileName); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
validate := validator.New()
if err := validate.Struct(&fileName); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
removeErr := ctr.fileSvc.RemoveFile(fileName.FileName)
@@ -187,19 +212,19 @@ func (ctr DevControllerImpl) RemoveFile(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+removeErr.Error())
+ return response.Error(c, constants.ServerErr+removeErr.Error())
}
return response.SuccessNoContent(c)
}
-func (ctr DevControllerImpl) GetFilesList(c *fiber.Ctx) error {
+func (ctr *DevControllerImpl) GetFilesList(c *fiber.Ctx) error {
resp, getErr := ctr.fileSvc.GetFilesList()
if getErr != nil {
fiberErr, ok := getErr.(*fiber.Error)
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+getErr.Error())
+ return response.Error(c, constants.ServerErr+getErr.Error())
}
return response.SuccessLoaded(c, resp)
}
diff --git a/controller/development/dev_controller_test.go b/controller/development/dev_controller_test.go
index debb596..ea08ea7 100644
--- a/controller/development/dev_controller_test.go
+++ b/controller/development/dev_controller_test.go
@@ -21,7 +21,7 @@ func init() {
env.ReadConfig("./../../.env")
connector.LoadDatabase()
- connector.LoadRedisDatabase()
+ connector.LoadRedisCache()
}
func TestNewDevControllerImpl(t *testing.T) {
@@ -67,7 +67,7 @@ func TestNewDevControllerImpl(t *testing.T) {
}
}
-func Test_Methods(t *testing.T) {
+func TestMethods(t *testing.T) {
c := helper.NewFiberCtx()
ctr := NewDevControllerImpl()
if ctr == nil || c == nil {
diff --git a/controller/permission/permission_controller.go b/controller/permission/permission_controller.go
index 8a985bf..1a54d0c 100644
--- a/controller/permission/permission_controller.go
+++ b/controller/permission/permission_controller.go
@@ -9,15 +9,25 @@ import (
"github.com/Lukmanern/gost/domain/base"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/response"
- service "github.com/Lukmanern/gost/service/rbac"
+ service "github.com/Lukmanern/gost/service/permission"
)
type PermissionController interface {
+ // Create func creates a new permission
Create(c *fiber.Ctx) error
+
+ // Get func gets a permission
Get(c *fiber.Ctx) error
+
+ // GetAll func gets some permissions
GetAll(c *fiber.Ctx) error
+
+ // Update func updates a permission
Update(c *fiber.Ctx) error
+
+ // Delete func deletes a permission
Delete(c *fiber.Ctx) error
}
@@ -39,14 +49,14 @@ func NewPermissionController(service service.PermissionService) PermissionContro
return permissionControllerImpl
}
-func (ctr PermissionControllerImpl) Create(c *fiber.Ctx) error {
+func (ctr *PermissionControllerImpl) Create(c *fiber.Ctx) error {
var permission model.PermissionCreate
if err := c.BodyParser(&permission); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
validate := validator.New()
if err := validate.Struct(&permission); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
@@ -56,7 +66,7 @@ func (ctr PermissionControllerImpl) Create(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+createErr.Error())
+ return response.Error(c, constants.ServerErr+createErr.Error())
}
data := map[string]any{
"id": id,
@@ -64,10 +74,10 @@ func (ctr PermissionControllerImpl) Create(c *fiber.Ctx) error {
return response.SuccessCreated(c, data)
}
-func (ctr PermissionControllerImpl) Get(c *fiber.Ctx) error {
+func (ctr *PermissionControllerImpl) Get(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
- return response.BadRequest(c, "invalid id")
+ return response.BadRequest(c, constants.InvalidID)
}
ctx := c.Context()
@@ -77,12 +87,12 @@ func (ctr PermissionControllerImpl) Get(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+getErr.Error())
+ return response.Error(c, constants.ServerErr+getErr.Error())
}
return response.SuccessLoaded(c, permission)
}
-func (ctr PermissionControllerImpl) GetAll(c *fiber.Ctx) error {
+func (ctr *PermissionControllerImpl) GetAll(c *fiber.Ctx) error {
request := base.RequestGetAll{
Page: c.QueryInt("page", 1),
Limit: c.QueryInt("limit", 20),
@@ -96,7 +106,7 @@ func (ctr PermissionControllerImpl) GetAll(c *fiber.Ctx) error {
ctx := c.Context()
permissions, total, getErr := ctr.service.GetAll(ctx, request)
if getErr != nil {
- return response.Error(c, "internal server error: "+getErr.Error())
+ return response.Error(c, constants.ServerErr+getErr.Error())
}
data := make([]interface{}, len(permissions))
@@ -114,19 +124,19 @@ func (ctr PermissionControllerImpl) GetAll(c *fiber.Ctx) error {
return response.SuccessLoaded(c, responseData)
}
-func (ctr PermissionControllerImpl) Update(c *fiber.Ctx) error {
+func (ctr *PermissionControllerImpl) Update(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
- return response.BadRequest(c, "invalid id")
+ return response.BadRequest(c, constants.InvalidID)
}
var permission model.PermissionUpdate
permission.ID = id
if err := c.BodyParser(&permission); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
validate := validator.New()
if err := validate.Struct(&permission); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
@@ -136,15 +146,15 @@ func (ctr PermissionControllerImpl) Update(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+updateErr.Error())
+ return response.Error(c, constants.ServerErr+updateErr.Error())
}
return response.SuccessNoContent(c)
}
-func (ctr PermissionControllerImpl) Delete(c *fiber.Ctx) error {
+func (ctr *PermissionControllerImpl) Delete(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
- return response.BadRequest(c, "invalid id")
+ return response.BadRequest(c, constants.InvalidID)
}
ctx := c.Context()
@@ -154,7 +164,7 @@ func (ctr PermissionControllerImpl) Delete(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+deleteErr.Error())
+ return response.Error(c, constants.ServerErr+deleteErr.Error())
}
return response.SuccessNoContent(c)
}
diff --git a/controller/permission/permission_controller_test.go b/controller/permission/permission_controller_test.go
index d0ed666..285fa0c 100644
--- a/controller/permission/permission_controller_test.go
+++ b/controller/permission/permission_controller_test.go
@@ -20,6 +20,7 @@ import (
"github.com/Lukmanern/gost/database/connector"
"github.com/Lukmanern/gost/domain/base"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/env"
"github.com/Lukmanern/gost/internal/helper"
"github.com/Lukmanern/gost/internal/middleware"
@@ -27,7 +28,8 @@ import (
userController "github.com/Lukmanern/gost/controller/user"
userRepository "github.com/Lukmanern/gost/repository/user"
- service "github.com/Lukmanern/gost/service/rbac"
+ service "github.com/Lukmanern/gost/service/permission"
+ roleService "github.com/Lukmanern/gost/service/role"
userService "github.com/Lukmanern/gost/service/user"
)
@@ -35,37 +37,37 @@ var (
userRepo userRepository.UserRepository
permService service.PermissionService
permController PermissionController
- appUrl string
+ appURL string
)
func init() {
env.ReadConfig("./../../.env")
config := env.Configuration()
- appUrl = config.AppUrl
+ appURL = config.AppURL
connector.LoadDatabase()
- connector.LoadRedisDatabase()
+ connector.LoadRedisCache()
userRepo = userRepository.NewUserRepository()
permService = service.NewPermissionService()
permController = NewPermissionController(permService)
}
-func Test_Perm_NewPermissionController(t *testing.T) {
+func TestPermNewPermissionController(t *testing.T) {
permSvc := service.NewPermissionService()
permCtr := NewPermissionController(permSvc)
if permSvc == nil || permCtr == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
}
-func Test_Perm_Create(t *testing.T) {
+func TestPermCreate(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := permController
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
userID, userToken := createUserAndToken()
@@ -113,7 +115,7 @@ func Test_Perm_Create(t *testing.T) {
jwtHandler := middleware.NewJWTHandler()
for _, tc := range testCases {
c := helper.NewFiberCtx()
- c.Request().Header.Set("Authorization", fmt.Sprintf("Bearer %s", tc.token))
+ c.Request().Header.Set(fiber.HeaderAuthorization, fmt.Sprintf("Bearer %s", tc.token))
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
requestBody, err := json.Marshal(tc.payload)
if err != nil {
@@ -166,12 +168,12 @@ func Test_Perm_Create(t *testing.T) {
}
}
-func Test_Perm_Get(t *testing.T) {
+func TestPermGet(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := permController
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
testCases := []struct {
@@ -207,7 +209,7 @@ func Test_Perm_Get(t *testing.T) {
app.Get("/permission/:id", permController.Get)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -233,12 +235,12 @@ func Test_Perm_Get(t *testing.T) {
}
}
-func Test_Perm_GetAll(t *testing.T) {
+func TestPermGetAll(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := permController
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
testCases := []struct {
@@ -278,7 +280,7 @@ func Test_Perm_GetAll(t *testing.T) {
app.Get("/permission", permController.GetAll)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -307,12 +309,12 @@ func Test_Perm_GetAll(t *testing.T) {
}
}
-func Test_Perm_Update(t *testing.T) {
+func TestPermUpdate(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := permController
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
// create 1 permission
@@ -385,19 +387,19 @@ func Test_Perm_Update(t *testing.T) {
log.Println(":::::::" + tc.caseName)
jsonObject, err := json.Marshal(tc.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := fmt.Sprintf(appUrl+"permission/%d", tc.permID)
+ url := fmt.Sprintf(appURL+"permission/%d", tc.permID)
req, err := http.NewRequest(http.MethodPut, url, bytes.NewReader(jsonObject))
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
app := fiber.New()
app.Put("/permission/:id", permController.Update)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -422,12 +424,12 @@ func Test_Perm_Update(t *testing.T) {
}
}
-func Test_Perm_Delete(t *testing.T) {
+func TestPermDelete(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := permController
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
// create 1 permission
@@ -471,17 +473,17 @@ func Test_Perm_Delete(t *testing.T) {
}
for _, tc := range testCases {
- url := appUrl + "permission/" + strconv.Itoa(tc.permID)
+ url := appURL + "permission/" + strconv.Itoa(tc.permID)
req, httpReqErr := http.NewRequest(http.MethodDelete, url, nil)
if httpReqErr != nil || req == nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
app := fiber.New()
app.Delete("/permission/:id", permController.Delete)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -492,7 +494,7 @@ func Test_Perm_Delete(t *testing.T) {
func createUserAndToken() (userID int, token string) {
permService := service.NewPermissionService()
- roleService := service.NewRoleService(permService)
+ roleService := roleService.NewRoleService(permService)
userSvc := userService.NewUserService(roleService)
userCtr := userController.NewUserController(userSvc)
@@ -500,7 +502,7 @@ func createUserAndToken() (userID int, token string) {
ctx := c.Context()
ctr := userCtr
if ctr == nil || c == nil || ctx == nil {
- log.Fatal("should not nil")
+ log.Fatal(constants.ShouldNotNil)
}
createdUser := model.UserRegister{
@@ -527,7 +529,7 @@ func createUserAndToken() (userID int, token string) {
Email: userByID.Email,
})
if verifyErr != nil {
- log.Fatal("should not error")
+ log.Fatal(constants.ShouldNotErr)
}
userByID = nil
userByID, getErr = userRepo.GetByID(ctx, userID)
diff --git a/controller/role/role_controller.go b/controller/role/role_controller.go
index 6718ba4..f794326 100644
--- a/controller/role/role_controller.go
+++ b/controller/role/role_controller.go
@@ -9,16 +9,30 @@ import (
"github.com/Lukmanern/gost/domain/base"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/response"
- service "github.com/Lukmanern/gost/service/rbac"
+ service "github.com/Lukmanern/gost/service/role"
)
type RoleController interface {
+
+ // Create func creates a new role
Create(c *fiber.Ctx) error
+
+ // Connect func connects a role with some permissions
+ // and storing data in role_has_permissions table
Connect(c *fiber.Ctx) error
+
+ // Get func gets a role
Get(c *fiber.Ctx) error
+
+ // GetAll func gets some roles
GetAll(c *fiber.Ctx) error
+
+ // Update func updates a role
Update(c *fiber.Ctx) error
+
+ // Delete func deletes a role
Delete(c *fiber.Ctx) error
}
@@ -40,10 +54,10 @@ func NewRoleController(service service.RoleService) RoleController {
return roleControllerImpl
}
-func (ctr RoleControllerImpl) Create(c *fiber.Ctx) error {
+func (ctr *RoleControllerImpl) Create(c *fiber.Ctx) error {
var role model.RoleCreate
if err := c.BodyParser(&role); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
// hashmap
idCheckers := make(map[int]bool)
@@ -58,7 +72,7 @@ func (ctr RoleControllerImpl) Create(c *fiber.Ctx) error {
}
validate := validator.New()
if err := validate.Struct(&role); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
@@ -68,7 +82,7 @@ func (ctr RoleControllerImpl) Create(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+createErr.Error())
+ return response.Error(c, constants.ServerErr+createErr.Error())
}
data := map[string]any{
"id": id,
@@ -76,10 +90,10 @@ func (ctr RoleControllerImpl) Create(c *fiber.Ctx) error {
return response.SuccessCreated(c, data)
}
-func (ctr RoleControllerImpl) Connect(c *fiber.Ctx) error {
+func (ctr *RoleControllerImpl) Connect(c *fiber.Ctx) error {
var role model.RoleConnectToPermissions
if err := c.BodyParser(&role); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
// hashmap
idCheckers := make(map[int]bool)
@@ -94,7 +108,7 @@ func (ctr RoleControllerImpl) Connect(c *fiber.Ctx) error {
}
validate := validator.New()
if err := validate.Struct(&role); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
@@ -104,15 +118,15 @@ func (ctr RoleControllerImpl) Connect(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+connectErr.Error())
+ return response.Error(c, constants.ServerErr+connectErr.Error())
}
return response.SuccessCreated(c, "role and permissions success connected")
}
-func (ctr RoleControllerImpl) Get(c *fiber.Ctx) error {
+func (ctr *RoleControllerImpl) Get(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
- return response.BadRequest(c, "invalid id")
+ return response.BadRequest(c, constants.InvalidID)
}
ctx := c.Context()
@@ -122,12 +136,12 @@ func (ctr RoleControllerImpl) Get(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+getErr.Error())
+ return response.Error(c, constants.ServerErr+getErr.Error())
}
return response.SuccessLoaded(c, role)
}
-func (ctr RoleControllerImpl) GetAll(c *fiber.Ctx) error {
+func (ctr *RoleControllerImpl) GetAll(c *fiber.Ctx) error {
request := base.RequestGetAll{
Page: c.QueryInt("page", 1),
Limit: c.QueryInt("limit", 20),
@@ -141,7 +155,7 @@ func (ctr RoleControllerImpl) GetAll(c *fiber.Ctx) error {
ctx := c.Context()
roles, total, getErr := ctr.service.GetAll(ctx, request)
if getErr != nil {
- return response.Error(c, "internal server error: "+getErr.Error())
+ return response.Error(c, constants.ServerErr+getErr.Error())
}
data := make([]interface{}, len(roles))
@@ -159,19 +173,19 @@ func (ctr RoleControllerImpl) GetAll(c *fiber.Ctx) error {
return response.SuccessLoaded(c, responseData)
}
-func (ctr RoleControllerImpl) Update(c *fiber.Ctx) error {
+func (ctr *RoleControllerImpl) Update(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
- return response.BadRequest(c, "invalid id")
+ return response.BadRequest(c, constants.InvalidID)
}
var role model.RoleUpdate
role.ID = id
if err := c.BodyParser(&role); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
validate := validator.New()
if err := validate.Struct(&role); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
@@ -181,15 +195,15 @@ func (ctr RoleControllerImpl) Update(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+updateErr.Error())
+ return response.Error(c, constants.ServerErr+updateErr.Error())
}
return response.SuccessNoContent(c)
}
-func (ctr RoleControllerImpl) Delete(c *fiber.Ctx) error {
+func (ctr *RoleControllerImpl) Delete(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
- return response.BadRequest(c, "invalid id")
+ return response.BadRequest(c, constants.InvalidID)
}
ctx := c.Context()
@@ -199,7 +213,7 @@ func (ctr RoleControllerImpl) Delete(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+deleteErr.Error())
+ return response.Error(c, constants.ServerErr+deleteErr.Error())
}
return response.SuccessNoContent(c)
}
diff --git a/controller/role/role_controller_test.go b/controller/role/role_controller_test.go
index 2565741..de4cbb9 100644
--- a/controller/role/role_controller_test.go
+++ b/controller/role/role_controller_test.go
@@ -15,44 +15,46 @@ import (
"github.com/Lukmanern/gost/database/connector"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/env"
"github.com/Lukmanern/gost/internal/helper"
"github.com/Lukmanern/gost/internal/response"
"github.com/gofiber/fiber/v2"
permissionController "github.com/Lukmanern/gost/controller/permission"
- service "github.com/Lukmanern/gost/service/rbac"
+ permSvc "github.com/Lukmanern/gost/service/permission"
+ service "github.com/Lukmanern/gost/service/role"
)
var (
- permService service.PermissionService
+ permService permSvc.PermissionService
roleService service.RoleService
roleController RoleController
permController permissionController.PermissionController
- appUrl string
+ appURL string
)
func init() {
env.ReadConfig("./../../.env")
config := env.Configuration()
- appUrl = config.AppUrl
+ appURL = config.AppURL
connector.LoadDatabase()
- connector.LoadRedisDatabase()
+ connector.LoadRedisCache()
- permService = service.NewPermissionService()
+ permService = permSvc.NewPermissionService()
permController = permissionController.NewPermissionController(permService)
roleService = service.NewRoleService(permService)
roleController = NewRoleController(roleService)
}
-func Test_Role_Create(t *testing.T) {
+func TestRoleCreate(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := permController
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
permIDs := make([]int, 0)
@@ -146,19 +148,19 @@ func Test_Role_Create(t *testing.T) {
log.Println(":::::::" + tc.caseName)
jsonObject, err := json.Marshal(tc.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := appUrl + "role"
+ url := appURL + "role"
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(jsonObject))
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
app := fiber.New()
app.Post("/role", roleController.Create)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -174,28 +176,28 @@ func Test_Role_Create(t *testing.T) {
if !ok1 {
t.Error("should ok1")
}
- anyId, ok2 := data["id"]
+ anyID, ok2 := data["id"]
if !ok2 {
t.Error("should ok2")
}
- intId, ok3 := anyId.(float64)
+ intID, ok3 := anyID.(float64)
if !ok3 {
t.Error("should ok3")
}
- deleteErr := roleService.Delete(ctx, int(intId))
+ deleteErr := roleService.Delete(ctx, int(intID))
if deleteErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
}
}
}
-func Test_Role_Connect(t *testing.T) {
+func TestRoleConnect(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := permController
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
permIDs := make([]int, 0)
@@ -286,19 +288,19 @@ func Test_Role_Connect(t *testing.T) {
log.Println(":::::::" + tc.caseName)
jsonObject, err := json.Marshal(tc.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := appUrl + "role/connect"
+ url := appURL + "role/connect"
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(jsonObject))
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
app := fiber.New()
app.Post("/role/connect", roleController.Connect)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -321,12 +323,12 @@ func Test_Role_Connect(t *testing.T) {
}
}
-func Test_Role_Get(t *testing.T) {
+func TestRoleGet(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := permController
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
permIDs := make([]int, 0)
@@ -394,17 +396,17 @@ func Test_Role_Get(t *testing.T) {
}
for _, tc := range testCases {
- url := fmt.Sprintf(appUrl+"role/%d", tc.roleID)
+ url := fmt.Sprintf(appURL+"role/%d", tc.roleID)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
app := fiber.New()
app.Get("/role/:id", roleController.Get)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -417,12 +419,12 @@ func Test_Role_Get(t *testing.T) {
}
}
-func Test_Role_GetAll(t *testing.T) {
+func TestRoleGetAll(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := permController
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
permIDs := make([]int, 0)
@@ -485,17 +487,17 @@ func Test_Role_GetAll(t *testing.T) {
}
for _, tc := range testCases {
- url := appUrl + "role?" + tc.params
+ url := appURL + "role?" + tc.params
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
app := fiber.New()
app.Get("/role", roleController.GetAll)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -508,12 +510,12 @@ func Test_Role_GetAll(t *testing.T) {
}
}
-func Test_Role_Update(t *testing.T) {
+func TestRoleUpdate(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := permController
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
permIDs := make([]int, 0)
@@ -610,19 +612,19 @@ func Test_Role_Update(t *testing.T) {
log.Println(":::::::" + tc.caseName)
jsonObject, err := json.Marshal(tc.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := fmt.Sprintf(appUrl+"role/%d", tc.roleID)
+ url := fmt.Sprintf(appURL+"role/%d", tc.roleID)
req, err := http.NewRequest(http.MethodPut, url, bytes.NewReader(jsonObject))
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
app := fiber.New()
app.Put("/role/:id", roleController.Update)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -647,12 +649,12 @@ func Test_Role_Update(t *testing.T) {
}
}
-func Test_Role_Delete(t *testing.T) {
+func TestRoleDelete(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := permController
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
permIDs := make([]int, 0)
@@ -720,17 +722,17 @@ func Test_Role_Delete(t *testing.T) {
}
for _, tc := range testCases {
- url := fmt.Sprintf(appUrl+"role/%d", tc.roleID)
+ url := fmt.Sprintf(appURL+"role/%d", tc.roleID)
req, err := http.NewRequest(http.MethodDelete, url, nil)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
app := fiber.New()
app.Delete("/role/:id", roleController.Delete)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
diff --git a/controller/user/user_controller.go b/controller/user/user_controller.go
index 0c8a5b2..4e860b2 100644
--- a/controller/user/user_controller.go
+++ b/controller/user/user_controller.go
@@ -9,21 +9,49 @@ import (
"github.com/gofiber/fiber/v2"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/middleware"
"github.com/Lukmanern/gost/internal/response"
service "github.com/Lukmanern/gost/service/user"
)
type UserController interface {
+ // Register function register user account,
+ // than send verification-code to email
Register(c *fiber.Ctx) error
+
+ // AccountActivation function activates user account with
+ // verification code that has been sended to the user's email
AccountActivation(c *fiber.Ctx) error
+
+ // DeleteUserByVerification function deletes user data if the
+ // user account is not yet verified. This implies that the email
+ // owner hasn't actually registered the email, indicating that
+ // the user who registered may be making typing errors or may
+ // be a hacker attempting to get the verification code.
DeleteAccountActivation(c *fiber.Ctx) error
+
+ // ForgetPassword function send
+ // verification code into user's email
ForgetPassword(c *fiber.Ctx) error
+
+ // ResetPassword func resets password by creating
+ // new password by email and verification code
ResetPassword(c *fiber.Ctx) error
+
+ // Login func gives token and access to user
Login(c *fiber.Ctx) error
+
+ // Logout func stores user's token into Redis
Logout(c *fiber.Ctx) error
+
+ // UpdatePassword func updates user's password
UpdatePassword(c *fiber.Ctx) error
+
+ // UpdateProfile func updates user's profile data
UpdateProfile(c *fiber.Ctx) error
+
+ // MyProfile func shows user's profile data
MyProfile(c *fiber.Ctx) error
}
@@ -46,15 +74,15 @@ func NewUserController(service service.UserService) UserController {
return userAuthController
}
-func (ctr UserControllerImpl) Register(c *fiber.Ctx) error {
+func (ctr *UserControllerImpl) Register(c *fiber.Ctx) error {
var user model.UserRegister
if err := c.BodyParser(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
user.Email = strings.ToLower(user.Email)
validate := validator.New()
if err := validate.Struct(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
@@ -64,7 +92,7 @@ func (ctr UserControllerImpl) Register(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+regisErr.Error())
+ return response.Error(c, constants.ServerErr+regisErr.Error())
}
message := "Account success created. please check " + user.Email + " "
@@ -75,14 +103,14 @@ func (ctr UserControllerImpl) Register(c *fiber.Ctx) error {
return response.CreateResponse(c, fiber.StatusCreated, true, message, data)
}
-func (ctr UserControllerImpl) AccountActivation(c *fiber.Ctx) error {
+func (ctr *UserControllerImpl) AccountActivation(c *fiber.Ctx) error {
var user model.UserVerificationCode
if err := c.BodyParser(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
validate := validator.New()
if err := validate.Struct(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
err := ctr.service.Verification(ctx, user)
@@ -91,21 +119,21 @@ func (ctr UserControllerImpl) AccountActivation(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+err.Error())
+ return response.Error(c, constants.ServerErr+err.Error())
}
message := "Thank you for your confirmation. Your account is active now, you can login."
return response.CreateResponse(c, fiber.StatusOK, true, message, nil)
}
-func (ctr UserControllerImpl) DeleteAccountActivation(c *fiber.Ctx) error {
+func (ctr *UserControllerImpl) DeleteAccountActivation(c *fiber.Ctx) error {
var verifyData model.UserVerificationCode
if err := c.BodyParser(&verifyData); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
validate := validator.New()
if err := validate.Struct(&verifyData); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
err := ctr.service.DeleteUserByVerification(ctx, verifyData)
@@ -114,23 +142,23 @@ func (ctr UserControllerImpl) DeleteAccountActivation(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+err.Error())
+ return response.Error(c, constants.ServerErr+err.Error())
}
message := "Your data is already deleted, thank you for your confirmation."
return response.CreateResponse(c, fiber.StatusOK, true, message, nil)
}
-func (ctr UserControllerImpl) Login(c *fiber.Ctx) error {
+func (ctr *UserControllerImpl) Login(c *fiber.Ctx) error {
var user model.UserLogin
- // user.IP = c.IP() // Todo : uncomment this line in production
+ // user.IP = c.IP() // Note : uncomment this line in production
if err := c.BodyParser(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
userIP := net.ParseIP(user.IP)
if userIP == nil {
- return response.BadRequest(c, "invalid json body: invalid user ip address")
+ return response.BadRequest(c, constants.InvalidBody+"invalid user ip address")
}
counter, _ := ctr.service.FailedLoginCounter(userIP.String(), false)
ipBlockMsg := "Your IP has been blocked by system. Please try again in 1 or 2 Hour"
@@ -140,7 +168,7 @@ func (ctr UserControllerImpl) Login(c *fiber.Ctx) error {
validate := validator.New()
if err := validate.Struct(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
@@ -154,7 +182,7 @@ func (ctr UserControllerImpl) Login(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+loginErr.Error())
+ return response.Error(c, constants.ServerErr+loginErr.Error())
}
data := map[string]any{
@@ -164,26 +192,26 @@ func (ctr UserControllerImpl) Login(c *fiber.Ctx) error {
return response.CreateResponse(c, fiber.StatusOK, true, "success login", data)
}
-func (ctr UserControllerImpl) Logout(c *fiber.Ctx) error {
+func (ctr *UserControllerImpl) Logout(c *fiber.Ctx) error {
userClaims, ok := c.Locals("claims").(*middleware.Claims)
if !ok || userClaims == nil {
return response.Unauthorized(c)
}
logoutErr := ctr.service.Logout(c)
if logoutErr != nil {
- return response.Error(c, "internal server error: "+logoutErr.Error())
+ return response.Error(c, constants.ServerErr+logoutErr.Error())
}
return response.CreateResponse(c, fiber.StatusOK, true, "success logout", nil)
}
-func (ctr UserControllerImpl) ForgetPassword(c *fiber.Ctx) error {
+func (ctr *UserControllerImpl) ForgetPassword(c *fiber.Ctx) error {
var user model.UserForgetPassword
if err := c.BodyParser(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
validate := validator.New()
if err := validate.Struct(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
@@ -193,21 +221,21 @@ func (ctr UserControllerImpl) ForgetPassword(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+forgetErr.Error())
+ return response.Error(c, constants.ServerErr+forgetErr.Error())
}
message := "success sending link for reset password to email, check your email inbox"
return response.CreateResponse(c, fiber.StatusAccepted, true, message, nil)
}
-func (ctr UserControllerImpl) ResetPassword(c *fiber.Ctx) error {
+func (ctr *UserControllerImpl) ResetPassword(c *fiber.Ctx) error {
var user model.UserResetPassword
if err := c.BodyParser(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
validate := validator.New()
if err := validate.Struct(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
if user.NewPassword != user.NewPasswordConfirm {
return response.BadRequest(c, "password confirmation not match")
@@ -220,14 +248,14 @@ func (ctr UserControllerImpl) ResetPassword(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+resetErr.Error())
+ return response.Error(c, constants.ServerErr+resetErr.Error())
}
message := "your password already updated, you can login with your new password, thank you"
return response.CreateResponse(c, fiber.StatusAccepted, true, message, nil)
}
-func (ctr UserControllerImpl) UpdatePassword(c *fiber.Ctx) error {
+func (ctr *UserControllerImpl) UpdatePassword(c *fiber.Ctx) error {
userClaims, ok := c.Locals("claims").(*middleware.Claims)
if !ok || userClaims == nil {
return response.Unauthorized(c)
@@ -235,13 +263,13 @@ func (ctr UserControllerImpl) UpdatePassword(c *fiber.Ctx) error {
var user model.UserPasswordUpdate
if err := c.BodyParser(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
user.ID = userClaims.ID
validate := validator.New()
if err := validate.Struct(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
if user.NewPassword != user.NewPasswordConfirm {
return response.BadRequest(c, "new password confirmation is wrong")
@@ -257,13 +285,13 @@ func (ctr UserControllerImpl) UpdatePassword(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+updateErr.Error())
+ return response.Error(c, constants.ServerErr+updateErr.Error())
}
return response.SuccessNoContent(c)
}
-func (ctr UserControllerImpl) UpdateProfile(c *fiber.Ctx) error {
+func (ctr *UserControllerImpl) UpdateProfile(c *fiber.Ctx) error {
userClaims, ok := c.Locals("claims").(*middleware.Claims)
if !ok || userClaims == nil {
return response.Unauthorized(c)
@@ -271,12 +299,12 @@ func (ctr UserControllerImpl) UpdateProfile(c *fiber.Ctx) error {
var user model.UserProfileUpdate
if err := c.BodyParser(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
user.ID = userClaims.ID
validate := validator.New()
if err := validate.Struct(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
@@ -286,13 +314,13 @@ func (ctr UserControllerImpl) UpdateProfile(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+updateErr.Error())
+ return response.Error(c, constants.ServerErr+updateErr.Error())
}
return response.SuccessNoContent(c)
}
-func (ctr UserControllerImpl) MyProfile(c *fiber.Ctx) error {
+func (ctr *UserControllerImpl) MyProfile(c *fiber.Ctx) error {
userClaims, ok := c.Locals("claims").(*middleware.Claims)
if !ok || userClaims == nil {
return response.Unauthorized(c)
@@ -305,7 +333,7 @@ func (ctr UserControllerImpl) MyProfile(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+getErr.Error())
+ return response.Error(c, constants.ServerErr+getErr.Error())
}
return response.SuccessLoaded(c, userProfile)
}
diff --git a/controller/user/user_controller_test.go b/controller/user/user_controller_test.go
index bd0ac8b..c107a85 100644
--- a/controller/user/user_controller_test.go
+++ b/controller/user/user_controller_test.go
@@ -10,18 +10,18 @@ import (
"testing"
"github.com/gofiber/fiber/v2"
- "golang.org/x/text/cases"
- "golang.org/x/text/language"
"github.com/Lukmanern/gost/database/connector"
"github.com/Lukmanern/gost/domain/entity"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/env"
"github.com/Lukmanern/gost/internal/helper"
"github.com/Lukmanern/gost/internal/middleware"
"github.com/Lukmanern/gost/internal/response"
repository "github.com/Lukmanern/gost/repository/user"
- rbacService "github.com/Lukmanern/gost/service/rbac"
+ permService "github.com/Lukmanern/gost/service/permission"
+ roleService "github.com/Lukmanern/gost/service/role"
service "github.com/Lukmanern/gost/service/user"
)
@@ -29,43 +29,43 @@ var (
userSvc service.UserService
userCtr UserController
userRepo repository.UserRepository
- appUrl string
+ appURL string
)
func init() {
env.ReadConfig("./../../.env")
config := env.Configuration()
- appUrl = config.AppUrl
+ appURL = config.AppURL
connector.LoadDatabase()
- r := connector.LoadRedisDatabase()
+ r := connector.LoadRedisCache()
r.FlushAll() // clear all key:value in redis
- permService := rbacService.NewPermissionService()
- roleService := rbacService.NewRoleService(permService)
+ permService := permService.NewPermissionService()
+ roleService := roleService.NewRoleService(permService)
userSvc = service.NewUserService(roleService)
userCtr = NewUserController(userSvc)
userRepo = repository.NewUserRepository()
}
func TestNewUserController(t *testing.T) {
- permService := rbacService.NewPermissionService()
- roleService := rbacService.NewRoleService(permService)
+ permService := permService.NewPermissionService()
+ roleService := roleService.NewRoleService(permService)
userService := service.NewUserService(roleService)
userController := NewUserController(userService)
if userController == nil || userService == nil || roleService == nil || permService == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
}
-func Test_Register(t *testing.T) {
+func TestRegister(t *testing.T) {
// unaudit
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := userCtr
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
c.Method(http.MethodPost)
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -192,12 +192,12 @@ func Test_Register(t *testing.T) {
log.Println(":::::::" + tc.caseName)
jsonObject, err := json.Marshal(&tc.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := appUrl + endp
+ url := appURL + endp
req, httpReqErr := http.NewRequest(http.MethodPost, url, bytes.NewReader(jsonObject))
if httpReqErr != nil || req == nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -206,7 +206,7 @@ func Test_Register(t *testing.T) {
req.Close = true
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -217,7 +217,7 @@ func Test_Register(t *testing.T) {
respModel := response.Response{}
decodeErr := json.NewDecoder(resp.Body).Decode(&respModel)
if decodeErr != nil {
- t.Error("should not error", decodeErr)
+ t.Error(constants.ShouldNotErr, decodeErr)
}
}
@@ -226,7 +226,7 @@ func Test_Register(t *testing.T) {
if getErr != nil || userByEmail == nil {
t.Fatal("should success whilte create and get user")
}
- if userByEmail.Name != cases.Title(language.Und).String(tc.payload.Name) {
+ if userByEmail.Name != helper.ToTitle(tc.payload.Name) {
t.Error("name should equal")
}
@@ -239,13 +239,13 @@ func Test_Register(t *testing.T) {
}
-func Test_AccountActivation(t *testing.T) {
+func TestAccountActivation(t *testing.T) {
// unaudit
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := userCtr
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
c.Method(http.MethodPost)
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -313,12 +313,12 @@ func Test_AccountActivation(t *testing.T) {
log.Println(":::::::" + tc.caseName)
jsonObject, err := json.Marshal(&tc.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := appUrl + endp
+ url := appURL + endp
req, httpReqErr := http.NewRequest(http.MethodPost, url, bytes.NewReader(jsonObject))
if httpReqErr != nil || req == nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -327,7 +327,7 @@ func Test_AccountActivation(t *testing.T) {
req.Close = true
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -350,13 +350,13 @@ func Test_AccountActivation(t *testing.T) {
}
}
-func Test_DeleteAccountActivation(t *testing.T) {
+func TestDeleteAccountActivation(t *testing.T) {
// unaudit
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := userCtr
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
c.Method(http.MethodPost)
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -424,12 +424,12 @@ func Test_DeleteAccountActivation(t *testing.T) {
log.Println(":::::::" + tc.caseName)
jsonObject, err := json.Marshal(&tc.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := appUrl + endp
+ url := appURL + endp
req, httpReqErr := http.NewRequest(http.MethodPost, url, bytes.NewReader(jsonObject))
if httpReqErr != nil || req == nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -438,7 +438,7 @@ func Test_DeleteAccountActivation(t *testing.T) {
req.Close = true
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -462,13 +462,13 @@ func Test_DeleteAccountActivation(t *testing.T) {
}
}
-func Test_ForgetPassword(t *testing.T) {
+func TestForgetPassword(t *testing.T) {
// unaudit
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := userCtr
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
c.Method(http.MethodPost)
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -553,12 +553,12 @@ func Test_ForgetPassword(t *testing.T) {
log.Println(":::::::" + tc.caseName)
jsonObject, err := json.Marshal(&tc.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := appUrl + endp
+ url := appURL + endp
req, httpReqErr := http.NewRequest(http.MethodPost, url, bytes.NewReader(jsonObject))
if httpReqErr != nil || req == nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -567,7 +567,7 @@ func Test_ForgetPassword(t *testing.T) {
req.Close = true
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -576,13 +576,13 @@ func Test_ForgetPassword(t *testing.T) {
}
}
-func Test_ResetPassword(t *testing.T) {
+func TestResetPassword(t *testing.T) {
// unaudit
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := userCtr
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
c.Method(http.MethodPost)
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -610,7 +610,7 @@ func Test_ResetPassword(t *testing.T) {
Email: userByID.Email,
})
if verifyErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
// value reset
@@ -629,7 +629,7 @@ func Test_ResetPassword(t *testing.T) {
}
forgetPassErr := userSvc.ForgetPassword(ctx, userForgetPasswd)
if forgetPassErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
// value reset
@@ -661,6 +661,7 @@ func Test_ResetPassword(t *testing.T) {
caseName: "success reset password",
respCode: http.StatusAccepted,
payload: &model.UserResetPassword{
+ Email: userByID.Email,
Code: *userByID.VerificationCode,
NewPassword: "newPassword",
NewPasswordConfirm: "newPassword",
@@ -670,6 +671,7 @@ func Test_ResetPassword(t *testing.T) {
caseName: "failed reset password: password not match",
respCode: http.StatusBadRequest,
payload: &model.UserResetPassword{
+ Email: userByID.Email,
Code: *userByID.VerificationCode,
NewPassword: "newPassword",
NewPasswordConfirm: "newPasswordNotMatch",
@@ -679,6 +681,7 @@ func Test_ResetPassword(t *testing.T) {
caseName: "failed reset password: verification code too short",
respCode: http.StatusBadRequest,
payload: &model.UserResetPassword{
+ Email: helper.RandomEmail(),
Code: "short",
NewPassword: "newPassword",
NewPasswordConfirm: "newPasswordNotMatch",
@@ -691,12 +694,12 @@ func Test_ResetPassword(t *testing.T) {
log.Println(":::::::" + tc.caseName)
jsonObject, err := json.Marshal(&tc.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := appUrl + endp
+ url := appURL + endp
req, httpReqErr := http.NewRequest(http.MethodPost, url, bytes.NewReader(jsonObject))
if httpReqErr != nil || req == nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -705,7 +708,7 @@ func Test_ResetPassword(t *testing.T) {
req.Close = true
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -726,13 +729,13 @@ func Test_ResetPassword(t *testing.T) {
}
}
-func Test_Login(t *testing.T) {
+func TestLogin(t *testing.T) {
// unaudit
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := userCtr
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
c.Method(http.MethodPost)
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -768,13 +771,13 @@ func Test_Login(t *testing.T) {
// create active user
createdActiveUser := entity.User{}
func() {
- createdUser_2 := model.UserRegister{
+ createdUser2 := model.UserRegister{
Name: helper.RandomString(10),
Email: helper.RandomEmail(),
Password: helper.RandomString(10),
RoleID: 1, // admin
}
- userID, createErr := userSvc.Register(ctx, createdUser_2)
+ userID, createErr := userSvc.Register(ctx, createdUser2)
if createErr != nil || userID <= 0 {
t.Fatal("should success create user, user failed to create")
}
@@ -793,7 +796,7 @@ func Test_Login(t *testing.T) {
Email: userByID.Email,
})
if verifyErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
userByID = nil
userByID, getErr = userRepo.GetByID(ctx, userID)
@@ -802,7 +805,7 @@ func Test_Login(t *testing.T) {
}
createdActiveUser = *userByID
- createdActiveUser.Password = createdUser_2.Password
+ createdActiveUser.Password = createdUser2.Password
}()
defer userRepo.Delete(ctx, createdActiveUser.ID)
@@ -891,12 +894,12 @@ func Test_Login(t *testing.T) {
log.Println(":::::::" + tc.caseName)
jsonObject, err := json.Marshal(&tc.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := appUrl + endp
+ url := appURL + endp
req, httpReqErr := http.NewRequest(http.MethodPost, url, bytes.NewReader(jsonObject))
if httpReqErr != nil || req == nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -905,7 +908,7 @@ func Test_Login(t *testing.T) {
req.Close = true
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
@@ -932,12 +935,12 @@ func Test_Login(t *testing.T) {
log.Println(":::::::" + testCase.caseName)
jsonObject, err := json.Marshal(&testCase.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := appUrl + endp
+ url := appURL + endp
req, httpReqErr := http.NewRequest(http.MethodPost, url, bytes.NewReader(jsonObject))
if httpReqErr != nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -946,7 +949,7 @@ func Test_Login(t *testing.T) {
req.Close = true
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != testCase.respCode {
@@ -954,9 +957,9 @@ func Test_Login(t *testing.T) {
}
}
- redis := connector.LoadRedisDatabase()
+ redis := connector.LoadRedisCache()
if redis == nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
}
value := redis.Get("failed-login-" + clientIP).Val()
if value != "5" {
@@ -964,13 +967,13 @@ func Test_Login(t *testing.T) {
}
}
-func Test_Logout(t *testing.T) {
+func TestLogout(t *testing.T) {
// unaudit
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := userCtr
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
// create inactive user
@@ -998,7 +1001,7 @@ func Test_Logout(t *testing.T) {
Email: userByID.Email,
})
if verifyErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
userByID = nil
userByID, getErr = userRepo.GetByID(ctx, userID)
@@ -1051,7 +1054,7 @@ func Test_Logout(t *testing.T) {
jwtHandler := middleware.NewJWTHandler()
for _, tc := range testCases {
c := helper.NewFiberCtx()
- c.Request().Header.Set("Authorization", fmt.Sprintf("Bearer %s", userToken))
+ c.Request().Header.Set(fiber.HeaderAuthorization, fmt.Sprintf("Bearer %s", userToken))
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
fakeClaims := jwtHandler.GenerateClaims(tc.token)
if fakeClaims != nil {
@@ -1083,12 +1086,12 @@ func Test_Logout(t *testing.T) {
}
}
-func Test_UpdatePassword(t *testing.T) {
+func TestUpdatePassword(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := userCtr
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
// create inactive user
@@ -1116,7 +1119,7 @@ func Test_UpdatePassword(t *testing.T) {
Email: userByID.Email,
})
if verifyErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
userByID = nil
userByID, getErr = userRepo.GetByID(ctx, userID)
@@ -1200,7 +1203,7 @@ func Test_UpdatePassword(t *testing.T) {
jwtHandler := middleware.NewJWTHandler()
for _, tc := range testCases {
c := helper.NewFiberCtx()
- c.Request().Header.Set("Authorization", fmt.Sprintf("Bearer %s", userToken))
+ c.Request().Header.Set(fiber.HeaderAuthorization, fmt.Sprintf("Bearer %s", userToken))
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
if tc.payload != nil {
requestBody, err := json.Marshal(tc.payload)
@@ -1232,13 +1235,13 @@ func Test_UpdatePassword(t *testing.T) {
}
}
-func Test_UpdateProfile(t *testing.T) {
+func TestUpdateProfile(t *testing.T) {
// unaudit
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := userCtr
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
// create inactive user
@@ -1266,7 +1269,7 @@ func Test_UpdateProfile(t *testing.T) {
Email: userByID.Email,
})
if verifyErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
userByID = nil
userByID, getErr = userRepo.GetByID(ctx, userID)
@@ -1336,7 +1339,7 @@ func Test_UpdateProfile(t *testing.T) {
jwtHandler := middleware.NewJWTHandler()
for _, tc := range testCases {
c := helper.NewFiberCtx()
- c.Request().Header.Set("Authorization", fmt.Sprintf("Bearer %s", userToken))
+ c.Request().Header.Set(fiber.HeaderAuthorization, fmt.Sprintf("Bearer %s", userToken))
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
if tc.payload != nil {
requestBody, err := json.Marshal(tc.payload)
@@ -1358,22 +1361,22 @@ func Test_UpdateProfile(t *testing.T) {
if resp.StatusCode() == http.StatusNoContent {
userByID, err := userRepo.GetByID(ctx, userID)
if err != nil || userByID == nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
- if userByID.Name != cases.Title(language.Und).String(tc.payload.Name) {
+ if userByID.Name != helper.ToTitle(tc.payload.Name) {
t.Error("shoudl equal")
}
}
}
}
-func Test_MyProfile(t *testing.T) {
+func TestMyProfile(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
ctr := userCtr
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
// create inactive user
@@ -1401,7 +1404,7 @@ func Test_MyProfile(t *testing.T) {
Email: userByID.Email,
})
if verifyErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
userByID = nil
userByID, getErr = userRepo.GetByID(ctx, userID)
@@ -1454,7 +1457,7 @@ func Test_MyProfile(t *testing.T) {
jwtHandler := middleware.NewJWTHandler()
for _, tc := range testCases {
c := helper.NewFiberCtx()
- c.Request().Header.Set("Authorization", fmt.Sprintf("Bearer %s", userToken))
+ c.Request().Header.Set(fiber.HeaderAuthorization, fmt.Sprintf("Bearer %s", userToken))
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
fakeClaims := jwtHandler.GenerateClaims(tc.token)
if fakeClaims != nil {
diff --git a/controller/user_management/user_management_controller.go b/controller/user_management/user_management_controller.go
index 5b9e20d..8032c72 100644
--- a/controller/user_management/user_management_controller.go
+++ b/controller/user_management/user_management_controller.go
@@ -13,15 +13,25 @@ import (
"github.com/Lukmanern/gost/domain/base"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/response"
service "github.com/Lukmanern/gost/service/user_management"
)
type UserManagementController interface {
+ // Create func creates a new user
Create(c *fiber.Ctx) error
+
+ // Get func gets a user
Get(c *fiber.Ctx) error
+
+ // GetAll func gets some users
GetAll(c *fiber.Ctx) error
+
+ // Update func updates a user
Update(c *fiber.Ctx) error
+
+ // Delete func deletes a user
Delete(c *fiber.Ctx) error
}
@@ -35,15 +45,15 @@ func NewUserManagementController(userService service.UserManagementService) User
}
}
-func (ctr UserManagementControllerImpl) Create(c *fiber.Ctx) error {
+func (ctr *UserManagementControllerImpl) Create(c *fiber.Ctx) error {
var user model.UserCreate
if err := c.BodyParser(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
user.Email = strings.ToLower(user.Email)
validate := validator.New()
if err := validate.Struct(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
@@ -53,7 +63,7 @@ func (ctr UserManagementControllerImpl) Create(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+createErr.Error())
+ return response.Error(c, constants.ServerErr+createErr.Error())
}
data := map[string]any{
"id": id,
@@ -61,10 +71,10 @@ func (ctr UserManagementControllerImpl) Create(c *fiber.Ctx) error {
return response.SuccessCreated(c, data)
}
-func (ctr UserManagementControllerImpl) Get(c *fiber.Ctx) error {
+func (ctr *UserManagementControllerImpl) Get(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
- return response.BadRequest(c, "invalid id")
+ return response.BadRequest(c, constants.InvalidID)
}
ctx := c.Context()
@@ -74,12 +84,12 @@ func (ctr UserManagementControllerImpl) Get(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+getErr.Error())
+ return response.Error(c, constants.ServerErr+getErr.Error())
}
return response.SuccessLoaded(c, userProfile)
}
-func (ctr UserManagementControllerImpl) GetAll(c *fiber.Ctx) error {
+func (ctr *UserManagementControllerImpl) GetAll(c *fiber.Ctx) error {
request := base.RequestGetAll{
Page: c.QueryInt("page", 1),
Limit: c.QueryInt("limit", 20),
@@ -93,7 +103,7 @@ func (ctr UserManagementControllerImpl) GetAll(c *fiber.Ctx) error {
ctx := c.Context()
users, total, getErr := ctr.service.GetAll(ctx, request)
if getErr != nil {
- return response.Error(c, "internal server error: "+getErr.Error())
+ return response.Error(c, constants.ServerErr+getErr.Error())
}
data := make([]interface{}, len(users))
@@ -111,19 +121,19 @@ func (ctr UserManagementControllerImpl) GetAll(c *fiber.Ctx) error {
return response.SuccessLoaded(c, responseData)
}
-func (ctr UserManagementControllerImpl) Update(c *fiber.Ctx) error {
+func (ctr *UserManagementControllerImpl) Update(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
- return response.BadRequest(c, "invalid id")
+ return response.BadRequest(c, constants.InvalidID)
}
var user model.UserProfileUpdate
user.ID = id
if err := c.BodyParser(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
validate := validator.New()
if err := validate.Struct(&user); err != nil {
- return response.BadRequest(c, "invalid json body: "+err.Error())
+ return response.BadRequest(c, constants.InvalidBody+err.Error())
}
ctx := c.Context()
@@ -133,15 +143,15 @@ func (ctr UserManagementControllerImpl) Update(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+updateErr.Error())
+ return response.Error(c, constants.ServerErr+updateErr.Error())
}
return response.SuccessNoContent(c)
}
-func (ctr UserManagementControllerImpl) Delete(c *fiber.Ctx) error {
+func (ctr *UserManagementControllerImpl) Delete(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
- return response.BadRequest(c, "invalid id")
+ return response.BadRequest(c, constants.InvalidID)
}
ctx := c.Context()
@@ -151,7 +161,7 @@ func (ctr UserManagementControllerImpl) Delete(c *fiber.Ctx) error {
if ok {
return response.CreateResponse(c, fiberErr.Code, false, fiberErr.Message, nil)
}
- return response.Error(c, "internal server error: "+deleteErr.Error())
+ return response.Error(c, constants.ServerErr+deleteErr.Error())
}
return response.SuccessNoContent(c)
}
diff --git a/controller/user_management/user_management_controller_test.go b/controller/user_management/user_management_controller_test.go
index 26353f5..aa5fd96 100644
--- a/controller/user_management/user_management_controller_test.go
+++ b/controller/user_management/user_management_controller_test.go
@@ -15,11 +15,10 @@ import (
"testing"
"github.com/gofiber/fiber/v2"
- "golang.org/x/text/cases"
- "golang.org/x/text/language"
"github.com/Lukmanern/gost/database/connector"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/env"
"github.com/Lukmanern/gost/internal/helper"
"github.com/Lukmanern/gost/internal/response"
@@ -31,27 +30,27 @@ import (
var (
userDevService service.UserManagementService
userDevController controller.UserManagementController
- appUrl string
+ appURL string
)
func init() {
env.ReadConfig("./../../.env")
config := env.Configuration()
- appUrl = config.AppUrl
+ appURL = config.AppURL
connector.LoadDatabase()
- r := connector.LoadRedisDatabase()
+ r := connector.LoadRedisCache()
r.FlushAll() // clear all key:value in redis
userDevService = service.NewUserManagementService()
userDevController = controller.NewUserManagementController(userDevService)
}
-func Test_Create(t *testing.T) {
+func TestCreate(t *testing.T) {
c := helper.NewFiberCtx()
ctr := userDevController
if ctr == nil || c == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
c.Method(http.MethodPost)
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -148,13 +147,13 @@ func Test_Create(t *testing.T) {
for _, tc := range testCases {
jsonObject, marshalErr := json.Marshal(&tc.payload)
if marshalErr != nil {
- t.Error("should not error", marshalErr.Error())
+ t.Error(constants.ShouldNotErr, marshalErr.Error())
}
c.Request().SetBody(jsonObject)
createErr := ctr.Create(c)
if createErr != nil {
- t.Error("should not erro", createErr)
+ t.Error(constants.ShouldNotErr, createErr)
} else if tc.payload == nil {
continue
}
@@ -168,25 +167,25 @@ func Test_Create(t *testing.T) {
}
if !tc.wantErr {
if userByEMail == nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
} else {
deleteErr := userDevService.Delete(ctx, userByEMail.ID)
if deleteErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
}
- if userByEMail.Name != cases.Title(language.Und).String(tc.payload.Name) {
- t.Error("should equal")
+ if userByEMail.Name != helper.ToTitle(tc.payload.Name) {
+ t.Error(constants.ShouldEqual)
}
}
}
}
-func Test_Get(t *testing.T) {
+func TestGet(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
if c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
createdUser := model.UserCreate{
@@ -250,34 +249,34 @@ func Test_Get(t *testing.T) {
app.Get("/user-management/:id", userDevController.Get)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
- t.Error("should equal")
+ t.Error(constants.ShouldEqual)
}
if !tc.wantErr {
respModel := response.Response{}
decodeErr := json.NewDecoder(resp.Body).Decode(&respModel)
if decodeErr != nil {
- t.Error("should not error", decodeErr)
+ t.Error(constants.ShouldNotErr, decodeErr)
}
if tc.response.Message != respModel.Message && tc.response.Message != "" {
- t.Error("should equal")
+ t.Error(constants.ShouldEqual)
}
if respModel.Success != tc.response.Success {
- t.Error("should equal")
+ t.Error(constants.ShouldEqual)
}
}
}
}
-func Test_GetAll(t *testing.T) {
+func TestGetAll(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
if c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
userIDs := make([]int, 0)
@@ -331,21 +330,21 @@ func Test_GetAll(t *testing.T) {
app.Get("/user-management", userDevController.GetAll)
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error", err.Error())
+ t.Fatal(constants.ShouldNotErr, err.Error())
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
- t.Error("should equal")
+ t.Error(constants.ShouldEqual)
}
if !tc.wantErr {
body := response.Response{}
bytes, err := io.ReadAll(resp.Body)
if err != nil {
- t.Fatal("should not error", err.Error())
+ t.Fatal(constants.ShouldNotErr, err.Error())
}
err = json.Unmarshal(bytes, &body)
if err != nil {
- t.Fatal("should not error", err.Error())
+ t.Fatal(constants.ShouldNotErr, err.Error())
}
if !body.Success {
t.Fatal("should be success")
@@ -357,12 +356,12 @@ func Test_GetAll(t *testing.T) {
}
}
-func Test_Update(t *testing.T) {
+func TestUpdate(t *testing.T) {
c := helper.NewFiberCtx()
ctr := userDevController
ctx := c.Context()
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
c.Method(http.MethodPut)
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -444,12 +443,12 @@ func Test_Update(t *testing.T) {
log.Println(tc.caseName)
jsonObject, err := json.Marshal(&tc.payload)
if err != nil {
- t.Error("should not error", err.Error())
+ t.Error(constants.ShouldNotErr, err.Error())
}
- url := appUrl + "user-management/" + strconv.Itoa(tc.payload.ID)
+ url := appURL + "user-management/" + strconv.Itoa(tc.payload.ID)
req, httpReqErr := http.NewRequest(http.MethodPut, url, bytes.NewReader(jsonObject))
if httpReqErr != nil || req == nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -458,28 +457,28 @@ func Test_Update(t *testing.T) {
req.Close = true
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
- t.Error("should equal", resp.StatusCode)
+ t.Error(constants.ShouldEqual, resp.StatusCode)
}
if tc.payload != nil {
respModel := response.Response{}
decodeErr := json.NewDecoder(resp.Body).Decode(&respModel)
if decodeErr != nil && decodeErr != io.EOF {
- t.Error("should not error", decodeErr)
+ t.Error(constants.ShouldNotErr, decodeErr)
}
}
}
}
-func Test_Delete(t *testing.T) {
+func TestDelete(t *testing.T) {
c := helper.NewFiberCtx()
ctr := userDevController
ctx := c.Context()
if ctr == nil || c == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
c.Method(http.MethodPut)
c.Request().Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -528,10 +527,10 @@ func Test_Delete(t *testing.T) {
for _, tc := range testCases {
log.Println(tc.caseName)
- url := appUrl + "user-management/" + strconv.Itoa(tc.paramID)
+ url := appURL + "user-management/" + strconv.Itoa(tc.paramID)
req, httpReqErr := http.NewRequest(http.MethodDelete, url, nil)
if httpReqErr != nil || req == nil {
- t.Fatal("should not nil")
+ t.Fatal(constants.ShouldNotNil)
}
req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
@@ -540,11 +539,11 @@ func Test_Delete(t *testing.T) {
req.Close = true
resp, err := app.Test(req, -1)
if err != nil {
- t.Fatal("should not error")
+ t.Fatal(constants.ShouldNotErr)
}
defer resp.Body.Close()
if resp.StatusCode != tc.respCode {
- t.Error("should equal", resp.StatusCode)
+ t.Error(constants.ShouldEqual, resp.StatusCode)
}
}
diff --git a/database/connector/connector.go b/database/connector/connector.go
index 89b8baa..2cc6df2 100644
--- a/database/connector/connector.go
+++ b/database/connector/connector.go
@@ -19,7 +19,9 @@ var (
redisDatastoreOnce sync.Once
)
-// SQL Database
+// LoadDatabase func read env intenal package and
+// give database connection using gorm package,
+// also pings the DB before the function end.
func LoadDatabase() *gorm.DB {
gormDatabaseOnce.Do(func() {
// try to read env
@@ -60,8 +62,10 @@ func LoadDatabase() *gorm.DB {
return gormDatabase
}
-// Redis
-func LoadRedisDatabase() *redis.Client {
+// LoadRedisCache func read env intenal package and
+// give redis connection using redis external package,
+// also pings the DB before the function end.
+func LoadRedisCache() *redis.Client {
redisDatastoreOnce.Do(func() {
env.ReadConfig("./.env")
config := env.Configuration()
diff --git a/database/connector/connector_test.go b/database/connector/connector_test.go
index c08e071..ed9aa3b 100644
--- a/database/connector/connector_test.go
+++ b/database/connector/connector_test.go
@@ -18,8 +18,8 @@ func TestLoadDatabase(t *testing.T) {
}
}
-func TestLoadRedisDatabase(t *testing.T) {
- rds := LoadRedisDatabase()
+func TestLoadRedisCache(t *testing.T) {
+ rds := LoadRedisCache()
if rds == nil {
t.Error("rds shouldn't nil")
}
diff --git a/database/migration/main.go b/database/migration/main.go
index 05ddb40..66fa850 100644
--- a/database/migration/main.go
+++ b/database/migration/main.go
@@ -22,13 +22,14 @@ func setup() {
config = env.Configuration()
}
-// be careful using this
-// this will delete entire DB tables,
-// and recreate from the beginning
+// ⚠️ Do not run this on production.
+// ⚠️ Warning: This script will drop all tables in Database.
+// This script is designed to perform a complete reset of the database,
+// which involves the deletion of all existing tables and recreating them from scratch.
func main() {
setup()
log.Println("Start Migration")
- defer log.Println("Finish Migration: success no error")
+ defer log.Println("Finish Migration: success with no error")
// delete all tables if not in production
if !config.GetAppInProduction() {
@@ -61,6 +62,7 @@ func main() {
}
}
+// dropAll func drops all tables that listed in entity.AllTables()
func dropAll() {
log.Println("Warning: dropping all tables in 9 seconds (CTRL+C to stop)")
time.Sleep(10 * time.Second)
@@ -71,6 +73,9 @@ func dropAll() {
}
}
+// seeding func seed data like role, permission,
+// and role_has_permissions tables. You can add
+// more seed if you want.
func seeding() {
// Create a new transaction for seeding
tx := db.Begin()
@@ -86,7 +91,7 @@ func seeding() {
}
}
for _, perm := range rbac.AllPermissions() {
- perm.SetCreateTimes()
+ perm.SetCreateTime()
perm.ID = 0
if createErr := tx.Create(&perm).Error; createErr != nil {
tx.Rollback()
diff --git a/devnote.md b/devnote.md
deleted file mode 100644
index 2b566f1..0000000
--- a/devnote.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# DevNote
-
-### Todo / OnDev
-
-1. Add new feature file management (upload and download)
-
-### Read List
-
-1. Database connection conf : https://www.alexedwards.net/blog/configuring-sqldb
-2. GoFiber Test https://dev.to/koddr/go-fiber-by-examples-testing-the-application-1ldf
-3. https://aleksei-kornev.medium.com/production-readiness-checklist-for-backend-applications-8d2b0c57ccec/
-4. https://last9.io/blog/deployment-readiness-checklists/
-5. https://github.com/gorrion-io/production-readiness-checklist/
-6. https://www.cockroachlabs.com/docs/cockroachcloud/production-checklist/
-7. https://www.alexedwards.net/blog/ci-with-go-and-github-actions
-8. https://roadmap.sh/best-practices/api-security/
diff --git a/docs/Gost Project Docs.postman_collection.json b/docs/Gost Project Docs.postman_collection.json
new file mode 100644
index 0000000..ee57c4e
--- /dev/null
+++ b/docs/Gost Project Docs.postman_collection.json
@@ -0,0 +1,1419 @@
+{
+ "info": {
+ "_postman_id": "269648ff-f4c8-4685-8a64-211226f94ad6",
+ "name": "Gost Project Docs",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
+ "_exporter_id": "16420382"
+ },
+ "item": [
+ {
+ "name": "User Management",
+ "item": [
+ {
+ "name": "Create",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"John Doe\",\r\n \"email\": \"your_valid_active@gmail.com\",\r\n \"password\": \"password00\",\r\n \"is_admin\": true // if false -> user\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/user-management/create",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user-management",
+ "create"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:9009/user-management/1",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "9009",
+ "path": [
+ "user-management",
+ "1"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get All",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/user-management/?page=1&limit=100&search=",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user-management",
+ ""
+ ],
+ "query": [
+ {
+ "key": "page",
+ "value": "1"
+ },
+ {
+ "key": "limit",
+ "value": "100"
+ },
+ {
+ "key": "search",
+ "value": ""
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Update",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "PUT",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"John Doe Key\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/user-management/1",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user-management",
+ "1"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Delete",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/user-management/1",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user-management",
+ "1"
+ ]
+ }
+ },
+ "response": []
+ }
+ ],
+ "description": "CRUD User without authentication."
+ },
+ {
+ "name": "Development",
+ "item": [
+ {
+ "name": "Ping SQL DB",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/development/ping/db",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "development",
+ "ping",
+ "db"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Ping Redis",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/development/ping/redis",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "development",
+ "ping",
+ "redis"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Test Panic Handler",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/development/panic",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "development",
+ "panic"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Set to Redis",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/development/storing-to-redis",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "development",
+ "storing-to-redis"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get from Redis",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/development/get-from-redis",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "development",
+ "get-from-redis"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "_Test New Role (unused)",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiZW1haWwiOiJ1bnN1cmx1a21hbkBnbWFpbC5jb20iLCJyb2xlIjoibmV3LXJvbGUtMDAyIiwicGVybWlzc2lvbnMiOnsiMyI6Mjh9LCJleHAiOjE3MDA0NjAyMDIsIm5iZiI6MTY5OTYwMTAwMn0.WO9OnVLct8Ta-u3fuC5TZPzgwviFiEWvtALplsF_KNpH_LoGujjB_Aa55_eHaIQwg0b1Wycb4ntaojSG40Wiut7LN9Uizb17Je1ewUw7HZmZp-HU7dWvRDYi68FvCP1ra0VZ1BnFzs8d8gYzPJ0oUtRBs3oySJTULZaW_zN07M8",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/development/test-new-role",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "development",
+ "test-new-role"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "_Test New Permission (unused)",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiZW1haWwiOiJ1bnN1cmx1a21hbkBnbWFpbC5jb20iLCJyb2xlIjoibmV3LXJvbGUtMDAyIiwicGVybWlzc2lvbnMiOnsiMyI6Mjh9LCJleHAiOjE3MDA0NjAyMDIsIm5iZiI6MTY5OTYwMTAwMn0.WO9OnVLct8Ta-u3fuC5TZPzgwviFiEWvtALplsF_KNpH_LoGujjB_Aa55_eHaIQwg0b1Wycb4ntaojSG40Wiut7LN9Uizb17Je1ewUw7HZmZp-HU7dWvRDYi68FvCP1ra0VZ1BnFzs8d8gYzPJ0oUtRBs3oySJTULZaW_zN07M8",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/development/test-new-permission",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "development",
+ "test-new-permission"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "List All Files",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"limit\": 9999,\r\n \"offset\": 1,\r\n \"prefix\": \"\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "https://SECRET-UNIQUE-STRING.supabase.co/storage/v1/object/list/user_upload_public",
+ "protocol": "https",
+ "host": [
+ "SECRET-UNIQUE-STRING",
+ "supabase",
+ "co"
+ ],
+ "path": [
+ "storage",
+ "v1",
+ "object",
+ "list",
+ "user_upload_public"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "List All Files API",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": ""
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/development/get-files-list",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "development",
+ "get-files-list"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Upload File",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "file",
+ "type": "file",
+ "src": "/C:/Users/Lenovo/OneDrive/Desktop/Sec/Best Practices for MITRE ATT&CK Mapping.pdf"
+ },
+ {
+ "key": "",
+ "value": "",
+ "type": "text",
+ "disabled": true
+ }
+ ]
+ },
+ "url": {
+ "raw": "https://tntrwzoefhqqauzrttpe.supabase.co/storage/v1/object/user_upload_public/Best Practices for MITRE ATT&CK Mapping.pdf",
+ "protocol": "https",
+ "host": [
+ "tntrwzoefhqqauzrttpe",
+ "supabase",
+ "co"
+ ],
+ "path": [
+ "storage",
+ "v1",
+ "object",
+ "user_upload_public",
+ "Best Practices for MITRE ATT&CK Mapping.pdf"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Upload File API",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "file",
+ "type": "file",
+ "src": "/C:/Users/Lenovo/OneDrive/Desktop/Sec/Hack Book/cyber-security.pdf"
+ },
+ {
+ "key": "",
+ "value": "",
+ "type": "text",
+ "disabled": true
+ }
+ ]
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/development/upload-file",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "development",
+ "upload-file"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Remove File",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"prefixes\": \"Best Practices for MITRE ATT&CK Mapping.pdf\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "https://tntrwzoefhqqauzrttpe.supabase.co/storage/v1/object/user_upload_public",
+ "protocol": "https",
+ "host": [
+ "tntrwzoefhqqauzrttpe",
+ "supabase",
+ "co"
+ ],
+ "path": [
+ "storage",
+ "v1",
+ "object",
+ "user_upload_public"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Remove File API",
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"file_name\": \"uploaded-file.pdf\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/development/remove-file",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "development",
+ "remove-file"
+ ]
+ }
+ },
+ "response": []
+ }
+ ],
+ "description": "StartFragmentDevelopment Routes provides experimental/ developing/ testingfor routes, middleware, connection and many more without JWT authentication in header.\n\nSo, don't forget to commentedon the line of code that routes **getDevopmentRouterin the app.go.**"
+ },
+ {
+ "name": "User",
+ "item": [
+ {
+ "name": "Register",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"John Doe\",\r\n \"email\": \"your_valid_email@gmail.com\", // your valid & active email\r\n \"password\": \"password00\",\r\n \"role_id\": 1\r\n // role_id 1 == admin, 2 == user, see at database/migration golang-code.\r\n}\r\n\r\n",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/user/register",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user",
+ "register"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Verification / Account Activation",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"email\": \"your_valid_email@gmail.com\", // your valid & active email\r\n \"code\": \"QQFjl5ZNSEmrfGK6OOoF1\" // check your email inbox/ spam\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/user/verification",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user",
+ "verification"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Req. Delete Account from Activation",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"email\": \"lukmanernandi16@gmail.com\",\r\n \"code\": \"qJtHrTP1Yzy6N9UcgL8e0\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/user/request-delete",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user",
+ "request-delete"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Login",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"email\": \"your_valid_email@gmail.com\", // your valid & active email\r\n \"password\": \"password00\",\r\n \"ip\": \"128.0.0.18\"\r\n // After login you can copy-paste the token to https://jwt.io/ too see \r\n // and understand the structure of claims struct (see internal/middleware).\r\n}\r\n",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/user/login",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user",
+ "login"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Forgot Password",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"email\": \"your_valid_email@gmail.com\", // your valid & active email\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/user/forget-password",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user",
+ "forget-password"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Reset Password",
+ "request": {
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"email\": \"your_valid_email@gmail.com\", // your valid & active email\r\n \"code\": \"uH7b9bKO4kxY96NYwuQPQ\", // check your email inbox / spam\r\n \"new_password\": \"password99\",\r\n \"new_password_confirm\": \"password99\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/user/reset-password",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user",
+ "reset-password"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "My Profile",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/user/my-profile",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user",
+ "my-profile"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Logout",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/user/logout",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user",
+ "logout"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Profile Update",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "PUT",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"John Doe (updated)\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/user/profile-update",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user",
+ "profile-update"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Update Password",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"old_password\": \"password00\",\r\n \"new_password\": \"password000\", // should different with old passwd\r\n \"new_password_confirm\": \"password000\" // should equal with new passwd\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/user/update-password",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "user",
+ "update-password"
+ ]
+ }
+ },
+ "response": []
+ }
+ ],
+ "description": "StartFragmentUser Management Routes provides create, read (get & getAll), update, anddelete functionalities for user data management without JWT authenticationin header. So, don't forget to commented on the line of code that routesgetUserManagementRoutes in the app.go."
+ },
+ {
+ "name": "Role and Permission",
+ "item": [
+ {
+ "name": "Create Role",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"new-role-001\",\r\n \"description\": \"new-role-001 description\",\r\n \"permissions_id\": [1, 2, 3]\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/role",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "role"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Connect Role to Permissions",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"role_id\": 3,\r\n \"permissions_id\": [19,20,21]\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/role/connect",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "role",
+ "connect"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get Role",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/role/1",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "role",
+ "1"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get All Roles",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJsdWttYW5lcm5hbmRpMTZAZ21haWwuY29tIiwicm9sZSI6ImFkbWluIiwicGVybWlzc2lvbnMiOnsiMSI6MjU1LCIyIjoyNTUsIjMiOjE1fSwiZXhwIjoxNzAwNDYwNTY1LCJuYmYiOjE2OTk2MDEzNjV9.XdcZVnEmedlaYhWgNjGYPJn9tm9H4jEbkleIKCoE5bDqGj5Zcv4UjSebkCWOCSzNpiCEMVsePuXgW8bRn9r6JDgCAXgsneUvVJHIXwh5FmtOghqXU8E06PsJ2ajkkiSqR9yFh_xI2AsQ6TKk-RM_-DfeRZKt9uryZ5Iur_gaL9M",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/role?page=1&limit=20&search=",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "role"
+ ],
+ "query": [
+ {
+ "key": "page",
+ "value": "1"
+ },
+ {
+ "key": "limit",
+ "value": "20"
+ },
+ {
+ "key": "search",
+ "value": ""
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Update Role",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJsdWttYW5lcm5hbmRpMTZAZ21haWwuY29tIiwicm9sZSI6ImFkbWluIiwicGVybWlzc2lvbnMiOnsiMSI6MjU1LCIyIjoyNTUsIjMiOjE1fSwiZXhwIjoxNzAwNDYwNTY1LCJuYmYiOjE2OTk2MDEzNjV9.XdcZVnEmedlaYhWgNjGYPJn9tm9H4jEbkleIKCoE5bDqGj5Zcv4UjSebkCWOCSzNpiCEMVsePuXgW8bRn9r6JDgCAXgsneUvVJHIXwh5FmtOghqXU8E06PsJ2ajkkiSqR9yFh_xI2AsQ6TKk-RM_-DfeRZKt9uryZ5Iur_gaL9M",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "PUT",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\" : \"admin-update\",\r\n \"description\": \"description menyusul saja ya ...\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/role/1",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "role",
+ "1"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Delete Role",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJsdWttYW5lcm5hbmRpMTZAZ21haWwuY29tIiwicm9sZSI6ImFkbWluIiwicGVybWlzc2lvbnMiOnsiMSI6MjU1LCIyIjoyNTUsIjMiOjE1fSwiZXhwIjoxNzAwNDYwNTY1LCJuYmYiOjE2OTk2MDEzNjV9.XdcZVnEmedlaYhWgNjGYPJn9tm9H4jEbkleIKCoE5bDqGj5Zcv4UjSebkCWOCSzNpiCEMVsePuXgW8bRn9r6JDgCAXgsneUvVJHIXwh5FmtOghqXU8E06PsJ2ajkkiSqR9yFh_xI2AsQ6TKk-RM_-DfeRZKt9uryZ5Iur_gaL9M",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"test update\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/role/3",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "role",
+ "3"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Create Permission",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "POST",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"new-permission-001\",\r\n \"description\": \"new-permission-001 Description\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/permission",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "permission"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get Permission",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJleGFtcGxlMkBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsInBlcm1pc3Npb25zIjpbImNyZWF0ZS11c2VyIiwidmlldy11c2VyIiwidXBkYXRlLXVzZXIiLCJkZWxldGUtdXNlciIsImNyZWF0ZS1yb2xlIiwidmlldy1yb2xlIiwidXBkYXRlLXJvbGUiLCJkZWxldGUtcm9sZSIsImNyZWF0ZS11c2VyLWhhcy1yb2xlIiwidmlldy11c2VyLWhhcy1yb2xlIiwidXBkYXRlLXVzZXItaGFzLXJvbGUiLCJkZWxldGUtdXNlci1oYXMtcm9sZSIsImNyZWF0ZS1wZXJtaXNzaW9uIiwicmVhZC1wZXJtaXNzaW9uIiwidXBkYXRlLXBlcm1pc3Npb24iLCJkZWxldGUtcGVybWlzc2lvbiIsImNyZWF0ZS1yb2xlLWhhcy1wZXJtaXNzaW9ucyIsInZpZXctcm9sZS1oYXMtcGVybWlzc2lvbnMiLCJ1cGRhdGUtcm9sZS1oYXMtcGVybWlzc2lvbnMiLCJkZWxldGUtcm9sZS1oYXMtcGVybWlzc2lvbnMiLCJjcmVhdGUtb25lIiwidmlldy1vbmUiLCJ1cGRhdGUtb25lIiwiZGVsZXRlLW9uZSIsImNyZWF0ZS10d28iLCJ2aWV3LXR3byIsInVwZGF0ZS10d28iLCJkZWxldGUtdHdvIiwiY3JlYXRlLXRocmVlIiwidmlldy10aHJlZSIsInVwZGF0ZS10aHJlZSIsImRlbGV0ZS10aHJlZSIsImNyZWF0ZS1mb3VyIiwidmlldy1mb3VyIiwidXBkYXRlLWZvdXIiLCJkZWxldGUtZm91ciIsImNyZWF0ZS1maXZlIiwidmlldy1maXZlIiwidXBkYXRlLWZpdmUiLCJkZWxldGUtZml2ZSIsImNyZWF0ZS1zaXgiLCJ2aWV3LXNpeCIsInVwZGF0ZS1zaXgiLCJkZWxldGUtc2l4IiwiY3JlYXRlLXNldmVuIiwidmlldy1zZXZlbiIsInVwZGF0ZS1zZXZlbiIsImRlbGV0ZS1zZXZlbiJdLCJsYWJlbCI6bnVsbCwiZXhwIjoxNjk3OTQyNDkxLCJuYmYiOjE2OTcwODMyOTF9.ZMVHIXp9-e5JgTQxT2xf-exZlUc3b04gTAJy-kBIylAFHf92tmDrRvYt6o4z8UKGvu5OiUskSEplXPQ4h1cOAjV6g3JE4Zs8GBdj4t453rt7G54j7a2eDAd1Wv6bhLQvx-LtX_vGQHFWcW4ivz-p0FvUe5BP57S3ONXgb1P8ORk",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/permission/1",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "permission",
+ "1"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get All Permissions",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://127.0.0.1:9009/permission?page=1&limit=20&search=",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "permission"
+ ],
+ "query": [
+ {
+ "key": "page",
+ "value": "1"
+ },
+ {
+ "key": "limit",
+ "value": "20"
+ },
+ {
+ "key": "search",
+ "value": ""
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Update Permission",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "PUT",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"test update\",\r\n \"description\": \"xxx\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/permission/1",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "permission",
+ "1"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Delete Permission",
+ "request": {
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "",
+ "type": "string"
+ }
+ ]
+ },
+ "method": "DELETE",
+ "header": [],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"name\": \"test update\"\r\n}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "http://127.0.0.1:9009/permission/1",
+ "protocol": "http",
+ "host": [
+ "127",
+ "0",
+ "0",
+ "1"
+ ],
+ "port": "9009",
+ "path": [
+ "permission",
+ "1"
+ ]
+ }
+ },
+ "response": []
+ }
+ ],
+ "description": "StartFragmentRole-Permission Routes provides des create, read (get & getAll), update, anddelete functionalities for Role and Permission entities including connectingboth of them. This routes can be access by user that has admin-role (see database/migration)."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/domain/base/request.go b/domain/base/request.go
index bd552ef..fad61e6 100644
--- a/domain/base/request.go
+++ b/domain/base/request.go
@@ -1,5 +1,6 @@
package base
+// RequestGetAll struct used for request getAll controller funcs
type RequestGetAll struct {
Page int `query:"page"`
Limit int `query:"limit"`
diff --git a/domain/base/response.go b/domain/base/response.go
index 628108c..e6f4717 100644
--- a/domain/base/response.go
+++ b/domain/base/response.go
@@ -6,6 +6,7 @@ type PageMeta struct {
Page int `json:"page"`
}
+// GetAllResponse struct used for response getAll controller funcs
type GetAllResponse struct {
Meta PageMeta `json:"meta"`
Data interface{} `json:"data"`
diff --git a/domain/base/time.go b/domain/base/time.go
index e7f7909..acbe6f2 100644
--- a/domain/base/time.go
+++ b/domain/base/time.go
@@ -2,17 +2,25 @@ package base
import "time"
+// TimeFields struct used by almost all entity.
+// This struct give stabillity for creating more and more entity/ struct/ table.
+// This struct prevents developers from making typing errors / typo.
type TimeFields struct {
CreatedAt *time.Time `gorm:"type:timestamp null;default:null" json:"created_at"`
UpdatedAt *time.Time `gorm:"type:timestamp null;default:null" json:"updated_at"`
}
-func (att *TimeFields) SetCreateTimes() {
+// SetCreateTime func fills created_at and updated_at fields
+// This struct prevents developers from forgets or any common mistake.
+func (att *TimeFields) SetCreateTime() {
timeNow := time.Now()
att.CreatedAt = &timeNow
att.UpdatedAt = &timeNow
}
+// SetUpdateTime func fills updated_at fields
+// This struct prevents developers from forgets
+// or any common mistake.
func (att *TimeFields) SetUpdateTime() {
timeNow := time.Now()
att.UpdatedAt = &timeNow
diff --git a/domain/base/time_test.go b/domain/base/time_test.go
index 5016179..73354b5 100644
--- a/domain/base/time_test.go
+++ b/domain/base/time_test.go
@@ -2,10 +2,9 @@ package base
import (
"testing"
- "time"
)
-func TestTimeFields_SetCreateTimes(t *testing.T) {
+func TestTimeFields_SetCreateTime(t *testing.T) {
tests := []struct {
name string
att *TimeFields
@@ -18,14 +17,10 @@ func TestTimeFields_SetCreateTimes(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- tt.att.SetCreateTimes()
- currentTime := time.Now()
+ tt.att.SetCreateTime()
if tt.att.CreatedAt == nil || tt.att.UpdatedAt == nil {
t.Errorf("Expected CreatedAt and UpdatedAt to be set, but one or both are nil")
}
- if !tt.att.CreatedAt.Equal(currentTime) || !tt.att.UpdatedAt.Equal(currentTime) {
- t.Errorf("Expected CreatedAt and UpdatedAt to be equal to the current time")
- }
})
}
}
@@ -44,13 +39,9 @@ func TestTimeFields_SetUpdateTime(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.att.SetUpdateTime()
- currentTime := time.Now()
if tt.att.UpdatedAt == nil {
t.Errorf("Expected UpdatedAt to be set, but it is nil")
}
- if !tt.att.UpdatedAt.Equal(currentTime) {
- t.Errorf("Expected UpdatedAt to be equal to the current time")
- }
})
}
}
diff --git a/domain/entity/all_entities.go b/domain/entity/all_entities.go
index 848eb95..0105969 100644
--- a/domain/entity/all_entities.go
+++ b/domain/entity/all_entities.go
@@ -1,9 +1,22 @@
+// ⚠️ Don't forget to Add Your new
+// Table Here : must sorted by developer.
+
+// Package entity contains all the structs that will be used to build tables in the database.
package entity
-// Don't forget to Add Your new
-// Table Here : must sorted by developer.
-func AllTables() []interface{} {
- return []any{
+import "log"
+
+// Table interface is contract that make developer
+// not forget to add TableName method for struct
+type Table interface {
+ TableName() string
+}
+
+// AllTables func serve/ return all structs that
+// developer has been created. This func used in
+// database migration scripts.
+func AllTables() []any {
+ allTables := []any{
&User{},
&UserHasRoles{},
&Role{},
@@ -13,4 +26,11 @@ func AllTables() []interface{} {
// ...
// Add more tables/structs
}
+ for _, table := range allTables {
+ _, ok := table.(Table)
+ if !ok {
+ log.Fatal("please add TableName() func to all structs")
+ }
+ }
+ return allTables
}
diff --git a/domain/entity/all_entities_test.go b/domain/entity/all_entities_test.go
index 3da956f..4aac3e5 100644
--- a/domain/entity/all_entities_test.go
+++ b/domain/entity/all_entities_test.go
@@ -5,6 +5,7 @@ import (
"time"
"github.com/Lukmanern/gost/domain/base"
+ "github.com/Lukmanern/gost/internal/constants"
)
func TestAllTablesName(t *testing.T) {
@@ -39,9 +40,9 @@ func TestUserSetActivateAccount(t *testing.T) {
user.SetActivateAccount()
if user.VerificationCode != nil {
- t.Error("should nil")
+ t.Error(constants.ShouldNil)
}
if user.ActivatedAt == nil {
- t.Error("should nil")
+ t.Error(constants.ShouldNil)
}
}
diff --git a/domain/entity/rbac.go b/domain/entity/role_permission.go
similarity index 90%
rename from domain/entity/rbac.go
rename to domain/entity/role_permission.go
index 6042aca..cacd618 100644
--- a/domain/entity/rbac.go
+++ b/domain/entity/role_permission.go
@@ -1,3 +1,6 @@
+// ⚠️ Don't forget to Add Your new Table
+// at AllTables func at all_entities.go file
+
package entity
import "github.com/Lukmanern/gost/domain/base"
diff --git a/domain/entity/user.go b/domain/entity/user.go
index 5e304f3..fb7b47c 100644
--- a/domain/entity/user.go
+++ b/domain/entity/user.go
@@ -1,3 +1,6 @@
+// ⚠️ Don't forget to Add Your new Table
+// at AllTables func at all_entities.go file
+
package entity
import (
diff --git a/domain/model/rbac.go b/domain/model/role_permission.go
similarity index 95%
rename from domain/model/rbac.go
rename to domain/model/role_permission.go
index a327f5a..fd23d2f 100644
--- a/domain/model/rbac.go
+++ b/domain/model/role_permission.go
@@ -1,6 +1,5 @@
package model
-// Role
type RoleCreate struct {
Name string `validate:"required,min=1,max=60" json:"name"`
Description string `validate:"required,min=1,max=100" json:"description"`
@@ -24,7 +23,6 @@ type RoleConnectToPermissions struct {
PermissionsID []int `validate:"required" json:"permissions_id"`
}
-// Permission
type PermissionCreate struct {
Name string `validate:"required,min=1,max=60" json:"name"`
Description string `validate:"required,min=1,max=100" json:"description"`
diff --git a/domain/model/user.go b/domain/model/user.go
index 589bf33..80f0457 100644
--- a/domain/model/user.go
+++ b/domain/model/user.go
@@ -25,6 +25,7 @@ type UserForgetPassword struct {
}
type UserResetPassword struct {
+ Email string `validate:"required,email,min=5,max=60" json:"email"`
Code string `validate:"required,min=21,max=60" json:"code"`
NewPassword string `validate:"required,min=8,max=30" json:"new_password"`
NewPasswordConfirm string `validate:"required,min=8,max=30" json:"new_password_confirm"`
diff --git a/go.mod b/go.mod
index 2ef8537..23baf07 100644
--- a/go.mod
+++ b/go.mod
@@ -3,34 +3,26 @@ module github.com/Lukmanern/gost
go 1.20
require (
+ github.com/go-playground/validator/v10 v10.15.5
github.com/go-redis/redis v6.15.9+incompatible
github.com/gofiber/fiber/v2 v2.50.0
- github.com/ilyakaznacheev/cleanenv v1.5.0
- gorm.io/gorm v1.25.4
-)
-
-require (
- github.com/go-playground/validator/v10 v10.15.5
github.com/golang-jwt/jwt/v5 v5.0.0
+ github.com/ilyakaznacheev/cleanenv v1.5.0
github.com/stretchr/testify v1.8.4
github.com/valyala/fasthttp v1.50.0
golang.org/x/crypto v0.14.0
- gorm.io/driver/postgres v1.5.3
-)
-
-require (
- github.com/gabriel-vasile/mimetype v1.4.2 // indirect
- github.com/go-playground/locales v0.14.1 // indirect
- github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/leodido/go-urn v1.2.4 // indirect
- golang.org/x/net v0.17.0 // indirect
golang.org/x/text v0.13.0
+ gorm.io/driver/postgres v1.5.3
+ gorm.io/gorm v1.25.4
)
require (
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.2 // indirect
+ github.com/go-playground/locales v0.14.1 // indirect
+ github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
@@ -40,6 +32,7 @@ require (
github.com/joho/godotenv v1.5.1 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/kr/text v0.2.0 // indirect
+ github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
@@ -50,6 +43,7 @@ require (
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
+ golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect
diff --git a/internal/constants/constants.go b/internal/constants/constants.go
new file mode 100644
index 0000000..94ab46a
--- /dev/null
+++ b/internal/constants/constants.go
@@ -0,0 +1,19 @@
+// Constants used for error messages and testing assertions.
+
+package constants
+
+const (
+ Unauthorized = "should unauthorized"
+ RedisNil = "redis nil value"
+ NotFound = "data not found"
+ ServerErr = "internal server error: "
+ InvalidID = "invalid id"
+ InvalidBody = "invalid json body: "
+
+ ShouldErr = "should error"
+ ShouldNotErr = "should not error"
+ ShouldNil = "should nil"
+ ShouldNotNil = "should not nil"
+ ShouldEqual = "should equal"
+ ShouldNotEqual = "should not equal"
+)
diff --git a/internal/env/env.go b/internal/env/env.go
index 685e844..b722255 100644
--- a/internal/env/env.go
+++ b/internal/env/env.go
@@ -14,11 +14,10 @@ import (
type Config struct {
AppName string `env:"APP_NAME"`
AppInProduction bool `env:"APP_IN_PRODUCTION"`
- AppKey string `env:"APP_SECRET_KEY"`
AppAccessTokenTTL time.Duration `env:"APP_ACCESS_TOKEN_TTL"`
AppPort int `env:"APP_PORT"`
AppTimeZone string `env:"APP_TIME_ZONE"`
- AppUrl string
+ AppURL string
DatabaseHost string `env:"DB_HOST"`
DatabasePort string `env:"DB_PORT"`
@@ -57,6 +56,7 @@ var (
paths = []string{"", "./..", "./../..", "./../../.."}
)
+// ReadConfig func check .env file and read the value.
func ReadConfig(filePath string) *Config {
cfgOnce.Do(func() {
if _, err := os.Stat(filePath); os.IsNotExist(err) {
@@ -75,6 +75,8 @@ func ReadConfig(filePath string) *Config {
return &cfg
}
+// Configuration func set and update .env file
+// and returning config it self.
func Configuration() Config {
if envFile == nil {
log.Panic(`configuration file is not set. Call ReadConfig("path_to_file") first`)
@@ -83,10 +85,11 @@ func Configuration() Config {
if err != nil {
log.Fatalf("Config error %s", err.Error())
}
- cfg.setAppUrl()
+ cfg.setAppURL()
return cfg
}
+// GetDatabaseURI func return string URI of database.
func (c *Config) GetDatabaseURI() string {
c.DatabaseURI = fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=%s",
c.DatabaseHost, c.DatabaseUser, c.DatabasePassword, c.DatabaseName, c.DatabasePort, c.AppTimeZone,
@@ -95,10 +98,19 @@ func (c *Config) GetDatabaseURI() string {
return c.DatabaseURI
}
+// GetAppInProduction func return application condition is
+// under development or production ready.
func (c *Config) GetAppInProduction() bool {
return c.AppInProduction
}
+// setAppURL func combine set AppURL with localhost and port.
+func (c *Config) setAppURL() {
+ localAddr := fmt.Sprintf("http://127.0.0.1:%d/", c.AppPort)
+ c.AppURL = localAddr
+}
+
+// GetPublicKey func gets PublicKey values.
func (c *Config) GetPublicKey() []byte {
PublicKeyReadOne.Do(func() {
var foundPath string
@@ -121,6 +133,7 @@ func (c *Config) GetPublicKey() []byte {
return *PublicKey
}
+// GetPrivateKey func gets PrivateKey values.
func (c *Config) GetPrivateKey() []byte {
PrivateKeyReadOne.Do(func() {
var foundPath string
@@ -143,19 +156,14 @@ func (c *Config) GetPrivateKey() []byte {
return *PrivateKey
}
-func (c *Config) setAppUrl() {
- localAddr := fmt.Sprintf("http://127.0.0.1:%d/", c.AppPort)
- c.AppUrl = localAddr
-}
-
+// ShowConfig prints all fields that Config struct has
func (c *Config) ShowConfig() {
fmt.Printf("%-21s: %s\n", "AppName", c.AppName)
fmt.Printf("%-21s: %v\n", "AppInProduction", c.AppInProduction)
- fmt.Printf("%-21s: %s\n", "AppKey", c.AppKey)
fmt.Printf("%-21s: %s\n", "AppAccessTokenTTL", c.AppAccessTokenTTL)
fmt.Printf("%-21s: %d\n", "AppPort", c.AppPort)
fmt.Printf("%-21s: %s\n", "AppTimeZone", c.AppTimeZone)
- fmt.Printf("%-21s: %s\n", "AppUrl", c.AppUrl)
+ fmt.Printf("%-21s: %s\n", "AppURL", c.AppURL)
fmt.Printf("%-21s: %s\n", "DatabaseHost", c.DatabaseHost)
fmt.Printf("%-21s: %s\n", "DatabasePort", c.DatabasePort)
@@ -174,4 +182,7 @@ func (c *Config) ShowConfig() {
fmt.Printf("%-21s: %s\n", "SMTPEmail", c.SMTPEmail)
fmt.Printf("%-21s: %s\n", "SMTPPassword", c.SMTPPassword)
fmt.Printf("%-21s: %s\n", "ClientURL", c.ClientURL)
+
+ // ...
+ // add more
}
diff --git a/internal/env/env_test.go b/internal/env/env_test.go
index 22bf8fa..c627a4e 100644
--- a/internal/env/env_test.go
+++ b/internal/env/env_test.go
@@ -3,6 +3,8 @@ package env
import (
"net/url"
"testing"
+
+ "github.com/stretchr/testify/assert"
)
var (
@@ -14,74 +16,39 @@ func TestReadConfigAndConfiguration(t *testing.T) {
ReadConfig(validPath)
c := Configuration()
- if c == config {
- t.Error("Expected configuration to be initialized, but it is null")
- }
-
- if c.AppKey == "" {
- t.Error("AppKey is empty; it should have a valid value")
- }
-
- if c.AppPort < 1 {
- t.Error("AppPort is less than 1; it should be a positive integer")
- }
-
- if c.DatabasePort == "" {
- t.Error("DatabasePort is empty; it should have a valid value")
- }
-
- if c.PublicKey == "" {
- t.Error("PublicKey is empty; it should have a valid value")
- }
-
- if c.PrivateKey == "" {
- t.Error("PrivateKey is empty; it should have a valid value")
- }
-
- if c.RedisURI == "" {
- t.Error("RedisURI is empty; it should have a valid value")
- }
-
- if c.SMTPPort < 1 {
- t.Error("SMTPPort is less than 1; it should be a positive integer")
- }
+ assert.NotEqual(t, c, config, "Expected configuration to be initialized, but it is null")
+ assert.True(t, c.AppPort >= 1, "AppPort is less than 1; it should be a positive integer")
+ assert.NotEmpty(t, c.DatabasePort, "DatabasePort is empty; it should have a valid value")
+ assert.NotEmpty(t, c.PublicKey, "PublicKey is empty; it should have a valid value")
+ assert.NotEmpty(t, c.PrivateKey, "PrivateKey is empty; it should have a valid value")
+ assert.NotEmpty(t, c.RedisURI, "RedisURI is empty; it should have a valid value")
+ assert.True(t, c.SMTPPort >= 1, "SMTPPort is less than 1; it should be a positive integer")
}
-func TestConfig_GetPublicKeyAndGetPrivateKey(t *testing.T) {
+func TestConfigGetPublicKeyAndGetPrivateKey(t *testing.T) {
defer func() {
r := recover()
- if r != nil {
- t.Error("should not panic")
- }
+ assert.Nil(t, r, "should not panic")
}()
ReadConfig(validPath)
c := Configuration()
- dbUri := c.GetDatabaseURI()
- if len(dbUri) < 1 {
- t.Error("should more long")
- }
+ rawDbURI := "host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=%s"
+ dbURI := c.GetDatabaseURI()
+ assert.True(t, len(dbURI) >= len(rawDbURI), "should be more long")
isProd := c.GetAppInProduction()
- if c.AppInProduction != isProd {
- t.Error("Expected AppInProduction to match the configuration, but it doesn't")
- }
+ assert.Equal(t, c.AppInProduction, isProd, "Expected AppInProduction to match the configuration, but it doesn't")
pubKey := c.GetPublicKey()
- if pubKey == nil {
- t.Error("Public Key should not be nil")
- }
+ assert.NotNil(t, pubKey, "Public Key should not be nil")
privKey := c.GetPrivateKey()
- if privKey == nil {
- t.Error("Private Key should not be nil")
- }
+ assert.NotNil(t, privKey, "Private Key should not be nil")
c.ShowConfig()
- c.setAppUrl()
- _, parseUrlErr := url.Parse(c.AppUrl)
- if parseUrlErr != nil {
- t.Error("should not error while parsing url")
- }
+ c.setAppURL()
+ _, parseURLErr := url.Parse(c.AppURL)
+ assert.NoError(t, parseURLErr, "should not error while parsing url")
}
diff --git a/internal/hash/hash_test.go b/internal/hash/hash_test.go
index 8cc1cdc..9547614 100644
--- a/internal/hash/hash_test.go
+++ b/internal/hash/hash_test.go
@@ -2,6 +2,8 @@ package hash
import (
"testing"
+
+ "github.com/stretchr/testify/assert"
)
func TestGenerate(t *testing.T) {
@@ -17,9 +19,7 @@ func TestGenerate(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := Generate(tt.data)
- if (err != nil) != tt.wantErr {
- t.Errorf("Generate() error = %v, wantErr %v", err, tt.wantErr)
- }
+ assert.Equal(t, (err != nil), tt.wantErr, "Generate() error")
})
}
}
@@ -43,12 +43,8 @@ func TestVerify(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Verify(tt.hashedPassword, tt.password)
- if (err != nil) != tt.wantErr {
- t.Errorf("Verify() error = %v, wantErr %v", err, tt.wantErr)
- }
- if got != tt.want {
- t.Errorf("Verify() = %v, want %v", got, tt.want)
- }
+ assert.Equal(t, (err != nil), tt.wantErr, "Verify() error")
+ assert.Equal(t, got, tt.want, "Verify() result")
})
}
}
diff --git a/internal/helper/helper.go b/internal/helper/helper.go
index b3779c6..ac7fa44 100644
--- a/internal/helper/helper.go
+++ b/internal/helper/helper.go
@@ -10,10 +10,16 @@ import (
"github.com/gofiber/fiber/v2"
"github.com/valyala/fasthttp"
+ "golang.org/x/text/cases"
+ "golang.org/x/text/language"
)
+// RandomString func generate random string
+// used for testing and any needs.
func RandomString(n uint) string {
- letterBytes := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
+ letterBytes := "abcdefghijklmnopqrstuvwxyz"
+ letterBytes += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ letterBytes += "1234567890"
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
@@ -21,12 +27,14 @@ func RandomString(n uint) string {
return string(b)
}
+// RandomEmails func return some emails
+// used for testing and any needs.
func RandomEmails(n uint) []string {
emailsMap := make(map[string]int)
for uint(len(emailsMap)) < n {
body := strings.ToLower(RandomString(7) + RandomString(7) + RandomString(7))
randEmail := body + "@gost.project"
- emailsMap[randEmail] += 1
+ emailsMap[randEmail]++
}
emails := make([]string, 0, len(emailsMap))
@@ -36,18 +44,16 @@ func RandomEmails(n uint) []string {
return emails
}
+// RandomEmail func return a email
+// used for testing and any needs.
func RandomEmail() string {
body := strings.ToLower(RandomString(7) + RandomString(7) + RandomString(7))
randEmail := body + "@gost.project"
return randEmail
}
-// This used for testing handler : controller/ middleware/ any
-func NewFiberCtx() *fiber.Ctx {
- app := fiber.New()
- return app.AcquireCtx(&fasthttp.RequestCtx{})
-}
-
+// RandomIPAddress func return a IP Address
+// used for testing and any needs.
func RandomIPAddress() string {
source := rand.NewSource(time.Now().UnixNano())
rng := rand.New(source)
@@ -60,6 +66,7 @@ func RandomIPAddress() string {
return ip.String()
}
+// ValidateEmails func validates emails
func ValidateEmails(emails ...string) error {
for _, email := range emails {
_, err := mail.ParseAddress(email)
@@ -69,3 +76,16 @@ func ValidateEmails(emails ...string) error {
}
return nil
}
+
+// NewFiberCtx func create new fiber.Ctx used for testing
+// handler like controller and middleware.
+func NewFiberCtx() *fiber.Ctx {
+ app := fiber.New()
+ return app.AcquireCtx(&fasthttp.RequestCtx{})
+}
+
+// ToTitle func make string to Title Case
+// Example : Your name => Your Name
+func ToTitle(s string) string {
+ return cases.Title(language.Und).String(s)
+}
diff --git a/internal/helper/helper_test.go b/internal/helper/helper_test.go
index 2e8efc5..2ec1c68 100644
--- a/internal/helper/helper_test.go
+++ b/internal/helper/helper_test.go
@@ -4,30 +4,25 @@ import (
"net"
"strings"
"testing"
+
+ "github.com/Lukmanern/gost/internal/constants"
+ "github.com/stretchr/testify/assert"
)
func TestRandomString(t *testing.T) {
for i := 0; i < 25; i++ {
s := RandomString(uint(i))
- if len(s) != i {
- t.Error("len of string should equal")
- }
+ assert.Len(t, s, i, "length of string should equal")
}
}
func TestRandomEmails(t *testing.T) {
for i := 1; i <= 20; i++ {
emails := RandomEmails(uint(i))
- if len(emails) != i {
- t.Error("total of emails should equal")
- }
+ assert.Len(t, emails, i, "total of emails should equal")
for _, email := range emails {
- if len(email) < 10 {
- t.Error("length of an email should not less than 10")
- }
- if email != strings.ToLower(email) {
- t.Error("email should lower by results")
- }
+ assert.GreaterOrEqual(t, len(email), 10, "length of an email should not less than 10")
+ assert.Equal(t, email, strings.ToLower(email), "email should be lowercase")
}
}
}
@@ -35,20 +30,8 @@ func TestRandomEmails(t *testing.T) {
func TestRandomEmail(t *testing.T) {
for i := 0; i < 25; i++ {
email := RandomEmail()
- validateErr := ValidateEmails(email)
- if validateErr != nil {
- t.Error("should not error")
- }
- if len(email) < 25 {
- t.Error("should more than 25")
- }
- }
-}
-
-func TestNewFiberCtx(t *testing.T) {
- c := NewFiberCtx()
- if c == nil {
- t.Error("should not nil")
+ assert.NoError(t, ValidateEmails(email), "should not error")
+ assert.GreaterOrEqual(t, len(email), 25, "should be more than 25")
}
}
@@ -56,30 +39,54 @@ func TestRandomIPAddress(t *testing.T) {
for i := 0; i < 20; i++ {
ipRand := RandomIPAddress()
ip := net.ParseIP(ipRand)
- if ip == nil {
- t.Error("should not nil")
- }
+ assert.NotNil(t, ip, constants.ShouldNotNil)
}
}
func TestValidateEmails(t *testing.T) {
err1 := ValidateEmails("f", "a")
- if err1 == nil {
- t.Error("should err not nil")
- }
+ assert.Error(t, err1, constants.ShouldErr)
- err2 := ValidateEmails("validemail0987@gmail.com")
- if err2 != nil {
- t.Error("should err not nil")
- }
+ err2 := ValidateEmails("validemail098@gmail.com")
+ assert.NoError(t, err2, "should not error")
+
+ err3 := ValidateEmails("validemail0911@gmail.com", "invalidemail0987@.gmail.com")
+ assert.Error(t, err3, constants.ShouldErr)
- err3 := ValidateEmails("validemail0987@gmail.com", "invalidemail0987@.gmail.com")
- if err3 == nil {
- t.Error("should err not nil")
+ err4 := ValidateEmails("validemail0987@gmail.com", "valid_email0987@gmail.com", "invalidemail0987@gmail.com.")
+ assert.Error(t, err4, constants.ShouldErr)
+}
+
+func TestNewFiberCtx(t *testing.T) {
+ c := NewFiberCtx()
+ assert.NotNil(t, c, constants.ShouldNotNil)
+}
+
+func TestToTitle(t *testing.T) {
+ payloads := []struct {
+ str string
+ isEqual bool
+ }{
+ {"", true},
+ {"Bca", true},
+ {"A B C", true},
+ {"Your Name", true},
+ {"ABC", false},
+ {"aa", false},
+ {" bb", false},
+ {" ccc", false},
+ {"a ab cc", false},
+ {"your -Name", false},
+ {"-m'rning", false},
}
- err4 := ValidateEmails("validemail0987@gmail.com", "validemail0987@gmail.com", "invalidemail0987@gmail.com.")
- if err4 == nil {
- t.Error("should err not nil")
+ for _, payload := range payloads {
+ res := ToTitle(payload.str)
+
+ if res != payload.str && payload.isEqual {
+ assert.Failf(t, "should equal, but not at: %s got %s", payload.str, res)
+ } else if res == payload.str && !payload.isEqual {
+ assert.Failf(t, "should not equal, but equal at: %s got %s", payload.str, res)
+ }
}
}
diff --git a/internal/middleware/middleware.go b/internal/middleware/middleware.go
index 88bb4f9..323d52a 100644
--- a/internal/middleware/middleware.go
+++ b/internal/middleware/middleware.go
@@ -17,12 +17,17 @@ import (
"github.com/Lukmanern/gost/internal/response"
)
+// JWTHandler struct handles some key and redis connection
+// The purpose of this is to handler and checking HTTP Header
+// and/or checking is JWT blacklisted or not. See IsBlacklisted func.
type JWTHandler struct {
publicKey *rsa.PublicKey
privateKey *rsa.PrivateKey
cache *redis.Client
}
+// Claims struct will be generated as token,contains
+// user data like ID, email, role and permissions.
type Claims struct {
ID int `json:"id"`
Email string `json:"email"`
@@ -31,6 +36,7 @@ type Claims struct {
jwt.RegisteredClaims
}
+// NewJWTHandler func creates new JwtHandler struct
func NewJWTHandler() *JWTHandler {
env.ReadConfig("./.env")
config := env.Configuration()
@@ -46,7 +52,7 @@ func NewJWTHandler() *JWTHandler {
if privateKeyErr != nil {
log.Fatalln("jwt private key parser failed: please check in log file at ./log/log-files")
}
- newJWTHandler.cache = connector.LoadRedisDatabase()
+ newJWTHandler.cache = connector.LoadRedisCache()
if newJWTHandler.privateKey == nil {
log.Fatalln("jwt private keys are missed (nil)")
@@ -60,7 +66,7 @@ func NewJWTHandler() *JWTHandler {
return &newJWTHandler
}
-// This func used for login.
+// GenerateJWT func generate new token with expire time for user
func (j *JWTHandler) GenerateJWT(id int, email, role string, permissions map[int]int, expired time.Time) (t string, err error) {
if email == "" || role == "" || len(permissions) < 1 {
return "", errors.New("email/ role/ permission too short or void")
@@ -86,6 +92,9 @@ func (j *JWTHandler) GenerateJWT(id int, email, role string, permissions map[int
return t, nil
}
+// InvalidateToken func stores (blacklistings) token to redis.
+// After storing token in redis, the token is already blacklisted.
+// This func is used in Logout feature.
func (j JWTHandler) InvalidateToken(c *fiber.Ctx) error {
cookie := extractToken(c)
claims := Claims{}
@@ -107,12 +116,15 @@ func (j JWTHandler) InvalidateToken(c *fiber.Ctx) error {
return nil
}
+// IsBlacklisted func check the token/cookie is blacklisted or not.
func (j JWTHandler) IsBlacklisted(cookie string) bool {
status := j.cache.Get(cookie)
val, _ := status.Result()
return val != ""
}
+// IsAuthenticated func extracts token from context (fiber Ctx),
+// check is blacklisted or not. And checks the expire time too.
func (j JWTHandler) IsAuthenticated(c *fiber.Ctx) error {
cookie := extractToken(c)
if j.IsBlacklisted(cookie) {
@@ -134,23 +146,9 @@ func (j JWTHandler) IsAuthenticated(c *fiber.Ctx) error {
return c.Next()
}
-func (j JWTHandler) IsTokenValid(cookie string) bool {
- claims := Claims{}
- token, err := jwt.ParseWithClaims(cookie, &claims, func(jwtToken *jwt.Token) (interface{}, error) {
- if _, ok := jwtToken.Method.(*jwt.SigningMethodRSA); !ok {
- return nil, fiber.NewError(fiber.StatusUnauthorized)
- }
-
- return j.publicKey, nil
- })
- if err != nil || !token.Valid {
- return false
- }
- return true
-}
-
+// extractToken func extracts token from fiber Ctx.
func extractToken(c *fiber.Ctx) string {
- bearerToken := c.Get("Authorization")
+ bearerToken := c.Get(fiber.HeaderAuthorization)
// Normally Authorization HTTP header.
onlyToken := strings.Split(bearerToken, " ")
if len(onlyToken) == 2 {
@@ -160,6 +158,7 @@ func extractToken(c *fiber.Ctx) string {
return ""
}
+// GenerateClaims func generates claims struct from jwt.
func (j JWTHandler) GenerateClaims(cookieToken string) *Claims {
if j.IsBlacklisted(cookieToken) {
return nil
@@ -179,6 +178,14 @@ func (j JWTHandler) GenerateClaims(cookieToken string) *Claims {
return &claims
}
+// BuildBitGroups func builds bit-group that can contains
+// so much permissions data inside with fast and effective
+// with bit manipulations. See the example :
+// permissions = {9:10,10:256}
+// => read as : bit-group-9th, contains 2 permissions
+// => read as : bit-group-10th, contains 8 permissions
+// per group contain max 8 permissions sequentially,
+// for more You can read in paper (for link, see in readme-md)
func BuildBitGroups(permIDs ...int) map[int]int {
groups := make(map[int]int)
for _, id := range permIDs {
@@ -189,8 +196,10 @@ func BuildBitGroups(permIDs ...int) map[int]int {
return groups
}
-func CheckHasPermission(endpointPermID int, userPermissions map[int]int) bool {
- endpointBits := BuildBitGroups(endpointPermID)
+// CheckHasPermission func checks if bitGroups (map[int]int)
+// contains require permission ID or not
+func CheckHasPermission(requirePermID int, userPermissions map[int]int) bool {
+ endpointBits := BuildBitGroups(requirePermID)
// it seems O(n), but it's actually O(1)
// because length of $endpointBits is 1
for key, requiredBits := range endpointBits {
@@ -202,7 +211,7 @@ func CheckHasPermission(endpointPermID int, userPermissions map[int]int) bool {
return true
}
-// type PermissionMap = map[uint8]uint8
+// HasPermission func extracts and checks for claims from fiber Ctx
func (j JWTHandler) HasPermission(c *fiber.Ctx, endpointPermID int) error {
claims, ok := c.Locals("claims").(*Claims)
if !ok {
@@ -221,6 +230,7 @@ func (j JWTHandler) HasPermission(c *fiber.Ctx, endpointPermID int) error {
return c.Next()
}
+// HasRole func check claims-role equal or not with require role
func (j JWTHandler) HasRole(c *fiber.Ctx, role string) error {
claims, ok := c.Locals("claims").(*Claims)
if !ok || role != claims.Role {
@@ -229,14 +239,16 @@ func (j JWTHandler) HasRole(c *fiber.Ctx, role string) error {
return c.Next()
}
-// for handler or middleware
+// CheckHasPermission func is handler/middleware that
+// called before the controller for checks the fiber ctx
func (j JWTHandler) CheckHasPermission(endpointPermID int) func(c *fiber.Ctx) error {
return func(c *fiber.Ctx) error {
return j.HasPermission(c, endpointPermID)
}
}
-// for handler or middleware
+// CheckHasRole func is handler/middleware that
+// called before the controller for checks the fiber ctx
func (j JWTHandler) CheckHasRole(role string) func(c *fiber.Ctx) error {
return func(c *fiber.Ctx) error {
return j.HasRole(c, role)
diff --git a/internal/middleware/middleware_test.go b/internal/middleware/middleware_test.go
index 6e8a05e..280764d 100644
--- a/internal/middleware/middleware_test.go
+++ b/internal/middleware/middleware_test.go
@@ -7,10 +7,10 @@ import (
"testing"
"time"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/env"
"github.com/Lukmanern/gost/internal/helper"
"github.com/gofiber/fiber/v2"
- "github.com/stretchr/testify/assert"
"github.com/valyala/fasthttp"
)
@@ -94,7 +94,7 @@ func TestGenerateClaims(t *testing.T) {
}
}
-func TestJWTHandler_InvalidateToken(t *testing.T) {
+func TestJWTHandlerInvalidateToken(t *testing.T) {
jwtHandler := NewJWTHandler()
token, err := jwtHandler.GenerateJWT(params.ID, params.Email, params.Role, params.Per, params.Exp)
if err != nil {
@@ -111,14 +111,14 @@ func TestJWTHandler_InvalidateToken(t *testing.T) {
t.Error("Should error: Expected error for no token")
}
- c.Request().Header.Add("Authorization", "Bearer "+token)
+ c.Request().Header.Add(fiber.HeaderAuthorization, "Bearer "+token)
invalidErr2 := jwtHandler.InvalidateToken(c)
if invalidErr2 != nil {
t.Error("Expected no error for a valid token, but got an error.")
}
}
-func TestJWTHandler_IsBlacklisted(t *testing.T) {
+func TestJWTHandlerIsBlacklisted(t *testing.T) {
jwtHandler := NewJWTHandler()
cookie, err := jwtHandler.GenerateJWT(1000,
helper.RandomEmail(), "example-role",
@@ -152,7 +152,7 @@ func TestJWTHandler_IsBlacklisted(t *testing.T) {
}
}
-func TestJWTHandler_IsAuthenticated(t *testing.T) {
+func TestJWTHandlerIsAuthenticated(t *testing.T) {
jwtHandler := NewJWTHandler()
token, err := jwtHandler.GenerateJWT(params.ID, params.Email, params.Role, params.Per, params.Exp)
if err != nil {
@@ -183,7 +183,7 @@ func TestJWTHandler_IsAuthenticated(t *testing.T) {
jwtHandler3 := NewJWTHandler()
app := fiber.New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
- c.Request().Header.Add("Authorization", " "+token)
+ c.Request().Header.Add(fiber.HeaderAuthorization, " "+token)
c.Status(fiber.StatusUnauthorized)
jwtHandler3.IsAuthenticated(c)
if c.Context().Response.StatusCode() != fiber.StatusUnauthorized {
@@ -192,27 +192,7 @@ func TestJWTHandler_IsAuthenticated(t *testing.T) {
}()
}
-func TestJWTHandler_IsTokenValid(t *testing.T) {
- jwtHandler := NewJWTHandler()
- token, err := jwtHandler.GenerateJWT(params.ID, params.Email, params.Role, params.Per, params.Exp)
- if err != nil {
- t.Error("error while generating token")
- }
- if token == "" {
- t.Error("error : token void")
- }
-
- isValid := jwtHandler.IsTokenValid(token)
- assert.True(t, isValid, "Valid token should be considered valid")
-
- isValid = jwtHandler.IsTokenValid("expiredToken")
- assert.False(t, isValid, "Expired token should be considered invalid")
-
- isValid = jwtHandler.IsTokenValid("invalidToken")
- assert.False(t, isValid, "Invalid token should be considered invalid")
-}
-
-func TestJWTHandler_HasPermission(t *testing.T) {
+func TestJWTHandlerHasPermission(t *testing.T) {
jwtHandler := NewJWTHandler()
token, err := jwtHandler.GenerateJWT(params.ID, params.Email, params.Role, params.Per, params.Exp)
if err != nil {
@@ -224,14 +204,14 @@ func TestJWTHandler_HasPermission(t *testing.T) {
app := fiber.New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
- c.Request().Header.Add("Authorization", "Bearer "+token)
+ c.Request().Header.Add(fiber.HeaderAuthorization, "Bearer "+token)
jwtHandler.HasPermission(c, 25)
if c.Response().Header.StatusCode() != fiber.StatusUnauthorized {
t.Error("Should authorized")
}
}
-func TestJWTHandler_HasRole(t *testing.T) {
+func TestJWTHandlerHasRole(t *testing.T) {
jwtHandler := NewJWTHandler()
token, err := jwtHandler.GenerateJWT(params.ID, params.Email, params.Role, params.Per, params.Exp)
if err != nil {
@@ -243,14 +223,14 @@ func TestJWTHandler_HasRole(t *testing.T) {
app := fiber.New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
- c.Request().Header.Add("Authorization", "Bearer "+token)
+ c.Request().Header.Add(fiber.HeaderAuthorization, "Bearer "+token)
jwtHandler.HasRole(c, "test-role")
if c.Response().Header.StatusCode() != fiber.StatusUnauthorized {
- t.Error("Should unauthorized")
+ t.Error(constants.Unauthorized)
}
}
-func TestJWTHandler_CheckHasPermission(t *testing.T) {
+func TestJWTHandlerCheckHasPermission(t *testing.T) {
jwtHandler := NewJWTHandler()
token, err := jwtHandler.GenerateJWT(params.ID, params.Email, params.Role, params.Per, params.Exp)
if err != nil {
@@ -262,11 +242,11 @@ func TestJWTHandler_CheckHasPermission(t *testing.T) {
err2 := jwtHandler.CheckHasPermission(9999)
if err2 == nil {
- t.Error("Should unauthorized")
+ t.Error(constants.Unauthorized)
}
}
-func TestJWTHandler_CheckHasRole(t *testing.T) {
+func TestJWTHandlerCheckHasRole(t *testing.T) {
jwtHandler := NewJWTHandler()
token, err := jwtHandler.GenerateJWT(params.ID, params.Email, params.Role, params.Per, params.Exp)
if err != nil {
@@ -278,11 +258,11 @@ func TestJWTHandler_CheckHasRole(t *testing.T) {
err2 := jwtHandler.CheckHasRole("permission-1")
if err2 == nil {
- t.Error("Should unauthorized")
+ t.Error(constants.Unauthorized)
}
}
-func Test_PermissionBitGroup(t *testing.T) {
+func TestPermissionBitGroup(t *testing.T) {
d := 8
testCases := []struct {
input int
@@ -335,7 +315,7 @@ func Test_PermissionBitGroup(t *testing.T) {
}
}
-func Test_CheckHasPermission(t *testing.T) {
+func TestCheckHasPermission(t *testing.T) {
// user perms
permIDs := make([]int, 0)
for i := 1; i <= 19; i++ {
diff --git a/internal/rbac/permission.go b/internal/rbac/permission.go
index 06da6ce..0e55c0e 100644
--- a/internal/rbac/permission.go
+++ b/internal/rbac/permission.go
@@ -6,8 +6,10 @@ import (
"github.com/Lukmanern/gost/domain/entity"
)
-// Don't forget to run Test_AllPermissions
-// to audit
+// AllPermissions func return all permissions entities
+// that has been created by developer. This func run self
+// audit that check for name and id should be unique value.
+// ⚠️ Do not forget to run TestAllPermissions to audit
func AllPermissions() []entity.Permission {
permissions := []entity.Permission{
// user
@@ -32,8 +34,8 @@ func AllPermissions() []entity.Permission {
if perm.ID < 1 || len(perm.Name) <= 1 {
log.Fatal("permission name too short or invalid id at:", perm)
}
- checkIDs[perm.ID] += 1
- checkNames[perm.Name] += 1
+ checkIDs[perm.ID]++
+ checkNames[perm.Name]++
if checkIDs[perm.ID] > 1 || checkNames[perm.Name] > 1 {
log.Fatal("permission name or id should unique, but got:", perm)
}
@@ -72,5 +74,5 @@ var (
// add more permissions
// Rule :
// Name should unique
- // ID +1 from before
+ // ID +1 from the ID before
)
diff --git a/internal/rbac/rbac_test.go b/internal/rbac/rbac_test.go
index 9085585..1de87d2 100644
--- a/internal/rbac/rbac_test.go
+++ b/internal/rbac/rbac_test.go
@@ -2,63 +2,47 @@ package rbac
import (
"testing"
+
+ "github.com/stretchr/testify/assert"
)
func TestAllPermissions(t *testing.T) {
- for _, permission := range AllPermissions() {
- if permission.Name == "" {
- t.Error("permission name should not string-nil")
- }
+ defer func() {
+ r := recover()
+ assert.Nil(t, r, "should not panic, but got:", r)
+ }()
+
+ permissions := AllPermissions()
+ for _, permission := range permissions {
+ assert.NotEmpty(t, permission.Name, "permission name should not be empty")
}
}
-// This Test is to make sure
-// that permission value is unique
func TestCountPermissions(t *testing.T) {
hashMapPermissions := make(map[string]int, 0)
for _, permission := range AllPermissions() {
- hashMapPermissions[permission.Name] += 1
- if hashMapPermissions[permission.Name] > 1 {
- t.Error("should 1, not more, non-unique permission detected : ", permission.Name)
- }
+ hashMapPermissions[permission.Name]++
+ assert.LessOrEqual(t, hashMapPermissions[permission.Name], 1, "should be 1, not more; non-unique permission detected: %s", permission.Name)
}
- if len(hashMapPermissions) != len(AllPermissions()) {
- t.Error("should equal len, non-unique permission detected")
- }
+ assert.Equal(t, len(hashMapPermissions), len(AllPermissions()), "should have equal length; non-unique permission detected")
}
-func Test_AllPermissions(t *testing.T) {
- defer func() {
- r := recover()
- if r != nil {
- t.Error("should not panic, but got:", r)
- }
- }()
-
- AllPermissions()
-}
-
-func Test_AllRoles(t *testing.T) {
- for _, role := range AllRoles() {
- if role.Name == "" {
- t.Error("name should not string-nil")
- }
+func TestAllRoles(t *testing.T) {
+ roles := AllRoles()
+ for _, role := range roles {
+ assert.NotEmpty(t, role.Name, "name should not be empty")
}
}
-func Test_CountRoles(t *testing.T) {
+func TestCountRoles(t *testing.T) {
hashMapRoles := make(map[string]int, 0)
for _, role := range AllRoles() {
- hashMapRoles[role.Name] += 1
- if hashMapRoles[role.Name] > 1 {
- t.Error("should 1, not more, non-unique role (role:name) detected : ", role.Name)
- }
+ hashMapRoles[role.Name]++
+ assert.LessOrEqual(t, hashMapRoles[role.Name], 1, "should be 1, not more; non-unique role (role:name) detected: %s", role.Name)
}
- if len(hashMapRoles) != len(AllRoles()) {
- t.Error("should equal len, non-unique role detected")
- }
+ assert.Equal(t, len(hashMapRoles), len(AllRoles()), "should have equal length; non-unique role detected")
}
diff --git a/internal/rbac/role.go b/internal/rbac/role.go
index c448e3c..0953d81 100644
--- a/internal/rbac/role.go
+++ b/internal/rbac/role.go
@@ -2,11 +2,15 @@ package rbac
import "github.com/Lukmanern/gost/domain/entity"
-// for migration and seeder
+// AllRoles func return all roles entities that has been
+// created by developer. This func run self audit that
+// check for name should be unique value.
+// ⚠️ Do not forget to put new role here.
func AllRoles() []entity.Role {
roleNames := []string{
RoleAdmin,
RoleUser,
+
// ...
// add more here
}
@@ -16,7 +20,7 @@ func AllRoles() []entity.Role {
newRoleEntity := entity.Role{
Name: name,
}
- newRoleEntity.SetCreateTimes()
+ newRoleEntity.SetCreateTime()
roles = append(roles, newRoleEntity)
}
@@ -26,6 +30,7 @@ func AllRoles() []entity.Role {
const (
RoleAdmin = "admin"
RoleUser = "user"
+
// ...
// add more here
)
diff --git a/internal/response/response.go b/internal/response/response.go
index bc70ada..eb7447d 100644
--- a/internal/response/response.go
+++ b/internal/response/response.go
@@ -6,6 +6,9 @@ import (
"github.com/gofiber/fiber/v2"
)
+// Response struct is standart JSON structure that this project used.
+// You can change if you want. This also prevents developers make
+// common mistake to make messsage to frontend developer.
type Response struct {
Message string `json:"message"`
Success bool `json:"success"`
diff --git a/main_test.go b/main_test.go
deleted file mode 100644
index 01eb0a8..0000000
--- a/main_test.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package main
-
-import (
- "testing"
- "time"
-)
-
-func Test_1(t *testing.T) {
- defer func() {
- r := recover()
- if r != nil {
- t.Error("should not panic", r)
- }
- }()
-
- go main()
- time.Sleep(3 * time.Second)
-}
diff --git a/repository/rbac/permission_repository.go b/repository/permission/permission_repository.go
similarity index 69%
rename from repository/rbac/permission_repository.go
rename to repository/permission/permission_repository.go
index aaa79a0..fb1fc94 100644
--- a/repository/rbac/permission_repository.go
+++ b/repository/permission/permission_repository.go
@@ -11,21 +11,30 @@ import (
)
type PermissionRepository interface {
+ // Create adds a new permission to the repository.
Create(ctx context.Context, permission entity.Permission) (id int, err error)
+
+ // GetByID retrieves a permission by its unique identifier.
GetByID(ctx context.Context, id int) (permission *entity.Permission, err error)
+
+ // GetByName retrieves a permission by its name.
GetByName(ctx context.Context, name string) (permission *entity.Permission, err error)
+
+ // GetAll retrieves all permissions based on a filter for pagination.
GetAll(ctx context.Context, filter base.RequestGetAll) (permissions []entity.Permission, total int, err error)
+
+ // Update modifies permission information in the repository.
Update(ctx context.Context, permission entity.Permission) (err error)
+
+ // Delete removes a permission from the repository by its ID.
Delete(ctx context.Context, id int) (err error)
}
type PermissionRepositoryImpl struct {
- permissionTableName string
- db *gorm.DB
+ db *gorm.DB
}
var (
- permissionTableName string = "permissions"
permissionRepositoryImpl *PermissionRepositoryImpl
permissionRepositoryImplOnce sync.Once
)
@@ -33,14 +42,13 @@ var (
func NewPermissionRepository() PermissionRepository {
permissionRepositoryImplOnce.Do(func() {
permissionRepositoryImpl = &PermissionRepositoryImpl{
- permissionTableName: permissionTableName,
- db: connector.LoadDatabase(),
+ db: connector.LoadDatabase(),
}
})
return permissionRepositoryImpl
}
-func (repo PermissionRepositoryImpl) Create(ctx context.Context, permission entity.Permission) (id int, err error) {
+func (repo *PermissionRepositoryImpl) Create(ctx context.Context, permission entity.Permission) (id int, err error) {
err = repo.db.Transaction(func(tx *gorm.DB) error {
res := tx.Create(&permission)
if res.Error != nil {
@@ -57,7 +65,7 @@ func (repo PermissionRepositoryImpl) Create(ctx context.Context, permission enti
return id, nil
}
-func (repo PermissionRepositoryImpl) GetByID(ctx context.Context, id int) (permission *entity.Permission, err error) {
+func (repo *PermissionRepositoryImpl) GetByID(ctx context.Context, id int) (permission *entity.Permission, err error) {
permission = &entity.Permission{}
result := repo.db.Where("id = ?", id).First(&permission)
if result.Error != nil {
@@ -66,7 +74,7 @@ func (repo PermissionRepositoryImpl) GetByID(ctx context.Context, id int) (permi
return permission, nil
}
-func (repo PermissionRepositoryImpl) GetByName(ctx context.Context, name string) (permission *entity.Permission, err error) {
+func (repo *PermissionRepositoryImpl) GetByName(ctx context.Context, name string) (permission *entity.Permission, err error) {
permission = &entity.Permission{}
result := repo.db.Where("name = ?", name).First(&permission)
if result.Error != nil {
@@ -75,7 +83,7 @@ func (repo PermissionRepositoryImpl) GetByName(ctx context.Context, name string)
return permission, nil
}
-func (repo PermissionRepositoryImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (permissions []entity.Permission, total int, err error) {
+func (repo *PermissionRepositoryImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (permissions []entity.Permission, total int, err error) {
var count int64
args := []interface{}{"%" + filter.Keyword + "%"}
cond := "name LIKE ?"
@@ -95,7 +103,7 @@ func (repo PermissionRepositoryImpl) GetAll(ctx context.Context, filter base.Req
return permissions, total, nil
}
-func (repo PermissionRepositoryImpl) Update(ctx context.Context, permission entity.Permission) (err error) {
+func (repo *PermissionRepositoryImpl) Update(ctx context.Context, permission entity.Permission) (err error) {
err = repo.db.Transaction(func(tx *gorm.DB) error {
var oldData entity.Permission
result := tx.Where("id = ?", permission.ID).First(&oldData)
@@ -118,7 +126,7 @@ func (repo PermissionRepositoryImpl) Update(ctx context.Context, permission enti
return err
}
-func (repo PermissionRepositoryImpl) Delete(ctx context.Context, id int) (err error) {
+func (repo *PermissionRepositoryImpl) Delete(ctx context.Context, id int) (err error) {
deleted := entity.Permission{}
result := repo.db.Where("id = ?", id).Delete(&deleted)
if result.Error != nil {
diff --git a/repository/rbac/permission_repository_test.go b/repository/permission/permission_repository_test.go
similarity index 94%
rename from repository/rbac/permission_repository_test.go
rename to repository/permission/permission_repository_test.go
index 4dbd05e..2493a8b 100644
--- a/repository/rbac/permission_repository_test.go
+++ b/repository/permission/permission_repository_test.go
@@ -24,8 +24,7 @@ func init() {
timeNow = time.Now()
ctx = context.Background()
permissionRepoImpl = PermissionRepositoryImpl{
- permissionTableName: permissionTableName,
- db: connector.LoadDatabase(),
+ db: connector.LoadDatabase(),
}
}
@@ -54,7 +53,7 @@ func TestNewPermissionRepository(t *testing.T) {
}
}
-func TestPermissionRepositoryImpl_Create(t *testing.T) {
+func TestPermissionRepositoryImplCreate(t *testing.T) {
permission := createOnePermission(t, "create-same-name")
if permission == nil {
t.Error("failed creating permission : permission is nil")
@@ -98,19 +97,19 @@ func TestPermissionRepositoryImpl_Create(t *testing.T) {
t.Errorf("create() do not panic")
}
}()
- gotId, err := tt.repo.Create(tt.args.ctx, tt.args.permission)
+ gotID, err := tt.repo.Create(tt.args.ctx, tt.args.permission)
if (err != nil) != tt.wantErr {
t.Errorf("PermissionRepositoryImpl.Create() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if gotId <= 0 {
+ if gotID <= 0 {
t.Errorf("ID should be positive")
}
})
}
}
-func TestPermissionRepositoryImpl_GetByID(t *testing.T) {
+func TestPermissionRepositoryImplGetByID(t *testing.T) {
permission := createOnePermission(t, "TestGetByID")
if permission == nil {
t.Error("failed creating permission : permission is nil")
@@ -162,7 +161,7 @@ func TestPermissionRepositoryImpl_GetByID(t *testing.T) {
}
}
-func TestPermissionRepositoryImpl_GetByName(t *testing.T) {
+func TestPermissionRepositoryImplGetByName(t *testing.T) {
permission := createOnePermission(t, "TestGetByName")
if permission == nil {
t.Error("failed creating permission : permission is nil")
@@ -215,7 +214,7 @@ func TestPermissionRepositoryImpl_GetByName(t *testing.T) {
}
}
-func TestPermissionRepositoryImpl_GetAll(t *testing.T) {
+func TestPermissionRepositoryImplGetAll(t *testing.T) {
permissions := make([]entity.Permission, 0)
for i := 0; i < 10; i++ {
permission := createOnePermission(t, "TestGetAll-"+strconv.Itoa(i))
@@ -285,7 +284,7 @@ func TestPermissionRepositoryImpl_GetAll(t *testing.T) {
}
}
-func TestPermissionRepositoryImpl_Update(t *testing.T) {
+func TestPermissionRepositoryImplUpdate(t *testing.T) {
permission := createOnePermission(t, "TestUpdateByID")
if permission == nil {
t.Error("failed creating permission : permission is nil")
@@ -349,7 +348,7 @@ func TestPermissionRepositoryImpl_Update(t *testing.T) {
}
}
-func TestPermissionRepositoryImpl_Delete(t *testing.T) {
+func TestPermissionRepositoryImplDelete(t *testing.T) {
permission := createOnePermission(t, "TestDeleteByID")
if permission == nil {
t.Error("failed creating permission : permission is nil")
diff --git a/repository/rbac/role_repository.go b/repository/role/role_repository.go
similarity index 71%
rename from repository/rbac/role_repository.go
rename to repository/role/role_repository.go
index 0d9d633..c1f4d96 100644
--- a/repository/rbac/role_repository.go
+++ b/repository/role/role_repository.go
@@ -11,22 +11,33 @@ import (
)
type RoleRepository interface {
+ // Create adds a new role to the repository with specified permissions.
Create(ctx context.Context, role entity.Role, permissionsID []int) (id int, err error)
+
+ // ConnectToPermission associates a role with specified permissions.
ConnectToPermission(ctx context.Context, roleID int, permissionsID []int) (err error)
+
+ // GetByID retrieves a role by its unique identifier.
GetByID(ctx context.Context, id int) (role *entity.Role, err error)
+
+ // GetByName retrieves a role by its name.
GetByName(ctx context.Context, name string) (role *entity.Role, err error)
+
+ // GetAll retrieves all roles based on a filter for pagination.
GetAll(ctx context.Context, filter base.RequestGetAll) (roles []entity.Role, total int, err error)
+
+ // Update modifies role information in the repository.
Update(ctx context.Context, role entity.Role) (err error)
+
+ // Delete removes a role from the repository by its ID.
Delete(ctx context.Context, id int) (err error)
}
type RoleRepositoryImpl struct {
- roleTableName string
- db *gorm.DB
+ db *gorm.DB
}
var (
- roleTableName string = "roles"
roleRepositoryImpl *RoleRepositoryImpl
roleRepositoryImplOnce sync.Once
)
@@ -34,14 +45,13 @@ var (
func NewRoleRepository() RoleRepository {
roleRepositoryImplOnce.Do(func() {
roleRepositoryImpl = &RoleRepositoryImpl{
- roleTableName: roleTableName,
- db: connector.LoadDatabase(),
+ db: connector.LoadDatabase(),
}
})
return roleRepositoryImpl
}
-func (repo RoleRepositoryImpl) Create(ctx context.Context, role entity.Role, permissionsID []int) (id int, err error) {
+func (repo *RoleRepositoryImpl) Create(ctx context.Context, role entity.Role, permissionsID []int) (id int, err error) {
err = repo.db.Transaction(func(tx *gorm.DB) error {
res := tx.Create(&role)
if res.Error != nil {
@@ -69,7 +79,7 @@ func (repo RoleRepositoryImpl) Create(ctx context.Context, role entity.Role, per
return id, nil
}
-func (repo RoleRepositoryImpl) ConnectToPermission(ctx context.Context, roleID int, permissionsID []int) (err error) {
+func (repo *RoleRepositoryImpl) ConnectToPermission(ctx context.Context, roleID int, permissionsID []int) (err error) {
err = repo.db.Transaction(func(tx *gorm.DB) error {
deleted := entity.RoleHasPermission{}
result := tx.Where("role_id = ?", roleID).Delete(&deleted)
@@ -96,7 +106,7 @@ func (repo RoleRepositoryImpl) ConnectToPermission(ctx context.Context, roleID i
return nil
}
-func (repo RoleRepositoryImpl) GetByID(ctx context.Context, id int) (role *entity.Role, err error) {
+func (repo *RoleRepositoryImpl) GetByID(ctx context.Context, id int) (role *entity.Role, err error) {
role = &entity.Role{}
result := repo.db.Where("id = ?", id).Preload("Permissions").First(&role)
if result.Error != nil {
@@ -105,7 +115,7 @@ func (repo RoleRepositoryImpl) GetByID(ctx context.Context, id int) (role *entit
return role, nil
}
-func (repo RoleRepositoryImpl) GetByName(ctx context.Context, name string) (role *entity.Role, err error) {
+func (repo *RoleRepositoryImpl) GetByName(ctx context.Context, name string) (role *entity.Role, err error) {
role = &entity.Role{}
result := repo.db.Where("name = ?", name).Preload("Permissions").First(&role)
if result.Error != nil {
@@ -114,7 +124,7 @@ func (repo RoleRepositoryImpl) GetByName(ctx context.Context, name string) (role
return role, nil
}
-func (repo RoleRepositoryImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (roles []entity.Role, total int, err error) {
+func (repo *RoleRepositoryImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (roles []entity.Role, total int, err error) {
var count int64
args := []interface{}{"%" + filter.Keyword + "%"}
cond := "name LIKE ?"
@@ -134,7 +144,7 @@ func (repo RoleRepositoryImpl) GetAll(ctx context.Context, filter base.RequestGe
return roles, total, nil
}
-func (repo RoleRepositoryImpl) Update(ctx context.Context, role entity.Role) (err error) {
+func (repo *RoleRepositoryImpl) Update(ctx context.Context, role entity.Role) (err error) {
err = repo.db.Transaction(func(tx *gorm.DB) error {
var oldData entity.Role
result := tx.Where("id = ?", role.ID).First(&oldData)
@@ -157,7 +167,7 @@ func (repo RoleRepositoryImpl) Update(ctx context.Context, role entity.Role) (er
return err
}
-func (repo RoleRepositoryImpl) Delete(ctx context.Context, id int) (err error) {
+func (repo *RoleRepositoryImpl) Delete(ctx context.Context, id int) (err error) {
deleted := entity.Role{}
result := repo.db.Where("id = ?", id).Delete(&deleted)
if result.Error != nil {
diff --git a/repository/rbac/role_repository_test.go b/repository/role/role_repository_test.go
similarity index 93%
rename from repository/rbac/role_repository_test.go
rename to repository/role/role_repository_test.go
index dde5834..8835806 100644
--- a/repository/rbac/role_repository_test.go
+++ b/repository/role/role_repository_test.go
@@ -16,8 +16,8 @@ import (
var (
roleRepoImpl RoleRepositoryImpl
permissionsID []int
- // timeNow and ctx is declared
- // at permission_repository_test file
+ timeNow time.Time
+ ctx context.Context
)
func init() {
@@ -28,8 +28,7 @@ func init() {
ctx = context.Background()
roleRepoImpl = RoleRepositoryImpl{
- roleTableName: roleTableName,
- db: connector.LoadDatabase(),
+ db: connector.LoadDatabase(),
}
permissionsID = []int{1, 2, 3, 4, 5}
@@ -59,7 +58,7 @@ func TestNewRoleRepository(t *testing.T) {
}
}
-func TestRoleRepositoryImpl_Create(t *testing.T) {
+func TestCreate(t *testing.T) {
role := createOneRole(t, "create-same-name")
if role == nil {
t.Error("failed creating role : role is nil")
@@ -103,19 +102,19 @@ func TestRoleRepositoryImpl_Create(t *testing.T) {
t.Errorf("create() do not panic")
}
}()
- gotId, err := tt.repo.Create(tt.args.ctx, tt.args.role, tt.args.permissionsID)
+ gotID, err := tt.repo.Create(tt.args.ctx, tt.args.role, tt.args.permissionsID)
if (err != nil) != tt.wantErr {
t.Errorf("RoleRepositoryImpl.Create() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if gotId <= 0 {
+ if gotID <= 0 {
t.Errorf("ID should be positive")
}
})
}
}
-func TestRoleRepositoryImpl_ConnectToPermission(t *testing.T) {
+func TestConnectToPermission(t *testing.T) {
role := createOneRole(t, "TestRoleConnectToPermission")
if role == nil || role.ID == 0 {
t.Error("failed creating role : role is nil")
@@ -172,7 +171,7 @@ func TestRoleRepositoryImpl_ConnectToPermission(t *testing.T) {
}
}
-func TestRoleRepositoryImpl_GetByID(t *testing.T) {
+func TestGetByID(t *testing.T) {
role := createOneRole(t, "TestGetByID")
if role == nil {
t.Error("failed creating role : role is nil")
@@ -224,7 +223,7 @@ func TestRoleRepositoryImpl_GetByID(t *testing.T) {
}
}
-func TestRoleRepositoryImpl_GetByName(t *testing.T) {
+func TestGetByName(t *testing.T) {
role := createOneRole(t, "TestGetByName")
if role == nil {
t.Error("failed creating role : role is nil")
@@ -276,7 +275,7 @@ func TestRoleRepositoryImpl_GetByName(t *testing.T) {
}
}
-func TestRoleRepositoryImpl_GetAll(t *testing.T) {
+func TestGetAll(t *testing.T) {
roles := make([]entity.Role, 0)
for i := 0; i < 10; i++ {
role := createOneRole(t, "TestGetAll"+strconv.Itoa(i))
@@ -345,7 +344,7 @@ func TestRoleRepositoryImpl_GetAll(t *testing.T) {
}
}
-func TestRoleRepositoryImpl_Update(t *testing.T) {
+func TestUpdate(t *testing.T) {
role := createOneRole(t, "TestUpdateByID")
if role == nil {
t.Error("failed creating role : role is nil")
@@ -408,7 +407,7 @@ func TestRoleRepositoryImpl_Update(t *testing.T) {
}
}
-func TestRoleRepositoryImpl_Delete(t *testing.T) {
+func TestDelete(t *testing.T) {
role := createOneRole(t, "TestDeleteByID")
if role == nil {
t.Error("failed creating role : role is nil")
diff --git a/repository/user/user_repository.go b/repository/user/user_repository.go
index b45ab06..51e36f8 100644
--- a/repository/user/user_repository.go
+++ b/repository/user/user_repository.go
@@ -14,23 +14,36 @@ import (
)
type UserRepository interface {
+ // Create adds a new user to the repository with a specified role.
Create(ctx context.Context, user entity.User, roleID int) (id int, err error)
+
+ // GetByID retrieves a user by their unique identifier.
GetByID(ctx context.Context, id int) (user *entity.User, err error)
+
+ // GetByEmail retrieves a user by their email address.
GetByEmail(ctx context.Context, email string) (user *entity.User, err error)
+
+ // GetByConditions retrieves a user based on specified conditions.
GetByConditions(ctx context.Context, conds map[string]any) (user *entity.User, err error)
+
+ // GetAll retrieves all users based on a filter for pagination.
GetAll(ctx context.Context, filter base.RequestGetAll) (users []entity.User, total int, err error)
+
+ // Update modifies user information in the repository.
Update(ctx context.Context, user entity.User) (err error)
+
+ // Delete removes a user from the repository by their ID.
Delete(ctx context.Context, id int) (err error)
+
+ // UpdatePassword updates a user's password in the repository.
UpdatePassword(ctx context.Context, id int, passwordHashed string) (err error)
}
type UserRepositoryImpl struct {
- userTableName string
- db *gorm.DB
+ db *gorm.DB
}
var (
- userTableName string = "users"
userRepositoryImpl *UserRepositoryImpl
userRepositoryImplOnce sync.Once
)
@@ -38,14 +51,13 @@ var (
func NewUserRepository() UserRepository {
userRepositoryImplOnce.Do(func() {
userRepositoryImpl = &UserRepositoryImpl{
- userTableName: userTableName,
- db: connector.LoadDatabase(),
+ db: connector.LoadDatabase(),
}
})
return userRepositoryImpl
}
-func (repo UserRepositoryImpl) Create(ctx context.Context, user entity.User, roleID int) (id int, err error) {
+func (repo *UserRepositoryImpl) Create(ctx context.Context, user entity.User, roleID int) (id int, err error) {
err = repo.db.Transaction(func(tx *gorm.DB) error {
if res := tx.Create(&user); res.Error != nil {
tx.Rollback()
@@ -68,7 +80,7 @@ func (repo UserRepositoryImpl) Create(ctx context.Context, user entity.User, rol
return id, nil
}
-func (repo UserRepositoryImpl) GetByID(ctx context.Context, id int) (user *entity.User, err error) {
+func (repo *UserRepositoryImpl) GetByID(ctx context.Context, id int) (user *entity.User, err error) {
user = &entity.User{}
result := repo.db.Where("id = ?", id).Preload("Roles.Permissions").First(&user)
if result.Error != nil {
@@ -77,7 +89,7 @@ func (repo UserRepositoryImpl) GetByID(ctx context.Context, id int) (user *entit
return user, nil
}
-func (repo UserRepositoryImpl) GetByEmail(ctx context.Context, email string) (user *entity.User, err error) {
+func (repo *UserRepositoryImpl) GetByEmail(ctx context.Context, email string) (user *entity.User, err error) {
user = &entity.User{}
result := repo.db.Where("email = ?", email).Preload("Roles.Permissions").First(&user)
if result.Error != nil {
@@ -86,7 +98,7 @@ func (repo UserRepositoryImpl) GetByEmail(ctx context.Context, email string) (us
return user, nil
}
-func (repo UserRepositoryImpl) GetByConditions(ctx context.Context, conds map[string]any) (user *entity.User, err error) {
+func (repo *UserRepositoryImpl) GetByConditions(ctx context.Context, conds map[string]any) (user *entity.User, err error) {
// this func is easy-contain-vunarable by default
user = &entity.User{}
query := repo.db
@@ -100,7 +112,7 @@ func (repo UserRepositoryImpl) GetByConditions(ctx context.Context, conds map[st
return user, nil
}
-func (repo UserRepositoryImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (users []entity.User, total int, err error) {
+func (repo *UserRepositoryImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (users []entity.User, total int, err error) {
var count int64
args := []interface{}{"%" + filter.Keyword + "%"}
cond := "name LIKE ?"
@@ -120,7 +132,7 @@ func (repo UserRepositoryImpl) GetAll(ctx context.Context, filter base.RequestGe
return users, total, nil
}
-func (repo UserRepositoryImpl) Update(ctx context.Context, user entity.User) (err error) {
+func (repo *UserRepositoryImpl) Update(ctx context.Context, user entity.User) (err error) {
err = repo.db.Transaction(func(tx *gorm.DB) error {
var oldData entity.User
result := tx.Where("id = ?", user.ID).First(&oldData)
@@ -142,7 +154,7 @@ func (repo UserRepositoryImpl) Update(ctx context.Context, user entity.User) (er
return err
}
-func (repo UserRepositoryImpl) Delete(ctx context.Context, id int) (err error) {
+func (repo *UserRepositoryImpl) Delete(ctx context.Context, id int) (err error) {
deleted := entity.User{}
result := repo.db.Where("id = ?", id).Delete(&deleted)
if result.Error != nil {
@@ -151,7 +163,7 @@ func (repo UserRepositoryImpl) Delete(ctx context.Context, id int) (err error) {
return nil
}
-func (repo UserRepositoryImpl) UpdatePassword(ctx context.Context, id int, passwordHashed string) (err error) {
+func (repo *UserRepositoryImpl) UpdatePassword(ctx context.Context, id int, passwordHashed string) (err error) {
err = repo.db.Transaction(func(tx *gorm.DB) error {
var user entity.User
result := tx.Where("id = ?", id).First(&user)
diff --git a/repository/user/user_repository_test.go b/repository/user/user_repository_test.go
index af7ba74..1a6dada 100644
--- a/repository/user/user_repository_test.go
+++ b/repository/user/user_repository_test.go
@@ -30,7 +30,7 @@ func TestNewUserRepository(t *testing.T) {
}
}
-func TestUserRepositoryImpl_Create(t *testing.T) {
+func TestUserRepositoryImplCreate(t *testing.T) {
userRepositoryImpl := UserRepositoryImpl{
db: connector.LoadDatabase(),
}
@@ -82,17 +82,17 @@ func TestUserRepositoryImpl_Create(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if !tt.wantPanic {
- gotId, err := tt.repo.Create(tt.args.ctx, tt.args.user, 1)
+ gotID, err := tt.repo.Create(tt.args.ctx, tt.args.user, 1)
if (err != nil) != tt.wantErr {
t.Errorf("UserRepositoryImpl.Create() error = %v, wantErr %v", err, tt.wantErr)
return
- } else {
- gotId2, err2 := tt.repo.Create(tt.args.ctx, tt.args.user, 1)
- if err2 == nil || gotId2 != 0 {
- t.Error("should be error, couse email is already used")
- }
- tt.repo.Delete(tt.args.ctx, gotId)
}
+ gotID2, err2 := tt.repo.Create(tt.args.ctx, tt.args.user, 1)
+ if err2 == nil || gotID2 != 0 {
+ t.Error("should be error, couse email is already used")
+ }
+ tt.repo.Delete(tt.args.ctx, gotID)
+
return
}
// want panic
@@ -111,7 +111,7 @@ func TestUserRepositoryImpl_Create(t *testing.T) {
}
}
-func TestUserRepositoryImpl_GetByID(t *testing.T) {
+func TestUserRepositoryImplGetByID(t *testing.T) {
// create user
user := entity.User{
Name: "validname",
@@ -181,7 +181,7 @@ func TestUserRepositoryImpl_GetByID(t *testing.T) {
}
}
-func TestUserRepositoryImpl_GetByEmail(t *testing.T) {
+func TestUserRepositoryImplGetByEmail(t *testing.T) {
// create user
user := entity.User{
Name: "validname",
@@ -251,7 +251,7 @@ func TestUserRepositoryImpl_GetByEmail(t *testing.T) {
}
}
-func TestUserRepositoryImpl_GetAll(t *testing.T) {
+func TestUserRepositoryImplGetAll(t *testing.T) {
// create user
allUsersID := make([]int, 0)
userRepositoryImpl := UserRepositoryImpl{
@@ -336,7 +336,7 @@ func TestUserRepositoryImpl_GetAll(t *testing.T) {
}
}
-func TestUserRepositoryImpl_Update(t *testing.T) {
+func TestUserRepositoryImplUpdate(t *testing.T) {
// create user
user := entity.User{
Name: "validname",
@@ -399,7 +399,7 @@ func TestUserRepositoryImpl_Update(t *testing.T) {
}
}
-func TestUserRepositoryImpl_Delete(t *testing.T) {
+func TestUserRepositoryImplDelete(t *testing.T) {
userRepository := NewUserRepository()
if userRepository == nil {
t.Error("shouldn't nil")
@@ -415,7 +415,7 @@ func TestUserRepositoryImpl_Delete(t *testing.T) {
}
}
-func TestUserRepositoryImpl_UpdatePassword(t *testing.T) {
+func TestUserRepositoryImplUpdatePassword(t *testing.T) {
// create user
user := entity.User{
Name: "validname",
@@ -489,7 +489,7 @@ func TestUserRepositoryImpl_UpdatePassword(t *testing.T) {
}
}
-func TestUserRepositoryImpl_GetByConditions(t *testing.T) {
+func TestUserRepositoryImplGetByConditions(t *testing.T) {
user := entity.User{
Name: "validname",
Email: "valid10@email.com",
diff --git a/scripts/generate_keys.sh b/scripts/generate_keys.sh
new file mode 100644
index 0000000..d771f74
--- /dev/null
+++ b/scripts/generate_keys.sh
@@ -0,0 +1,7 @@
+# unix
+# openssl req -x509 -newkey rsa:4096 -keyout keys/private.key -out keys/publickey.crt -days 365 -nodes -subj "/CN=localhost"
+
+# windows
+# "C:\Program Files\Git\usr\bin\openssl.exe" req -x509 -newkey rsa:4096 -keyout keys/private.key -out keys/publickey.crt -days 365 -nodes -subj "/CN=localhost"
+
+echo "Open this file and You should run this scripts manually, choose between windows/ unix."
\ No newline at end of file
diff --git a/scripts/update_module.sh b/scripts/update_module.sh
new file mode 100644
index 0000000..73e853f
--- /dev/null
+++ b/scripts/update_module.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# Step 1: Remove .git directory
+rm -rf .git
+
+# Step 2: Search and replace "github.com/Lukmanern/gost" with "github.com/YourUsername/YourRepoName"
+find . -type f -exec sed -i 's/github\.com\/Lukmanern\/gost/github\.com\/YourUsername\/YourRepoName/g' {} +
+
+echo "Finish! .git directory removed and search/replace completed."
diff --git a/service/email/email_service.go b/service/email/email_service.go
index 87508ca..e503d93 100644
--- a/service/email/email_service.go
+++ b/service/email/email_service.go
@@ -11,9 +11,12 @@ import (
)
type EmailService interface {
+ // SendMail func sends message with subject to some emails address.
SendMail(emails []string, subject, message string) error
}
+// EmailServiceImpl struct contains all the
+// SMTP needs for sending emails.
// SMTP => Simple Mail Transfer Protocol
type EmailServiceImpl struct {
Server string
@@ -48,7 +51,7 @@ func NewEmailService() EmailService {
return emailService
}
-func (svc EmailServiceImpl) SendMail(emails []string, subject, message string) error {
+func (svc *EmailServiceImpl) SendMail(emails []string, subject, message string) error {
validateErr := helper.ValidateEmails(emails...)
if validateErr != nil {
return validateErr
diff --git a/service/email/email_service_test.go b/service/email/email_service_test.go
index ee6160c..ee4553b 100644
--- a/service/email/email_service_test.go
+++ b/service/email/email_service_test.go
@@ -12,10 +12,10 @@ func init() {
env.ReadConfig("./../../.env")
connector.LoadDatabase()
- connector.LoadRedisDatabase()
+ connector.LoadRedisCache()
}
-func Test_SendEmail(t *testing.T) {
+func TestSendEmail(t *testing.T) {
emailService := NewEmailService()
if emailService == nil {
t.Error("should not nil")
diff --git a/service/file/file_service.go b/service/file/file_service.go
index d5d18dd..88f8aa3 100644
--- a/service/file/file_service.go
+++ b/service/file/file_service.go
@@ -8,6 +8,7 @@ import (
"mime/multipart"
"net/http"
"strconv"
+ "sync"
"github.com/Lukmanern/gost/internal/env"
"github.com/gofiber/fiber/v2"
@@ -21,13 +22,18 @@ type FileReponse struct {
} `json:"metadata"`
}
-type UploadFile interface {
+type FileService interface {
+ // UploadFile func uploads file to supabase bucket.
UploadFile(fileHeader *multipart.FileHeader) (fileURL string, err error)
+
+ // RemoveFile func deletes a file from supabase bucket.
RemoveFile(fileName string) (err error)
+
+ // GetFilesList func get list of files from supabase bucket.
GetFilesList() (files []map[string]any, err error)
}
-type client struct {
+type FileServiceImpl struct {
PublicURL string
ListFilesURL string
UploadURL string
@@ -35,19 +41,27 @@ type client struct {
Token string
}
-func NewFileService() UploadFile {
- config := env.Configuration()
- baseURL := config.BucketURL + "/storage/v1/object/"
- return &client{
- PublicURL: baseURL + "public/" + config.BucketName + "/",
- ListFilesURL: baseURL + "list/" + config.BucketName,
- UploadURL: baseURL + config.BucketName + "/",
- DeleteURL: baseURL + config.BucketName,
- Token: config.BucketToken,
- }
+var (
+ fileService *FileServiceImpl
+ fileServiceOnce sync.Once
+)
+
+func NewFileService() FileService {
+ fileServiceOnce.Do(func() {
+ config := env.Configuration()
+ baseURL := config.BucketURL + "/storage/v1/object/"
+ fileService = &FileServiceImpl{
+ PublicURL: baseURL + "public/" + config.BucketName + "/",
+ ListFilesURL: baseURL + "list/" + config.BucketName,
+ UploadURL: baseURL + config.BucketName + "/",
+ DeleteURL: baseURL + config.BucketName,
+ Token: config.BucketToken,
+ }
+ })
+ return fileService
}
-func (c *client) UploadFile(fileHeader *multipart.FileHeader) (fileURL string, err error) {
+func (c FileServiceImpl) UploadFile(fileHeader *multipart.FileHeader) (fileURL string, err error) {
fileName := fileHeader.Filename
file, headerErr := fileHeader.Open()
if headerErr != nil {
@@ -72,10 +86,10 @@ func (c *client) UploadFile(fileHeader *multipart.FileHeader) (fileURL string, e
return "", newReqErr
}
- request.Header.Set("Authorization", "Bearer "+c.Token)
- request.Header.Set("Content-Type", writer.FormDataContentType())
- client := &http.Client{}
- respUpload, doErr := client.Do(request)
+ request.Header.Set(fiber.HeaderAuthorization, "Bearer "+c.Token)
+ request.Header.Set(fiber.HeaderContentType, writer.FormDataContentType())
+ FileServiceImpl := &http.Client{}
+ respUpload, doErr := FileServiceImpl.Do(request)
if doErr != nil {
return "", doErr
}
@@ -90,7 +104,7 @@ func (c *client) UploadFile(fileHeader *multipart.FileHeader) (fileURL string, e
if reqErr != nil {
return "", reqErr
}
- respTestGet, doErr := client.Do(reqTestGet)
+ respTestGet, doErr := FileServiceImpl.Do(reqTestGet)
if doErr != nil {
return "", doErr
}
@@ -101,7 +115,7 @@ func (c *client) UploadFile(fileHeader *multipart.FileHeader) (fileURL string, e
return link, nil
}
-func (c *client) RemoveFile(fileName string) (err error) {
+func (c FileServiceImpl) RemoveFile(fileName string) (err error) {
body := map[string]interface{}{
"prefixes": fileName,
}
@@ -114,10 +128,10 @@ func (c *client) RemoveFile(fileName string) (err error) {
return err
}
- request.Header.Set("Authorization", "Bearer "+c.Token)
- request.Header.Set("Content-Type", "application/json")
- client := &http.Client{}
- response, err := client.Do(request)
+ request.Header.Set(fiber.HeaderAuthorization, "Bearer "+c.Token)
+ request.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
+ FileServiceImpl := &http.Client{}
+ response, err := FileServiceImpl.Do(request)
if err != nil {
return responseErrHandler(response)
}
@@ -140,7 +154,7 @@ func (c *client) RemoveFile(fileName string) (err error) {
return nil
}
-func (c *client) GetFilesList() (files []map[string]any, err error) {
+func (c FileServiceImpl) GetFilesList() (files []map[string]any, err error) {
type sortBy struct {
Column string `json:"column"`
Order string `json:"order"`
@@ -171,10 +185,10 @@ func (c *client) GetFilesList() (files []map[string]any, err error) {
return nil, err
}
- request.Header.Set("Authorization", "Bearer "+c.Token)
- request.Header.Set("Content-Type", "application/json")
- client := &http.Client{}
- response, err := client.Do(request)
+ request.Header.Set(fiber.HeaderAuthorization, "Bearer "+c.Token)
+ request.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
+ FileServiceImpl := &http.Client{}
+ response, err := FileServiceImpl.Do(request)
if err != nil {
return nil, responseErrHandler(response)
}
diff --git a/service/rbac/permission_service.go b/service/permission/permission_service.go
similarity index 69%
rename from service/rbac/permission_service.go
rename to service/permission/permission_service.go
index 8608eb7..31e742d 100644
--- a/service/rbac/permission_service.go
+++ b/service/permission/permission_service.go
@@ -11,14 +11,23 @@ import (
"github.com/Lukmanern/gost/domain/base"
"github.com/Lukmanern/gost/domain/entity"
"github.com/Lukmanern/gost/domain/model"
- repository "github.com/Lukmanern/gost/repository/rbac"
+ repository "github.com/Lukmanern/gost/repository/permission"
)
type PermissionService interface {
+ // Create func create one permission.
Create(ctx context.Context, permission model.PermissionCreate) (id int, err error)
+
+ // GetByID func get one permission by ID.
GetByID(ctx context.Context, id int) (permission *model.PermissionResponse, err error)
+
+ // GetAll func get some permissions with payload.
GetAll(ctx context.Context, filter base.RequestGetAll) (permissions []model.PermissionResponse, total int, err error)
+
+ // Update func update one permission by ID and payload.
Update(ctx context.Context, permission model.PermissionUpdate) (err error)
+
+ // Delete func delete one permission by ID.
Delete(ctx context.Context, id int) (err error)
}
@@ -31,6 +40,8 @@ var (
permissionServiceImplOnce sync.Once
)
+const permNotFound = "permission/s not found"
+
func NewPermissionService() PermissionService {
permissionServiceImplOnce.Do(func() {
permissionServiceImpl = &PermissionServiceImpl{
@@ -40,7 +51,7 @@ func NewPermissionService() PermissionService {
return permissionServiceImpl
}
-func (svc PermissionServiceImpl) Create(ctx context.Context, permission model.PermissionCreate) (id int, err error) {
+func (svc *PermissionServiceImpl) Create(ctx context.Context, permission model.PermissionCreate) (id int, err error) {
permission.Name = strings.ToLower(permission.Name)
checkPermission, getErr := svc.repository.GetByName(ctx, permission.Name)
@@ -51,7 +62,7 @@ func (svc PermissionServiceImpl) Create(ctx context.Context, permission model.Pe
Name: permission.Name,
Description: permission.Description,
}
- entityPermission.SetCreateTimes()
+ entityPermission.SetCreateTime()
id, err = svc.repository.Create(ctx, entityPermission)
if err != nil {
return 0, err
@@ -59,16 +70,16 @@ func (svc PermissionServiceImpl) Create(ctx context.Context, permission model.Pe
return id, nil
}
-func (svc PermissionServiceImpl) GetByID(ctx context.Context, id int) (permission *model.PermissionResponse, err error) {
+func (svc *PermissionServiceImpl) GetByID(ctx context.Context, id int) (permission *model.PermissionResponse, err error) {
permissionEntity, getErr := svc.repository.GetByID(ctx, id)
if getErr != nil {
if getErr == gorm.ErrRecordNotFound {
- return nil, fiber.NewError(fiber.StatusNotFound, "permission not found")
+ return nil, fiber.NewError(fiber.StatusNotFound, permNotFound)
}
return nil, getErr
}
if permissionEntity == nil {
- return nil, fiber.NewError(fiber.StatusNotFound, "permission not found")
+ return nil, fiber.NewError(fiber.StatusNotFound, permNotFound)
}
permission = &model.PermissionResponse{
@@ -79,7 +90,7 @@ func (svc PermissionServiceImpl) GetByID(ctx context.Context, id int) (permissio
return permission, nil
}
-func (svc PermissionServiceImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (permissions []model.PermissionResponse, total int, err error) {
+func (svc *PermissionServiceImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (permissions []model.PermissionResponse, total int, err error) {
permissionEntities, total, err := svc.repository.GetAll(ctx, filter)
if err != nil {
return nil, 0, err
@@ -98,7 +109,7 @@ func (svc PermissionServiceImpl) GetAll(ctx context.Context, filter base.Request
return permissions, total, nil
}
-func (svc PermissionServiceImpl) Update(ctx context.Context, data model.PermissionUpdate) (err error) {
+func (svc *PermissionServiceImpl) Update(ctx context.Context, data model.PermissionUpdate) (err error) {
data.Name = strings.ToLower(data.Name)
permissionByName, getErr := svc.repository.GetByName(ctx, data.Name)
if getErr != nil && getErr != gorm.ErrRecordNotFound {
@@ -111,12 +122,12 @@ func (svc PermissionServiceImpl) Update(ctx context.Context, data model.Permissi
permissionByID, getErr := svc.repository.GetByID(ctx, data.ID)
if getErr != nil {
if getErr == gorm.ErrRecordNotFound {
- return fiber.NewError(fiber.StatusNotFound, "permission not found")
+ return fiber.NewError(fiber.StatusNotFound, permNotFound)
}
return getErr
}
if permissionByID == nil {
- return fiber.NewError(fiber.StatusNotFound, "permission not found")
+ return fiber.NewError(fiber.StatusNotFound, permNotFound)
}
entityRole := entity.Permission{
@@ -132,16 +143,16 @@ func (svc PermissionServiceImpl) Update(ctx context.Context, data model.Permissi
return nil
}
-func (svc PermissionServiceImpl) Delete(ctx context.Context, id int) (err error) {
+func (svc *PermissionServiceImpl) Delete(ctx context.Context, id int) (err error) {
permission, getErr := svc.repository.GetByID(ctx, id)
if getErr != nil {
if getErr == gorm.ErrRecordNotFound {
- return fiber.NewError(fiber.StatusNotFound, "permission not found")
+ return fiber.NewError(fiber.StatusNotFound, permNotFound)
}
return getErr
}
if permission == nil {
- return fiber.NewError(fiber.StatusNotFound, "permission not found")
+ return fiber.NewError(fiber.StatusNotFound, permNotFound)
}
err = svc.repository.Delete(ctx, id)
if err != nil {
diff --git a/service/rbac/permission_service_test.go b/service/permission/permission_service_test.go
similarity index 86%
rename from service/rbac/permission_service_test.go
rename to service/permission/permission_service_test.go
index b917607..e62aff0 100644
--- a/service/rbac/permission_service_test.go
+++ b/service/permission/permission_service_test.go
@@ -11,6 +11,7 @@ import (
"github.com/Lukmanern/gost/database/connector"
"github.com/Lukmanern/gost/domain/base"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/env"
"github.com/Lukmanern/gost/internal/helper"
)
@@ -20,13 +21,13 @@ func init() {
env.ReadConfig("./../../.env")
connector.LoadDatabase()
- connector.LoadRedisDatabase()
+ connector.LoadRedisCache()
}
func TestNewPermissionService(t *testing.T) {
svc := NewPermissionService()
if svc == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
}
@@ -37,12 +38,12 @@ func TestNewPermissionService(t *testing.T) {
// -> delete
// -> get by id
-func TestSuccessCRUD_Permission(t *testing.T) {
+func TestSuccessCrudPermission(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
svc := NewPermissionService()
if svc == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
modelPerm := model.PermissionCreate{
Name: strings.ToLower(helper.RandomString(10)),
@@ -76,7 +77,7 @@ func TestSuccessCRUD_Permission(t *testing.T) {
}
updateErr := svc.Update(ctx, updatePermModel)
if updateErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
// value reset
@@ -92,7 +93,7 @@ func TestSuccessCRUD_Permission(t *testing.T) {
deleteErr := svc.Delete(ctx, permID)
if deleteErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
// value reset
@@ -104,12 +105,12 @@ func TestSuccessCRUD_Permission(t *testing.T) {
}
}
-func TestFailedCRUD_Permission(t *testing.T) {
+func TestFailedCrudPermission(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
svc := NewPermissionService()
if svc == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
modelPerm := model.PermissionCreate{
Name: strings.ToLower(helper.RandomString(10)),
@@ -135,11 +136,11 @@ func TestFailedCRUD_Permission(t *testing.T) {
}
updateErr := svc.Update(ctx, updatePermModel)
if updateErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
deleteErr := svc.Delete(ctx, -10)
if deleteErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
}
diff --git a/service/rbac/role_service.go b/service/role/role_service.go
similarity index 67%
rename from service/rbac/role_service.go
rename to service/role/role_service.go
index 1a191d8..5969f5b 100644
--- a/service/rbac/role_service.go
+++ b/service/role/role_service.go
@@ -11,21 +11,34 @@ import (
"github.com/Lukmanern/gost/domain/base"
"github.com/Lukmanern/gost/domain/entity"
"github.com/Lukmanern/gost/domain/model"
- repository "github.com/Lukmanern/gost/repository/rbac"
+ repository "github.com/Lukmanern/gost/repository/role"
+ permService "github.com/Lukmanern/gost/service/permission"
)
type RoleService interface {
+
+ // Create func create one role.
Create(ctx context.Context, data model.RoleCreate) (id int, err error)
+
+ // ConnectPermissions func connect one role with one or more permissions.
ConnectPermissions(ctx context.Context, data model.RoleConnectToPermissions) (err error)
+
+ // GetByID func get one role.
GetByID(ctx context.Context, id int) (role *entity.Role, err error)
+
+ // GetAll func get some roles.
GetAll(ctx context.Context, filter base.RequestGetAll) (roles []model.RoleResponse, total int, err error)
+
+ // Update func update one role.
Update(ctx context.Context, data model.RoleUpdate) (err error)
+
+ // Delete func delete one role.
Delete(ctx context.Context, id int) (err error)
}
type RoleServiceImpl struct {
repository repository.RoleRepository
- servicePermission PermissionService
+ servicePermission permService.PermissionService
}
var (
@@ -33,7 +46,9 @@ var (
roleServiceImplOnce sync.Once
)
-func NewRoleService(servicePermission PermissionService) RoleService {
+const roleNotFound = "role/s not found"
+
+func NewRoleService(servicePermission permService.PermissionService) RoleService {
roleServiceImplOnce.Do(func() {
roleServiceImpl = &RoleServiceImpl{
repository: repository.NewRoleRepository(),
@@ -43,7 +58,7 @@ func NewRoleService(servicePermission PermissionService) RoleService {
return roleServiceImpl
}
-func (svc RoleServiceImpl) Create(ctx context.Context, data model.RoleCreate) (id int, err error) {
+func (svc *RoleServiceImpl) Create(ctx context.Context, data model.RoleCreate) (id int, err error) {
data.Name = strings.ToLower(data.Name)
for _, id := range data.PermissionsID {
permission, getErr := svc.servicePermission.GetByID(ctx, id)
@@ -60,7 +75,7 @@ func (svc RoleServiceImpl) Create(ctx context.Context, data model.RoleCreate) (i
Name: data.Name,
Description: data.Description,
}
- entityRole.SetCreateTimes()
+ entityRole.SetCreateTime()
id, err = svc.repository.Create(ctx, entityRole, data.PermissionsID)
if err != nil {
return 0, err
@@ -68,16 +83,16 @@ func (svc RoleServiceImpl) Create(ctx context.Context, data model.RoleCreate) (i
return id, nil
}
-func (svc RoleServiceImpl) ConnectPermissions(ctx context.Context, data model.RoleConnectToPermissions) (err error) {
+func (svc *RoleServiceImpl) ConnectPermissions(ctx context.Context, data model.RoleConnectToPermissions) (err error) {
role, getErr := svc.repository.GetByID(ctx, data.RoleID)
if getErr != nil {
if getErr == gorm.ErrRecordNotFound {
- return fiber.NewError(fiber.StatusNotFound, "role not found")
+ return fiber.NewError(fiber.StatusNotFound, roleNotFound)
}
return getErr
}
if role == nil {
- return fiber.NewError(fiber.StatusNotFound, "role not found")
+ return fiber.NewError(fiber.StatusNotFound, roleNotFound)
}
for _, id := range data.PermissionsID {
permission, getErr := svc.servicePermission.GetByID(ctx, id)
@@ -93,21 +108,21 @@ func (svc RoleServiceImpl) ConnectPermissions(ctx context.Context, data model.Ro
return nil
}
-func (svc RoleServiceImpl) GetByID(ctx context.Context, id int) (role *entity.Role, err error) {
+func (svc *RoleServiceImpl) GetByID(ctx context.Context, id int) (role *entity.Role, err error) {
role, err = svc.repository.GetByID(ctx, id)
if err != nil {
if err == gorm.ErrRecordNotFound {
- return nil, fiber.NewError(fiber.StatusNotFound, "role not found")
+ return nil, fiber.NewError(fiber.StatusNotFound, roleNotFound)
}
return nil, err
}
if role == nil {
- return nil, fiber.NewError(fiber.StatusNotFound, "role not found")
+ return nil, fiber.NewError(fiber.StatusNotFound, roleNotFound)
}
return role, nil
}
-func (svc RoleServiceImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (roles []model.RoleResponse, total int, err error) {
+func (svc *RoleServiceImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (roles []model.RoleResponse, total int, err error) {
roleEntities, total, err := svc.repository.GetAll(ctx, filter)
if err != nil {
return nil, 0, err
@@ -125,7 +140,7 @@ func (svc RoleServiceImpl) GetAll(ctx context.Context, filter base.RequestGetAll
return roles, total, nil
}
-func (svc RoleServiceImpl) Update(ctx context.Context, data model.RoleUpdate) (err error) {
+func (svc *RoleServiceImpl) Update(ctx context.Context, data model.RoleUpdate) (err error) {
data.Name = strings.ToLower(data.Name)
roleByName, getErr := svc.repository.GetByName(ctx, data.Name)
if getErr != nil && getErr != gorm.ErrRecordNotFound {
@@ -138,12 +153,12 @@ func (svc RoleServiceImpl) Update(ctx context.Context, data model.RoleUpdate) (e
roleByID, getErr := svc.repository.GetByID(ctx, data.ID)
if getErr != nil {
if getErr == gorm.ErrRecordNotFound {
- return fiber.NewError(fiber.StatusNotFound, "role not found")
+ return fiber.NewError(fiber.StatusNotFound, roleNotFound)
}
return getErr
}
if roleByID == nil {
- return fiber.NewError(fiber.StatusNotFound, "role not found")
+ return fiber.NewError(fiber.StatusNotFound, roleNotFound)
}
entityRole := entity.Role{
@@ -159,16 +174,16 @@ func (svc RoleServiceImpl) Update(ctx context.Context, data model.RoleUpdate) (e
return nil
}
-func (svc RoleServiceImpl) Delete(ctx context.Context, id int) (err error) {
+func (svc *RoleServiceImpl) Delete(ctx context.Context, id int) (err error) {
role, getErr := svc.repository.GetByID(ctx, id)
if getErr != nil {
if getErr == gorm.ErrRecordNotFound {
- return fiber.NewError(fiber.StatusNotFound, "role not found")
+ return fiber.NewError(fiber.StatusNotFound, roleNotFound)
}
return getErr
}
if role == nil {
- return fiber.NewError(fiber.StatusNotFound, "role not found")
+ return fiber.NewError(fiber.StatusNotFound, roleNotFound)
}
err = svc.repository.Delete(ctx, id)
if err != nil {
diff --git a/service/rbac/role_service_test.go b/service/role/role_service_test.go
similarity index 75%
rename from service/rbac/role_service_test.go
rename to service/role/role_service_test.go
index d374948..1b54a19 100644
--- a/service/rbac/role_service_test.go
+++ b/service/role/role_service_test.go
@@ -11,8 +11,10 @@ import (
"github.com/Lukmanern/gost/database/connector"
"github.com/Lukmanern/gost/domain/base"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/env"
"github.com/Lukmanern/gost/internal/helper"
+ permService "github.com/Lukmanern/gost/service/permission"
)
func init() {
@@ -20,30 +22,29 @@ func init() {
env.ReadConfig("./../../.env")
connector.LoadDatabase()
- connector.LoadRedisDatabase()
+ connector.LoadRedisCache()
}
func TestNewRoleService(t *testing.T) {
- permSvc := NewPermissionService()
+ permSvc := permService.NewPermissionService()
svc := NewRoleService(permSvc)
if svc == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
}
// create 1 role, create 4 permissions
// trying to connect
-
-func TestSuccessCRUD_Role(t *testing.T) {
+func TestSuccessCrudRole(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
- permSvc := NewPermissionService()
+ permSvc := permService.NewPermissionService()
if permSvc == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
svc := NewRoleService(permSvc)
if svc == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
modelRole := model.RoleCreate{
@@ -55,7 +56,7 @@ func TestSuccessCRUD_Role(t *testing.T) {
t.Error("should not error and id should more than zero")
}
- // save the id for delete the perms
+ // Save the ID for deleting the permissions
permsID := make([]int, 0)
for i := 0; i < 3; i++ {
modelPerm := model.PermissionCreate{
@@ -64,7 +65,7 @@ func TestSuccessCRUD_Role(t *testing.T) {
}
permID, createErr := permSvc.Create(ctx, modelPerm)
if createErr != nil || permID < 1 {
- t.Error("should not error and permID should more than one")
+ t.Error("should not error and permID should be more than one")
}
permsID = append(permsID, permID)
@@ -77,14 +78,14 @@ func TestSuccessCRUD_Role(t *testing.T) {
}
}()
- // success connect
+ // Success connect
modelConnect := model.RoleConnectToPermissions{
RoleID: roleID,
PermissionsID: permsID,
}
connectErr := svc.ConnectPermissions(ctx, modelConnect)
if connectErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
roleByID, getErr := svc.GetByID(ctx, roleID)
@@ -92,12 +93,12 @@ func TestSuccessCRUD_Role(t *testing.T) {
t.Error("should not error and role not nil")
}
if len(roleByID.Permissions) != len(permsID) {
- t.Error("total of permissions connected by role should equal")
+ t.Error("total of permissions connected by role should be equal")
}
roles, total, getAllErr := svc.GetAll(ctx, base.RequestGetAll{Limit: 10, Page: 1})
if len(roles) < 1 || total < 1 || getAllErr != nil {
- t.Error("should more than or equal one and not error at all")
+ t.Error("should be more than or equal to one and not error at all")
}
updateRoleModel := model.RoleUpdate{
@@ -107,44 +108,44 @@ func TestSuccessCRUD_Role(t *testing.T) {
}
updateErr := svc.Update(ctx, updateRoleModel)
if updateErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
- // value reset
+ // Value reset
roleByID = nil
getErr = nil
roleByID, getErr = svc.GetByID(ctx, roleID)
if getErr != nil || roleByID == nil {
- t.Error("should not error and roleByID should not nil")
+ t.Error("should not error and roleByID should not be nil")
}
if roleByID.Name != updateRoleModel.Name || roleByID.Description != updateRoleModel.Description {
- t.Error("name and desc should same")
+ t.Error("name and description should be the same")
}
deleteErr := svc.Delete(ctx, roleID)
if deleteErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
- // value reset
+ // Value reset
roleByID = nil
getErr = nil
roleByID, getErr = svc.GetByID(ctx, roleID)
if getErr == nil || roleByID != nil {
- t.Error("should error and roleByID should nil")
+ t.Error("should error and roleByID should be nil")
}
}
-func TestFailedCRUD_Roles(t *testing.T) {
+func TestFailedCrudRoles(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
- permSvc := NewPermissionService()
+ permSvc := permService.NewPermissionService()
if permSvc == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
svc := NewRoleService(permSvc)
if svc == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
// failed create: permissions not found
@@ -181,7 +182,7 @@ func TestFailedCRUD_Roles(t *testing.T) {
}
connectErr := svc.ConnectPermissions(ctx, modelConnectFailed)
if connectErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
modelConnectFailed = model.RoleConnectToPermissions{
@@ -191,7 +192,7 @@ func TestFailedCRUD_Roles(t *testing.T) {
connectErr = nil
connectErr = svc.ConnectPermissions(ctx, modelConnectFailed)
if connectErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
// failed update
@@ -202,12 +203,12 @@ func TestFailedCRUD_Roles(t *testing.T) {
}
updateErr := svc.Update(ctx, updateRoleModel)
if updateErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
// failed delete
deleteErr := svc.Delete(ctx, -1)
if deleteErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
}
diff --git a/service/user/user_service.go b/service/user/user_service.go
index 77fe87d..89de676 100644
--- a/service/user/user_service.go
+++ b/service/user/user_service.go
@@ -10,33 +10,64 @@ import (
"github.com/go-redis/redis"
"github.com/gofiber/fiber/v2"
- "golang.org/x/text/cases"
- "golang.org/x/text/language"
"gorm.io/gorm"
"github.com/Lukmanern/gost/database/connector"
"github.com/Lukmanern/gost/domain/entity"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/env"
"github.com/Lukmanern/gost/internal/hash"
"github.com/Lukmanern/gost/internal/helper"
"github.com/Lukmanern/gost/internal/middleware"
repository "github.com/Lukmanern/gost/repository/user"
emailService "github.com/Lukmanern/gost/service/email"
- roleService "github.com/Lukmanern/gost/service/rbac"
+ roleService "github.com/Lukmanern/gost/service/role"
)
type UserService interface {
+
+ // Register function register user account, than send verification-code to email
Register(ctx context.Context, user model.UserRegister) (id int, err error)
+
+ // Verification function activates user account with
+ // verification code that has been sended to the user's email
Verification(ctx context.Context, verifyData model.UserVerificationCode) (err error)
+
+ // DeleteUserByVerification function deletes user data if the user account is not yet verified.
+ // This implies that the email owner hasn't actually registered the email, indicating that
+ // the user who registered may be making typing errors or may be a hacker attempting to get
+ // the verification code.
DeleteUserByVerification(ctx context.Context, verifyData model.UserVerificationCode) (err error)
+
+ // FailedLoginCounter function counts failed login attempts and stores them in Redis.
+ // After the N-th attempt to log in with the same IP address results in continuous failures,
+ // the system will impose a 50-minute ban. During this period, login requests (refer to
+ // the login function in the user controller) will not be processed.
FailedLoginCounter(userIP string, increment bool) (counter int, err error)
+
+ // Login func give user token/ jwt for auth header.
Login(ctx context.Context, user model.UserLogin) (token string, err error)
+
+ // Logout function stores the user's active token in Redis, effectively
+ // blacklisting the token. This ensures that the token cannot be reused
+ // for authentication (refer to the IsBlacklisted function in internal/middleware).
Logout(c *fiber.Ctx) (err error)
+
+ // ForgetPassword func send verification code into user's email
ForgetPassword(ctx context.Context, user model.UserForgetPassword) (err error)
+
+ // ResetPassword func resets password by creating
+ // new password by email and verification code
ResetPassword(ctx context.Context, user model.UserResetPassword) (err error)
+
+ // UpdatePassword func updates user's password
UpdatePassword(ctx context.Context, user model.UserPasswordUpdate) (err error)
+
+ // UpdateProfile func updates user's profile data
UpdateProfile(ctx context.Context, user model.UserProfileUpdate) (err error)
+
+ // MyProfile func shows user's profile data
MyProfile(ctx context.Context, id int) (profile model.UserProfile, err error)
}
@@ -60,14 +91,14 @@ func NewUserService(roleService roleService.RoleService) UserService {
repository: repository.NewUserRepository(),
emailService: emailService.NewEmailService(),
jwtHandler: middleware.NewJWTHandler(),
- redis: connector.LoadRedisDatabase(),
+ redis: connector.LoadRedisCache(),
}
})
return userAuthService
}
-func (svc UserServiceImpl) Register(ctx context.Context, user model.UserRegister) (id int, err error) {
+func (svc *UserServiceImpl) Register(ctx context.Context, user model.UserRegister) (id int, err error) {
// search user by email
// if exist, return error
userByEmail, getUserErr := svc.repository.GetByEmail(ctx, user.Email)
@@ -98,7 +129,7 @@ func (svc UserServiceImpl) Register(ctx context.Context, user model.UserRegister
if getByCodeErr != nil || userGetByCode == nil {
break
}
- counter += 1
+ counter++
if counter >= 150 {
return 0, errors.New("failed generating verification code")
}
@@ -110,21 +141,21 @@ func (svc UserServiceImpl) Register(ctx context.Context, user model.UserRegister
if hashErr == nil {
break
}
- counter += 1
+ counter++
if counter >= 150 {
return 0, errors.New("failed hashing user password")
}
}
userEntity := entity.User{
- Name: cases.Title(language.Und).String(user.Name),
+ Name: helper.ToTitle(user.Name),
Email: user.Email,
Password: passwordHashed,
VerificationCode: &verifCode,
ActivatedAt: nil,
}
// set created_at and updated_at equal to now
- userEntity.SetCreateTimes()
+ userEntity.SetCreateTime()
id, err = svc.repository.Create(ctx, userEntity, user.RoleID)
if err != nil {
return 0, err
@@ -148,7 +179,7 @@ func (svc UserServiceImpl) Register(ctx context.Context, user model.UserRegister
return id, nil
}
-func (svc UserServiceImpl) Verification(ctx context.Context, verifyData model.UserVerificationCode) (err error) {
+func (svc *UserServiceImpl) Verification(ctx context.Context, verifyData model.UserVerificationCode) (err error) {
// search user by code, if not exist return error
userEntity, getByCodeErr := svc.repository.GetByConditions(ctx, map[string]any{
"verification_code =": verifyData.Code,
@@ -171,7 +202,7 @@ func (svc UserServiceImpl) Verification(ctx context.Context, verifyData model.Us
return nil
}
-func (svc UserServiceImpl) DeleteUserByVerification(ctx context.Context, verifyData model.UserVerificationCode) (err error) {
+func (svc *UserServiceImpl) DeleteUserByVerification(ctx context.Context, verifyData model.UserVerificationCode) (err error) {
// search user by code, if not exist return error
userEntity, getByCodeErr := svc.repository.GetByConditions(ctx, map[string]any{
"verification_code =": verifyData.Code,
@@ -191,7 +222,7 @@ func (svc UserServiceImpl) DeleteUserByVerification(ctx context.Context, verifyD
return nil
}
-func (svc UserServiceImpl) FailedLoginCounter(userIP string, increment bool) (counter int, err error) {
+func (svc *UserServiceImpl) FailedLoginCounter(userIP string, increment bool) (counter int, err error) {
// set key for banned counter
key := "failed-login-" + userIP
getStatus := svc.redis.Get(key)
@@ -206,18 +237,18 @@ func (svc UserServiceImpl) FailedLoginCounter(userIP string, increment bool) (co
return counter, nil
}
-func (svc UserServiceImpl) Login(ctx context.Context, user model.UserLogin) (token string, err error) {
+func (svc *UserServiceImpl) Login(ctx context.Context, user model.UserLogin) (token string, err error) {
// search user by email
// if not exist/found, return error
userEntity, err := svc.repository.GetByEmail(ctx, user.Email)
if err != nil {
if err == gorm.ErrRecordNotFound {
- return "", fiber.NewError(fiber.StatusNotFound, "data not found")
+ return "", fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
return "", err
}
if userEntity == nil {
- return "", fiber.NewError(fiber.StatusNotFound, "data not found")
+ return "", fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
res, verfiryErr := hash.Verify(userEntity.Password, user.Password)
@@ -255,7 +286,7 @@ func (svc UserServiceImpl) Login(ctx context.Context, user model.UserLogin) (tok
return token, nil
}
-func (svc UserServiceImpl) Logout(c *fiber.Ctx) (err error) {
+func (svc *UserServiceImpl) Logout(c *fiber.Ctx) (err error) {
err = svc.jwtHandler.InvalidateToken(c)
if err != nil {
return errors.New("problem invalidating token")
@@ -264,16 +295,16 @@ func (svc UserServiceImpl) Logout(c *fiber.Ctx) (err error) {
return nil
}
-func (svc UserServiceImpl) ForgetPassword(ctx context.Context, user model.UserForgetPassword) (err error) {
+func (svc *UserServiceImpl) ForgetPassword(ctx context.Context, user model.UserForgetPassword) (err error) {
userEntity, err := svc.repository.GetByEmail(ctx, user.Email)
if err != nil {
if err == gorm.ErrRecordNotFound {
- return fiber.NewError(fiber.StatusNotFound, "data not found")
+ return fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
return err
}
if userEntity == nil {
- return fiber.NewError(fiber.StatusNotFound, "data not found")
+ return fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
if userEntity.ActivatedAt == nil {
message := "your account has not been activated since register, please check your inbox/ spam mail."
@@ -292,7 +323,7 @@ func (svc UserServiceImpl) ForgetPassword(ctx context.Context, user model.UserFo
if getByCodeErr != nil || userGetByCode == nil {
break
}
- counter += 1
+ counter++
if counter >= 150 {
return errors.New("failed generating verification code")
}
@@ -324,13 +355,14 @@ func (svc UserServiceImpl) ForgetPassword(ctx context.Context, user model.UserFo
return nil
}
-func (svc UserServiceImpl) ResetPassword(ctx context.Context, user model.UserResetPassword) (err error) {
+func (svc *UserServiceImpl) ResetPassword(ctx context.Context, user model.UserResetPassword) (err error) {
userByCode, err := svc.repository.GetByConditions(ctx, map[string]any{
+ "email =": user.Email,
"verification_code =": user.Code,
})
if err != nil {
if err == gorm.ErrRecordNotFound {
- return fiber.NewError(fiber.StatusNotFound, "data not found")
+ return fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
return err
}
@@ -353,13 +385,14 @@ func (svc UserServiceImpl) ResetPassword(ctx context.Context, user model.UserRes
if hashErr == nil {
break
}
- counter += 1
+ counter++
if counter >= 150 {
return errors.New("failed hashing user password")
}
}
userByCode.VerificationCode = nil
+ userByCode.SetUpdateTime()
updateErr := svc.repository.Update(ctx, *userByCode)
if updateErr != nil {
return updateErr
@@ -372,11 +405,11 @@ func (svc UserServiceImpl) ResetPassword(ctx context.Context, user model.UserRes
return nil
}
-func (svc UserServiceImpl) UpdatePassword(ctx context.Context, user model.UserPasswordUpdate) (err error) {
+func (svc *UserServiceImpl) UpdatePassword(ctx context.Context, user model.UserPasswordUpdate) (err error) {
userByID, err := svc.repository.GetByID(ctx, user.ID)
if err != nil {
if err == gorm.ErrRecordNotFound {
- return fiber.NewError(fiber.StatusNotFound, "data not found")
+ return fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
return err
}
@@ -402,7 +435,7 @@ func (svc UserServiceImpl) UpdatePassword(ctx context.Context, user model.UserPa
if hashErr == nil {
break
}
- counter += 1
+ counter++
if counter >= 150 {
return errors.New("failed hashing user password")
}
@@ -415,7 +448,7 @@ func (svc UserServiceImpl) UpdatePassword(ctx context.Context, user model.UserPa
return nil
}
-func (svc UserServiceImpl) MyProfile(ctx context.Context, id int) (profile model.UserProfile, err error) {
+func (svc *UserServiceImpl) MyProfile(ctx context.Context, id int) (profile model.UserProfile, err error) {
// search profile by ID
user, err := svc.repository.GetByID(ctx, id)
if err != nil {
@@ -440,22 +473,22 @@ func (svc UserServiceImpl) MyProfile(ctx context.Context, id int) (profile model
return profile, nil
}
-func (svc UserServiceImpl) UpdateProfile(ctx context.Context, user model.UserProfileUpdate) (err error) {
+func (svc *UserServiceImpl) UpdateProfile(ctx context.Context, user model.UserProfileUpdate) (err error) {
// search profile by ID
userByID, getErr := svc.repository.GetByID(ctx, user.ID)
if getErr != nil {
if getErr == gorm.ErrRecordNotFound {
- return fiber.NewError(fiber.StatusNotFound, "data not found")
+ return fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
return err
}
if userByID == nil {
- return fiber.NewError(fiber.StatusNotFound, "data not found")
+ return fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
userEntity := entity.User{
ID: user.ID,
- Name: cases.Title(language.Und).String(user.Name),
+ Name: helper.ToTitle(user.Name),
VerificationCode: userByID.VerificationCode,
ActivatedAt: userByID.ActivatedAt,
}
diff --git a/service/user/user_service_test.go b/service/user/user_service_test.go
index 6451f94..00aa5c0 100644
--- a/service/user/user_service_test.go
+++ b/service/user/user_service_test.go
@@ -8,16 +8,16 @@ import (
"testing"
"github.com/gofiber/fiber/v2"
- "golang.org/x/text/cases"
- "golang.org/x/text/language"
"github.com/Lukmanern/gost/database/connector"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/env"
"github.com/Lukmanern/gost/internal/helper"
"github.com/Lukmanern/gost/internal/middleware"
repository "github.com/Lukmanern/gost/repository/user"
- rbacService "github.com/Lukmanern/gost/service/rbac"
+ permService "github.com/Lukmanern/gost/service/permission"
+ roleService "github.com/Lukmanern/gost/service/role"
)
func init() {
@@ -25,34 +25,34 @@ func init() {
env.ReadConfig("./../../.env")
connector.LoadDatabase()
- connector.LoadRedisDatabase()
+ connector.LoadRedisCache()
}
func TestNewUserService(t *testing.T) {
- permSvc := rbacService.NewPermissionService()
- roleSvc := rbacService.NewRoleService(permSvc)
+ permSvc := permService.NewPermissionService()
+ roleSvc := roleService.NewRoleService(permSvc)
svc := NewUserService(roleSvc)
if svc == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
}
-func Test_SuccessRegister(t *testing.T) {
+func TestSuccessRegister(t *testing.T) {
defer func() {
- connector.LoadRedisDatabase().FlushAll()
+ connector.LoadRedisCache().FlushAll()
}()
- permSvc := rbacService.NewPermissionService()
- roleSvc := rbacService.NewRoleService(permSvc)
+ permSvc := permService.NewPermissionService()
+ roleSvc := roleService.NewRoleService(permSvc)
svc := NewUserService(roleSvc)
c := helper.NewFiberCtx()
ctx := c.Context()
if svc == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
userRepo := repository.NewUserRepository()
if userRepo == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
modelUserRegis := model.UserRegister{
@@ -74,16 +74,16 @@ func Test_SuccessRegister(t *testing.T) {
if getErr != nil || userByID == nil {
t.Error("should not error and id should not nil")
}
- if userByID.Name != cases.Title(language.Und).String(modelUserRegis.Name) ||
+ if userByID.Name != helper.ToTitle(modelUserRegis.Name) ||
userByID.Email != modelUserRegis.Email ||
userByID.Roles[0].ID != modelUserRegis.RoleID {
t.Error("should equal")
}
if userByID.VerificationCode == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
if userByID.ActivatedAt != nil {
- t.Error("should nil")
+ t.Error(constants.ShouldNil)
}
// failed login : account is created,
@@ -131,7 +131,7 @@ func Test_SuccessRegister(t *testing.T) {
Email: userByID.Email,
})
if verifErr != nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
// value reset
@@ -142,10 +142,10 @@ func Test_SuccessRegister(t *testing.T) {
t.Error("should not error and id should not nil")
}
if userByID.VerificationCode != nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
if userByID.ActivatedAt == nil {
- t.Error("should nil")
+ t.Error(constants.ShouldNil)
}
// reset value
@@ -162,9 +162,6 @@ func Test_SuccessRegister(t *testing.T) {
}
jwtHandler := middleware.NewJWTHandler()
- if !jwtHandler.IsTokenValid(token) {
- t.Error("token should valid")
- }
if jwtHandler.IsBlacklisted(token) {
t.Error("should not in black-list")
}
@@ -174,7 +171,7 @@ func Test_SuccessRegister(t *testing.T) {
}
forgetPwErr := svc.ForgetPassword(ctx, modelUserForgetPasswd)
if forgetPwErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
// value reset
@@ -185,21 +182,22 @@ func Test_SuccessRegister(t *testing.T) {
t.Error("should not error and id should not nil")
}
if userByID.VerificationCode == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
if userByID.ActivatedAt == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
passwd := helper.RandomString(12)
modelUserResetPasswd := model.UserResetPassword{
+ Email: userByID.Email,
Code: *userByID.VerificationCode,
NewPassword: passwd,
NewPasswordConfirm: passwd,
}
resetErr := svc.ResetPassword(ctx, modelUserResetPasswd)
if resetErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
// reset value, login failed
@@ -237,7 +235,7 @@ func Test_SuccessRegister(t *testing.T) {
}
updatePasswdErr := svc.UpdatePassword(ctx, modelUserUpdatePasswd)
if updatePasswdErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
// reset value, login success
@@ -259,14 +257,14 @@ func Test_SuccessRegister(t *testing.T) {
}
updateProfileErr := svc.UpdateProfile(ctx, modelUserUpdate)
if updateProfileErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
profile, getErr := svc.MyProfile(ctx, userID)
if getErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
- if profile.Name != cases.Title(language.Und).String(modelUserUpdate.Name) {
+ if profile.Name != helper.ToTitle(modelUserUpdate.Name) {
t.Error("should equal")
}
@@ -278,22 +276,22 @@ func Test_SuccessRegister(t *testing.T) {
}
}
-func Test_FailedRegister(t *testing.T) {
+func TestFailedRegister(t *testing.T) {
defer func() {
- connector.LoadRedisDatabase().FlushAll()
+ connector.LoadRedisCache().FlushAll()
}()
- permSvc := rbacService.NewPermissionService()
- roleSvc := rbacService.NewRoleService(permSvc)
+ permSvc := permService.NewPermissionService()
+ roleSvc := roleService.NewRoleService(permSvc)
svc := NewUserService(roleSvc)
c := helper.NewFiberCtx()
ctx := c.Context()
if svc == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
userRepo := repository.NewUserRepository()
if userRepo == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
modelUserRegis := model.UserRegister{
@@ -316,7 +314,7 @@ func Test_FailedRegister(t *testing.T) {
Email: "wrongEmail",
})
if verifErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
fiberErr, ok := verifErr.(*fiber.Error)
if ok {
@@ -330,7 +328,7 @@ func Test_FailedRegister(t *testing.T) {
Email: "wrongEmail",
})
if deleteUserErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
fiberErr, ok = deleteUserErr.(*fiber.Error)
if ok {
@@ -344,47 +342,47 @@ func Test_FailedRegister(t *testing.T) {
IP: helper.RandomIPAddress(),
})
if loginErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
forgetErr := svc.ForgetPassword(ctx, model.UserForgetPassword{Email: "wrong_email@gost.project"})
if forgetErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
verifyErr := svc.ResetPassword(ctx, model.UserResetPassword{Code: "wrong-code"})
if verifyErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
updatePasswdErr := svc.UpdatePassword(ctx, model.UserPasswordUpdate{ID: -1})
if updatePasswdErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
_, getErr := svc.MyProfile(ctx, -10)
if getErr == nil {
- t.Error("should error")
+ t.Error(constants.ShouldErr)
}
}
-func Test_Banned_IP_Address(t *testing.T) {
+func TestBannedIPAddress(t *testing.T) {
defer func() {
- connector.LoadRedisDatabase().FlushAll()
+ connector.LoadRedisCache().FlushAll()
}()
- permSvc := rbacService.NewPermissionService()
- roleSvc := rbacService.NewRoleService(permSvc)
+ permSvc := permService.NewPermissionService()
+ roleSvc := roleService.NewRoleService(permSvc)
svc := NewUserService(roleSvc)
c := helper.NewFiberCtx()
ctx := c.Context()
if svc == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
for i := 1; i <= 15; i++ {
counter, err := svc.FailedLoginCounter(helper.RandomIPAddress(), true)
if err != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
if i >= 4 {
if counter == i {
diff --git a/service/user_management/user_management_service.go b/service/user_management/user_management_service.go
index fd8f7aa..e70ae6d 100644
--- a/service/user_management/user_management_service.go
+++ b/service/user_management/user_management_service.go
@@ -11,23 +11,35 @@ import (
"sync"
"github.com/gofiber/fiber/v2"
- "golang.org/x/text/cases"
- "golang.org/x/text/language"
"gorm.io/gorm"
"github.com/Lukmanern/gost/domain/base"
"github.com/Lukmanern/gost/domain/entity"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/hash"
+ "github.com/Lukmanern/gost/internal/helper"
repository "github.com/Lukmanern/gost/repository/user"
)
type UserManagementService interface {
+
+ // Create func create one user.
Create(ctx context.Context, user model.UserCreate) (id int, err error)
+
+ // GetByID func get one user by ID.
GetByID(ctx context.Context, id int) (user *model.UserResponse, err error)
+
+ // GetByEmail func get one user by Email.
GetByEmail(ctx context.Context, email string) (user *model.UserResponse, err error)
+
+ // GetAll func get some users
GetAll(ctx context.Context, filter base.RequestGetAll) (users []model.UserResponse, total int, err error)
+
+ // Update func update one user data.
Update(ctx context.Context, user model.UserProfileUpdate) (err error)
+
+ // Delete func delete one user.
Delete(ctx context.Context, id int) (err error)
}
@@ -50,7 +62,7 @@ func NewUserManagementService() UserManagementService {
return userManagementService
}
-func (svc UserManagementServiceImpl) Create(ctx context.Context, user model.UserCreate) (id int, err error) {
+func (svc *UserManagementServiceImpl) Create(ctx context.Context, user model.UserCreate) (id int, err error) {
userCheck, getErr := svc.GetByEmail(ctx, user.Email)
if getErr == nil || userCheck != nil {
return 0, fiber.NewError(fiber.StatusBadRequest, "email has been used")
@@ -63,11 +75,11 @@ func (svc UserManagementServiceImpl) Create(ctx context.Context, user model.User
}
userEntity := entity.User{
- Name: cases.Title(language.Und).String(user.Name),
+ Name: helper.ToTitle(user.Name),
Email: user.Email,
Password: passwordHashed,
}
- userEntity.SetCreateTimes()
+ userEntity.SetCreateTime()
roleID := entity.USER
if user.IsAdmin {
@@ -81,11 +93,11 @@ func (svc UserManagementServiceImpl) Create(ctx context.Context, user model.User
return id, nil
}
-func (svc UserManagementServiceImpl) GetByID(ctx context.Context, id int) (user *model.UserResponse, err error) {
+func (svc *UserManagementServiceImpl) GetByID(ctx context.Context, id int) (user *model.UserResponse, err error) {
userEntity, err := svc.repository.GetByID(ctx, id)
if err != nil {
if err == gorm.ErrRecordNotFound {
- return nil, fiber.NewError(fiber.StatusNotFound, "data not found")
+ return nil, fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
return nil, err
}
@@ -97,12 +109,12 @@ func (svc UserManagementServiceImpl) GetByID(ctx context.Context, id int) (user
return user, nil
}
-func (svc UserManagementServiceImpl) GetByEmail(ctx context.Context, email string) (user *model.UserResponse, err error) {
+func (svc *UserManagementServiceImpl) GetByEmail(ctx context.Context, email string) (user *model.UserResponse, err error) {
email = strings.ToLower(email)
userEntity, getErr := svc.repository.GetByEmail(ctx, email)
if getErr != nil {
if getErr == gorm.ErrRecordNotFound {
- return nil, fiber.NewError(fiber.StatusNotFound, "data not found")
+ return nil, fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
return nil, getErr
}
@@ -114,7 +126,7 @@ func (svc UserManagementServiceImpl) GetByEmail(ctx context.Context, email strin
return user, nil
}
-func (svc UserManagementServiceImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (users []model.UserResponse, total int, err error) {
+func (svc *UserManagementServiceImpl) GetAll(ctx context.Context, filter base.RequestGetAll) (users []model.UserResponse, total int, err error) {
userEntities, total, err := svc.repository.GetAll(ctx, filter)
if err != nil {
return nil, 0, err
@@ -132,21 +144,21 @@ func (svc UserManagementServiceImpl) GetAll(ctx context.Context, filter base.Req
return users, total, nil
}
-func (svc UserManagementServiceImpl) Update(ctx context.Context, user model.UserProfileUpdate) (err error) {
+func (svc *UserManagementServiceImpl) Update(ctx context.Context, user model.UserProfileUpdate) (err error) {
getUser, getErr := svc.repository.GetByID(ctx, user.ID)
if getErr != nil {
if getErr == gorm.ErrRecordNotFound {
- return fiber.NewError(fiber.StatusNotFound, "data not found")
+ return fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
return getErr
}
if getUser == nil {
- return fiber.NewError(fiber.StatusNotFound, "data not found")
+ return fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
userEntity := entity.User{
ID: user.ID,
- Name: cases.Title(language.Und).String(user.Name),
+ Name: helper.ToTitle(user.Name),
// ...
// add more fields
}
@@ -159,16 +171,16 @@ func (svc UserManagementServiceImpl) Update(ctx context.Context, user model.User
return nil
}
-func (svc UserManagementServiceImpl) Delete(ctx context.Context, id int) (err error) {
+func (svc *UserManagementServiceImpl) Delete(ctx context.Context, id int) (err error) {
getUser, getErr := svc.repository.GetByID(ctx, id)
if getErr != nil {
if getErr == gorm.ErrRecordNotFound {
- return fiber.NewError(fiber.StatusNotFound, "data not found")
+ return fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
return getErr
}
if getUser == nil {
- return fiber.NewError(fiber.StatusNotFound, "data not found")
+ return fiber.NewError(fiber.StatusNotFound, constants.NotFound)
}
err = svc.repository.Delete(ctx, id)
diff --git a/service/user_management/user_management_service_test.go b/service/user_management/user_management_service_test.go
index 29bb429..71b601b 100644
--- a/service/user_management/user_management_service_test.go
+++ b/service/user_management/user_management_service_test.go
@@ -10,6 +10,7 @@ import (
"github.com/Lukmanern/gost/database/connector"
"github.com/Lukmanern/gost/domain/base"
"github.com/Lukmanern/gost/domain/model"
+ "github.com/Lukmanern/gost/internal/constants"
"github.com/Lukmanern/gost/internal/env"
"github.com/Lukmanern/gost/internal/helper"
"github.com/gofiber/fiber/v2"
@@ -20,13 +21,13 @@ func init() {
env.ReadConfig("./../../.env")
connector.LoadDatabase()
- connector.LoadRedisDatabase()
+ connector.LoadRedisCache()
}
func TestNewUserManagementService(t *testing.T) {
svc := NewUserManagementService()
if svc == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
}
@@ -39,12 +40,12 @@ func TestNewUserManagementService(t *testing.T) {
// -> get by id (checking)
// -> get by email (checking)
-func TestSuccessCRUD(t *testing.T) {
+func TestSuccessCrud(t *testing.T) {
c := helper.NewFiberCtx()
ctx := c.Context()
svc := NewUserManagementService()
if svc == nil || ctx == nil {
- t.Error("should not nil")
+ t.Error(constants.ShouldNotNil)
}
userModel := model.UserCreate{
@@ -61,8 +62,8 @@ func TestSuccessCRUD(t *testing.T) {
svc.Delete(ctx, userID)
}()
- userByID, getByIdErr := svc.GetByID(ctx, userID)
- if getByIdErr != nil || userByID == nil {
+ userByID, getByIDErr := svc.GetByID(ctx, userID)
+ if getByIDErr != nil || userByID == nil {
t.Error("should not error or user should not nil")
}
if userByID.Name != userModel.Name || userByID.Email != userModel.Email {
@@ -88,14 +89,14 @@ func TestSuccessCRUD(t *testing.T) {
}
updateErr := svc.Update(ctx, updateUserData)
if updateErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
// reset value
- getByIdErr = nil
+ getByIDErr = nil
userByID = nil
- userByID, getByIdErr = svc.GetByID(ctx, userID)
- if getByIdErr != nil || userByID == nil {
+ userByID, getByIDErr = svc.GetByID(ctx, userID)
+ if getByIDErr != nil || userByID == nil {
t.Error("should not error or user should not nil")
}
if userByID.Name != updateUserData.Name || userByID.Email != userModel.Email {
@@ -104,17 +105,17 @@ func TestSuccessCRUD(t *testing.T) {
deleteErr := svc.Delete(ctx, userID)
if deleteErr != nil {
- t.Error("should not error")
+ t.Error(constants.ShouldNotErr)
}
// reset value
- getByIdErr = nil
+ getByIDErr = nil
userByID = nil
- userByID, getByIdErr = svc.GetByID(ctx, userID)
- if getByIdErr == nil || userByID != nil {
+ userByID, getByIDErr = svc.GetByID(ctx, userID)
+ if getByIDErr == nil || userByID != nil {
t.Error("should error and user should nil")
}
- fiberErr, ok := getByIdErr.(*fiber.Error)
+ fiberErr, ok := getByIDErr.(*fiber.Error)
if ok {
if fiberErr.Code != fiber.StatusNotFound {
t.Error("should error 404")
diff --git a/tests/.gitignore b/tests/.gitignore
deleted file mode 100644
index e160c35..0000000
--- a/tests/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/log
-/logs
-*.log
-*.logs
\ No newline at end of file
diff --git a/tests/user_dev_test.go b/tests/user_dev_test.go
deleted file mode 100644
index d051bd1..0000000
--- a/tests/user_dev_test.go
+++ /dev/null
@@ -1,186 +0,0 @@
-// Don't run test per file without -p 1
-// or simply run test per func or run
-// project test using make test command
-// check Makefile file
-package test
-
-import (
- "encoding/json"
- "log"
- "net/http"
- "reflect"
- "strings"
- "testing"
- "time"
-
- "github.com/Lukmanern/gost/application"
- "github.com/Lukmanern/gost/database/connector"
- "github.com/Lukmanern/gost/domain/model"
- "github.com/Lukmanern/gost/internal/env"
- "github.com/Lukmanern/gost/internal/helper"
- "github.com/Lukmanern/gost/internal/response"
- "github.com/gofiber/fiber/v2"
- "golang.org/x/text/cases"
- "golang.org/x/text/language"
-
- controller "github.com/Lukmanern/gost/controller/user_management"
- service "github.com/Lukmanern/gost/service/user_management"
-)
-
-var (
- userDevService service.UserManagementService
- userDevController controller.UserManagementController
- appUrl string
-)
-
-func init() {
- env.ReadConfig("./../.env")
- config := env.Configuration()
- appUrl = config.AppUrl
- dbURI := config.GetDatabaseURI()
- privKey := config.GetPrivateKey()
- pubKey := config.GetPublicKey()
- if dbURI == "" || privKey == nil || pubKey == nil {
- log.Fatal("Database URI or keys aren't valid")
- }
-
- connector.LoadDatabase()
- r := connector.LoadRedisDatabase()
- r.FlushAll() // clear all key:value in redis
-
- userDevService = service.NewUserManagementService()
- userDevController = controller.NewUserManagementController(userDevService)
-}
-
-func Test_Create(t *testing.T) {
- go application.RunApp()
- time.Sleep(4 * time.Second)
-
- ctr := userDevController
- if ctr == nil {
- t.Error("should not nil")
- }
- c := helper.NewFiberCtx()
- if ctr == nil || c == nil {
- t.Error("should not error")
- }
-
- ctx := c.Context()
- if ctx == nil {
- t.Error("should not nil")
- }
- modelUserCreate := model.UserCreate{
- Name: helper.RandomString(10),
- Email: helper.RandomEmail(),
- Password: helper.RandomString(11),
- IsAdmin: true,
- }
- userID, createErr := userDevService.Create(ctx, modelUserCreate)
- if createErr != nil || userID < 1 {
- t.Error("should not error and got id more or equal than 1")
- }
- defer func() {
- userDevService.Delete(ctx, userID)
- }()
-
- testCases := []struct {
- CaseName string
- payload model.UserCreate
- resp response.Response
- wantHttpCode int
- }{
- {
- CaseName: "failed create: email has already used",
- payload: model.UserCreate{
- Name: helper.RandomString(10),
- Email: modelUserCreate.Email,
- Password: helper.RandomString(11),
- IsAdmin: true,
- },
- resp: response.Response{
- Data: nil,
- Success: false,
- Message: "",
- },
- wantHttpCode: http.StatusBadRequest,
- },
- {
- CaseName: "success create",
- payload: model.UserCreate{
- Name: helper.RandomString(10),
- Email: helper.RandomEmail(),
- Password: helper.RandomString(11),
- IsAdmin: true,
- },
- resp: response.Response{
- Data: nil,
- Success: true,
- Message: response.MessageSuccessCreated,
- },
- wantHttpCode: http.StatusCreated,
- },
- }
-
- for _, tc := range testCases {
- jsonObject, err := json.Marshal(&tc.payload)
- if err != nil {
- t.Error("should not error", err.Error())
- }
- req, httpReqErr := http.NewRequest(http.MethodPost, appUrl+"user-management/create", strings.NewReader(string(jsonObject)))
- if httpReqErr != nil {
- t.Fatal("should not nil")
- }
- req.Close = true
- req.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
- client := &http.Client{
- Transport: &http.Transport{},
- }
- resp, clientErr := client.Do(req)
- if clientErr != nil {
- t.Fatalf("HTTP request failed: %v", clientErr)
- }
- defer resp.Body.Close()
- if resp.StatusCode != tc.wantHttpCode {
- t.Error("should equal")
- }
-
- respModel := response.Response{}
- decodeErr := json.NewDecoder(resp.Body).Decode(&respModel)
- if decodeErr != nil {
- t.Error("should not error", decodeErr)
- }
-
- if tc.resp.Success != respModel.Success {
- t.Fatal("should equal")
- }
- if tc.resp.Message != "" {
- if tc.resp.Message != respModel.Message {
- t.Error("should equal")
- }
- }
- if tc.resp.Data != nil {
- if !reflect.DeepEqual(tc.resp.Data, respModel.Data) {
- t.Error("should equal")
- }
- }
- if respModel.Success {
- userByEmail, getErr := userDevService.GetByEmail(ctx, tc.payload.Email)
- if userByEmail.Name != cases.Title(language.Und).String(tc.payload.Name) {
- t.Error("should equal")
- }
- if getErr != nil {
- t.Error("should not error", getErr)
- }
- deleteErr := userDevService.Delete(ctx, userByEmail.ID)
- if deleteErr != nil {
- t.Error("should not error", deleteErr)
- }
- }
- }
-}
-
-// Create(c *fiber.Ctx) error
-// Get(c *fiber.Ctx) error
-// GetAll(c *fiber.Ctx) error
-// Update(c *fiber.Ctx) error
-// Delete(c *fiber.Ctx) error