Skip to content

Commit

Permalink
authentication & transfer api, new unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
hafizxd committed Feb 18, 2024
1 parent 811215e commit 184c987
Show file tree
Hide file tree
Showing 44 changed files with 2,529 additions and 27 deletions.
7 changes: 7 additions & 0 deletions .idea/sqldialects.xml

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

14 changes: 13 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,26 @@ dropdb:
migrateup:
migrate -path db/migration -database "postgresql://root:secret@localhost:5432/simple_bank?sslmode=disable" -verbose up

migrateup1:
migrate -path db/migration -database "postgresql://root:secret@localhost:5432/simple_bank?sslmode=disable" -verbose up 1

migratedown:
migrate -path db/migration -database "postgresql://root:secret@localhost:5432/simple_bank?sslmode=disable" -verbose down

migratedown1:
migrate -path db/migration -database "postgresql://root:secret@localhost:5432/simple_bank?sslmode=disable" -verbose down 1

sqlc:
sqlc generate

test:
go test -v -cover ./...

server:
go run main.go

mock:
mockgen -package mockdb -destination db/mock/store.go github.com/hafizxd/simple_bank/db/sqlc Store

.PHONY:
postgres createdb dropdb migrateup migratedown
postgres postgresstart createdb dropdb migrateup migratedown sqlc test server mock
176 changes: 176 additions & 0 deletions api/account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package api

import (
"database/sql"
"errors"
"github.com/gin-gonic/gin"
db "github.com/hafizxd/simple_bank/db/sqlc"
"github.com/hafizxd/simple_bank/token"
"github.com/lib/pq"
"net/http"
)

type createAccountRequest struct {
Currency string `json:"currency" binding:"required,currency"`
}

func (server *Server) createAccount(ctx *gin.Context) {
var req createAccountRequest

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

authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
arg := db.CreateAccountParams{
Owner: authPayload.Username,
Currency: req.Currency,
Balance: 0,
}

account, err := server.store.CreateAccount(ctx, arg)
if err != nil {
if pqErr, ok := err.(*pq.Error); ok {
switch pqErr.Code.Name() {
case "foreign_key_violation", "unique_violation":
ctx.JSON(http.StatusForbidden, errorResponse(err))
return
}
}

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

ctx.JSON(http.StatusOK, account)
}

type GetAccountRequest struct {
ID int64 `uri:"id" binding:"required,min=1"`
}

func (server *Server) getAccount(ctx *gin.Context) {
var req GetAccountRequest

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

account, err := server.store.GetAccount(ctx, req.ID)
if err != nil {
if err == sql.ErrNoRows {
ctx.JSON(http.StatusNotFound, errorResponse(err))
return
}

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

authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
if account.Owner != authPayload.Username {
err := errors.New("account doesn't belong to authenticated users")
ctx.JSON(http.StatusUnauthorized, err)
return
}
ctx.JSON(http.StatusOK, account)
}

type ListAccountRequest struct {
PageID int32 `form:"page_id" binding:"required,min=1"`
PageSize int32 `form:"page_size" binding:"required,min=5,max=10"`
}

func (server *Server) listAccount(ctx *gin.Context) {
var req ListAccountRequest

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

authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
arg := db.ListAccountParams{
Owner: authPayload.Username,
Offset: (req.PageID * req.PageSize) - req.PageSize,
Limit: req.PageSize,
}

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

ctx.JSON(http.StatusOK, accounts)
}

type UpdateAccountUri struct {
ID int64 `uri:"id" binding:"required,min=1"`
}

type UpdateAccountRequest struct {
Balance int64 `json:"balance" binding:"required,min=1"`
}

func (server *Server) updateAccount(ctx *gin.Context) {
var uri UpdateAccountUri
var req UpdateAccountRequest

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

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

arg := db.UpdateAccountParams{
ID: uri.ID,
Balance: req.Balance,
}

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

ctx.JSON(http.StatusOK, account)
}

type DeleteAccountRequest struct {
ID int64 `uri:"id" binding:"required,min=1"`
}

func (server *Server) deleteAccount(ctx *gin.Context) {
var req DeleteAccountRequest

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

account, err := server.store.GetAccount(ctx, req.ID)
if err != nil {
if err == sql.ErrNoRows {
ctx.JSON(http.StatusNotFound, errorResponse(err))
return
}

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

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

ctx.JSON(http.StatusOK, &gin.H{"message": "Account deleted successfully"})
}
Loading

0 comments on commit 184c987

Please sign in to comment.