Skip to content
This repository has been archived by the owner on Oct 17, 2024. It is now read-only.

Commit

Permalink
Refactor and optimization
Browse files Browse the repository at this point in the history
- Manual SQL gluing replaced by squirrel
  • Loading branch information
v1adhope committed Apr 22, 2023
1 parent 3ba4837 commit 71c017d
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 128 deletions.
Binary file modified .bin/app
Binary file not shown.
6 changes: 1 addition & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@

default: build

mod:
build:
go mod tidy -v

verify:
go mod verify

build: mod verify
go build -o .bin/app -race ./cmd/app

stop:
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/v1adhope/crypto-diary
go 1.20

require (
github.com/Masterminds/squirrel v1.5.4
github.com/gin-gonic/gin v1.8.2
github.com/golang-jwt/jwt/v4 v4.4.3
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa
Expand All @@ -17,6 +18,8 @@ require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
)

Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/bsm/ginkgo/v2 v2.5.0 h1:aOAnND1T40wEdAtkGSkvSICWeQ8L3UASX7YVCqQx+eQ=
github.com/bsm/gomega v1.20.0 h1:JhAwLmtRzXFTx2AkALSLa8ijZafntmhSoU63Ok18Uq8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down Expand Up @@ -169,6 +171,10 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
Expand Down
42 changes: 5 additions & 37 deletions internal/usecase/repository/filter.go
Original file line number Diff line number Diff line change
@@ -1,47 +1,15 @@
package repository

import (
"fmt"
"strings"
"sync"

"github.com/v1adhope/crypto-diary/internal/entity"
)

var allowedFilters = map[string]string{
entity.FilterDate: "open_date",
entity.FilterPair: "pair",
entity.FilterStrategically: "strategically",
}

type filterBuilderDeps struct {
Query string
Filter entity.Filter
}

func BuildFilterString(deps filterBuilderDeps) (string, []string) {
var (
filterRaw strings.Builder
mu sync.RWMutex
)

fmt.Fprint(&filterRaw, deps.Query, " ")

argsCounter := strings.Count(deps.Query, "$") + 1

args := make([]string, 0)

for fieldKey, fieldVal := range deps.Filter.Fields {
mu.RLock()
if realFilterName, ok := allowedFilters[fieldKey]; ok {
fmt.Fprintf(&filterRaw, "AND %s = $%d ", realFilterName, argsCounter)
args = append(args, fieldVal)
argsCounter++
}
mu.RUnlock()
}

fmt.Fprintf(&filterRaw, "ORDER by position_id ASC LIMIT $%d", argsCounter)
var allowedFilters sync.Map

return filterRaw.String(), args
func init() {
allowedFilters.Store(entity.FilterDate, "open_date")
allowedFilters.Store(entity.FilterPair, "pair")
allowedFilters.Store(entity.FilterStrategically, "strategically")
}
154 changes: 76 additions & 78 deletions internal/usecase/repository/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"strings"

"github.com/jackc/pgx/v5"
"github.com/v1adhope/crypto-diary/internal/entity"
"github.com/v1adhope/crypto-diary/pkg/postgres"
)
Expand All @@ -23,26 +22,38 @@ func NewPosition(pg *postgres.Postgres) *PositionRepo {
}

