Skip to content

Commit

Permalink
feat: add transfer api with test and custom validation
Browse files Browse the repository at this point in the history
  • Loading branch information
cukhoaimon committed Jan 31, 2024
1 parent f9d2b20 commit bae48bd
Show file tree
Hide file tree
Showing 11 changed files with 445 additions and 10 deletions.
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/SimpleBank.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions api/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"database/sql"
"errors"
"net/http"

db "github.com/cukhoaimon/SimpleBank/db/sqlc"
Expand All @@ -10,7 +11,7 @@ import (

type createAccountRequest struct {
Owner string `json:"owner" binding:"required"`
Currency string `json:"currency" binding:"required,oneof=USD EUR VND"`
Currency string `json:"currency" binding:"required,currency"`
}

func (server *Server) createAccount(ctx *gin.Context) {
Expand Down Expand Up @@ -50,7 +51,7 @@ func (server *Server) getAccount(ctx *gin.Context) {

account, err := server.store.GetAccount(ctx, req.ID)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
ctx.JSON(http.StatusNotFound, errorResponse(err))
return
}
Expand Down
13 changes: 13 additions & 0 deletions api/server.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package api

import (
"fmt"
db "github.com/cukhoaimon/SimpleBank/db/sqlc"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
)

type Server struct {
Expand All @@ -15,11 +18,21 @@ func NewServer(store db.Store) *Server {
server := &Server{store: store}
router := gin.Default()

if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
err := v.RegisterValidation("currency", validCurrency)
if err != nil {
fmt.Printf("err %v", err.Error())
return nil
}
}

// routing here
router.GET("/api/v1/account", server.listAccount)
router.POST("/api/v1/account", server.createAccount)
router.GET("/api/v1/account/:id", server.getAccount)

router.POST("/api/v1/transfer", server.createTransfer)

server.router = router
return server
}
Expand Down
69 changes: 69 additions & 0 deletions api/transfer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package api

import (
"database/sql"
"errors"
"fmt"
db "github.com/cukhoaimon/SimpleBank/db/sqlc"
"github.com/gin-gonic/gin"
"net/http"
)

type transferRequest struct {
FromAccountID int64 `json:"from_account_id" binding:"required,min=1"`
ToAccountID int64 `json:"to_account_id" binding:"required,min=1"`
Amount int64 `json:"amount" binding:"required,min=1"`
Currency string `json:"currency" binding:"required,currency"`
}

func (server *Server) createTransfer(ctx *gin.Context) {
var req transferRequest

if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err))
return
}

if !server.validAccount(ctx, req.FromAccountID, req.Currency) {
return
}

if !server.validAccount(ctx, req.ToAccountID, req.Currency) {
return
}

arg := db.TransferTxParams{
FromAccountID: req.FromAccountID,
ToAccountID: req.ToAccountID,
Amount: req.Amount,
}

account, err := server.store.TransferTxAccount(ctx, arg)
if err != nil {
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
return
}

ctx.JSON(http.StatusCreated, account)
}

func (server *Server) validAccount(ctx *gin.Context, accountID int64, currency string) bool {
account, err := server.store.GetAccount(ctx, accountID)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
ctx.JSON(http.StatusNotFound, errorResponse(err))
return false
}

ctx.JSON(http.StatusInternalServerError, errorResponse(err))
return false
}

if account.Currency != currency {
err := fmt.Errorf("account [%d] mismatch: %s vs %s ", accountID, account.Currency, currency)
ctx.JSON(http.StatusBadRequest, errorResponse(err))
return false
}

return true
}
Loading

0 comments on commit bae48bd

Please sign in to comment.