func (pr *PositionRepo) Create(ctx context.Context, position *entity.Position) error {
q := `INSERT INTO positions(open_date, pair, reason, strategically, percentage_risk,
direction, deposit, open_price, stop_loss_price,
take_profit_price, close_price, user_id)
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
RETURNING position_id`

err := pr.Pool.QueryRow(ctx, q,
position.OpenDate,
position.Pair,
nullCheck(position.Reason),
position.Strategically,
position.Risk,
position.Direction,
position.Deposit,
position.OpenPrice,
position.StopLossPrice,
position.TakeProfitPrice,
nullCheck(position.ClosePrice),
position.UserID).
Scan(&position.ID)
sql, args, err := pr.Builder.Insert("positions").
Columns("open_date",
"pair",
"reason",
"strategically",
"percentage_risk",
"direction",
"deposit",
"open_price",
"stop_loss_price",
"take_profit_price",
"close_price",
"user_id").
Values(position.OpenDate,
position.Pair,
nullCheck(position.Reason),
position.Strategically,
position.Risk,
position.Direction,
position.Deposit,
position.OpenPrice,
position.StopLossPrice,
position.TakeProfitPrice,
nullCheck(position.ClosePrice),
position.UserID).
Suffix("RETURNING position_id").
ToSql()
if err != nil {
return fmt.Errorf("repository: Create position: Query builder: %s", err)
}

err = pr.Pool.QueryRow(ctx, sql, args...).Scan(&position.ID)
if err != nil {
return fmt.Errorf("repository: Create position: QueryRow: %s", err)
}
Expand All @@ -59,39 +70,25 @@ func nullCheck(s string) *string {
}

func (pr *PositionRepo) FindAll(ctx context.Context, userID string, filter entity.Filter) ([]entity.Position, error) {
q := `SELECT * FROM get_all_positions
WHERE user_id = $1 AND position_id > $2`

q, args := BuildFilterString(filterBuilderDeps{
Query: q,
Filter: filter,
})

var (
rows pgx.Rows
err error
)

const ( // Count of args in query
one = iota
two
three
)

switch len(args) - 1 {
default:
rows, err = pr.Pool.Query(ctx, q, userID, filter.PaginationCursor,
_defaultEntityCap)
case one:
rows, err = pr.Pool.Query(ctx, q, userID, filter.PaginationCursor,
args[one], _defaultEntityCap)
case two:
rows, err = pr.Pool.Query(ctx, q, userID, filter.PaginationCursor,
args[one], args[two], _defaultEntityCap)
case three:
rows, err = pr.Pool.Query(ctx, q, userID, filter.PaginationCursor,
args[one], args[two], args[three], _defaultEntityCap)

q := pr.Builder.Select("*").
From("get_all_positions").
Where("user_id = ? AND position_id > ?", userID, filter.PaginationCursor)

for fieldKey, fieldVal := range filter.Fields {
if realFilterName, ok := allowedFilters.Load(fieldKey); ok {
q = q.Where(fmt.Sprintf("%s = ?", realFilterName.(string)), fieldVal)
}
}

sql, args, err := q.OrderBy("position_id ASC").
Limit(_defaultEntityCap).
ToSql()
if err != nil {
return nil, fmt.Errorf("repository: FindAll position: Query builder: %s", err)
}

rows, err := pr.Pool.Query(ctx, sql, args...)
if err != nil {
return nil, fmt.Errorf("repository: FindAll position: Query: %s", err)
}
Expand Down Expand Up @@ -127,10 +124,14 @@ func (pr *PositionRepo) FindAll(ctx context.Context, userID string, filter entit
}

func (pr *PositionRepo) Delete(ctx context.Context, userID, positionID string) error {
q := `DELETE FROM positions
WHERE user_id = $1 AND position_id = $2`
sql, args, err := pr.Builder.Delete("positions").
Where("user_id = ? AND position_id = ?", userID, positionID).
ToSql()
if err != nil {
return fmt.Errorf("repository: Delete position: Query builder: %s", err)
}

commandTag, err := pr.Pool.Exec(ctx, q, userID, positionID)
commandTag, err := pr.Pool.Exec(ctx, sql, args...)
if err != nil {
return fmt.Errorf("repository: Delete positon: Exec: %s", err)
}
Expand All @@ -143,28 +144,25 @@ func (pr *PositionRepo) Delete(ctx context.Context, userID, positionID string) e
}

func (pr *PositionRepo) Replace(ctx context.Context, position *entity.Position) error {
q := `UPDATE positions
SET open_date = $1, pair = $2, reason = $3, strategically = $4,
percentage_risk = $5, direction = $6, deposit = $7, open_price = $8,
stop_loss_price=$9, take_profit_price=$10, close_price=$11
WHERE position_id = $12 AND user_id = $13`

commandTag, err := pr.Pool.Exec(ctx, q,
position.OpenDate,
position.Pair,
position.Reason,
position.Strategically,
position.Risk,
position.Direction,
position.Deposit,
position.OpenPrice,
position.StopLossPrice,
position.TakeProfitPrice,
position.ClosePrice,

position.ID,
position.UserID,
)
sql, args, err := pr.Builder.Update("positions").
Set("open_date", position.OpenDate).
Set("pair", position.Pair).
Set("reason", position.Reason).
Set("strategically", position.Strategically).
Set("percentage_risk", position.Risk).
Set("direction", position.Direction).
Set("deposit", position.Deposit).
Set("open_price", position.OpenPrice).
Set("stop_loss_price", position.StopLossPrice).
Set("take_profit_price", position.TakeProfitPrice).
Set("close_price", position.ClosePrice).
Where("user_id = ? AND position_id = ?", position.UserID, position.ID).
ToSql()
if err != nil {
return fmt.Errorf("repository: Replace position: Query builder: %s", err)
}

commandTag, err := pr.Pool.Exec(ctx, sql, args...)
if err != nil {
return fmt.Errorf("repository: Replace position: Exec: %w", err)
}
Expand Down
23 changes: 16 additions & 7 deletions internal/usecase/repository/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@ func NewUser(pg *postgres.Postgres) *UserRepo {
}

func (ur *UserRepo) Create(ctx context.Context, user entity.User) error {
q := `INSERT INTO users(email, password)
VALUES($1,$2)`
sql, args, err := ur.Builder.Insert("users").
Columns("email", "password").
Values(user.Email, user.Password).
ToSql()
if err != nil {
return fmt.Errorf("repository: Create user: Query builder: %s", err)
}

_, err := ur.Pool.Exec(ctx, q, user.Email, user.Password)
_, err = ur.Pool.Exec(ctx, sql, args...)
if err != nil {
var pgErr *pgconn.PgError

Expand All @@ -43,13 +48,17 @@ func (ur *UserRepo) Create(ctx context.Context, user entity.User) error {
}

func (ur *UserRepo) Get(ctx context.Context, email string) (*entity.User, error) {
q := `SELECT *
FROM get_user
WHERE email = $1`
sql, args, err := ur.Builder.Select("*").
From("get_user").
Where("email = ?", email).
ToSql()
if err != nil {
return nil, fmt.Errorf("repository: Create user: Query builder: %s", err)
}

u := &entity.User{}

err := ur.Pool.QueryRow(ctx, q, email).Scan(&u.ID, &u.Email, &u.Password)
err = ur.Pool.QueryRow(ctx, sql, args...).Scan(&u.ID, &u.Email, &u.Password)
if err != nil {
if err == pgx.ErrNoRows {
return nil, entity.ErrUserNotExists
Expand Down
6 changes: 5 additions & 1 deletion pkg/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"time"

"github.com/Masterminds/squirrel"
"github.com/jackc/pgx/v5/pgxpool"
)

Expand All @@ -20,14 +21,17 @@ type Config struct {
}

type Postgres struct {
Pool *pgxpool.Pool
Pool *pgxpool.Pool
Builder squirrel.StatementBuilderType
}

func NewClient(ctx context.Context, cfg *Config) (*Postgres, error) {
pg := &Postgres{}

dsn := fmt.Sprintf("postgresql://%s:%s@%s/%s", cfg.Username, cfg.Password, cfg.Socket, cfg.Database)

pg.Builder = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar)

poolCfg, err := pgxpool.ParseConfig(dsn)
if err != nil {
return nil, fmt.Errorf("postgres: parse config failed: %s", err)
Expand Down

0 comments on commit 71c017d

Please sign in to comment.