-
Notifications
You must be signed in to change notification settings - Fork 0
/
car.go
163 lines (132 loc) · 4.36 KB
/
car.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package postgres
import (
"context"
"database/sql"
"errors"
"fmt"
"sync"
"github.com/4aykovski/effective_mobile_test_task/internal/model"
"github.com/4aykovski/effective_mobile_test_task/internal/repository"
"github.com/4aykovski/effective_mobile_test_task/pkg/api/filter"
"github.com/4aykovski/effective_mobile_test_task/pkg/database/postgres"
"github.com/lib/pq"
)
type CarRepository struct {
postgres *postgres.Postgres
}
func NewCarRepository(postgres *postgres.Postgres) *CarRepository {
return &CarRepository{
postgres: postgres,
}
}
func (r *CarRepository) InsertCar(ctx context.Context, car model.Car) error {
stmt, err := r.postgres.Prepare(
`INSERT INTO cars (registration_number, mark, model, year, owner_name, owner_surname)
VALUES ($1, $2, $3, $4, $5, $6)`,
)
if err != nil {
return fmt.Errorf("failed to prepare add new car statement: %w", err)
}
defer stmt.Close()
var mu sync.Mutex
mu.Lock()
_, err = stmt.ExecContext(ctx, car.RegistrationNumber, car.Mark, car.Model, car.Year, car.OwnerName, car.OwnerSurname)
if err != nil {
var pqErr *pq.Error
if errors.As(err, &pqErr) {
if pqErr.Code.Name() == "unique_violation" {
return repository.ErrCarExists
}
}
return fmt.Errorf("failed to execute add new car statement: %w", err)
}
mu.Unlock()
return nil
}
func (r *CarRepository) DeleteCar(ctx context.Context, regNumber string) error {
stmt, err := r.postgres.Prepare("DELETE FROM cars WHERE registration_number = $1")
if err != nil {
return fmt.Errorf("failed to prepare delete car statement: %w", err)
}
defer stmt.Close()
res, err := stmt.ExecContext(ctx, regNumber)
if err != nil {
return fmt.Errorf("failed to execute delete car statement: %w", err)
}
deleted, err := res.RowsAffected()
if err != nil {
return fmt.Errorf("failed to get rows affected: %w", err)
}
if deleted == 0 {
return repository.ErrCarNotFound
}
return nil
}
func (r *CarRepository) UpdateCar(ctx context.Context, car model.Car) error {
stmt, err := r.postgres.Prepare(
`UPDATE cars
SET mark = $1, model = $2, year = $3, owner_name = $4, owner_surname = $5
WHERE registration_number = $6`,
)
if err != nil {
return fmt.Errorf("failed to prepare update car statement: %w", err)
}
defer stmt.Close()
res, err := stmt.ExecContext(ctx, car.Mark, car.Model, car.Year, car.OwnerName, car.OwnerSurname, car.RegistrationNumber)
if err != nil {
return fmt.Errorf("failed to execute update car statement: %w", err)
}
updated, err := res.RowsAffected()
if err != nil {
return fmt.Errorf("failed to get rows affected: %w", err)
}
if updated == 0 {
return repository.ErrCarNotFound
}
return nil
}
func (r *CarRepository) GetCar(ctx context.Context, regNumber string) (model.Car, error) {
stmt, err := r.postgres.Prepare("SELECT * FROM cars WHERE registration_number = $1")
if err != nil {
return model.Car{}, fmt.Errorf("failed to prepare get car statement: %w", err)
}
defer stmt.Close()
var car model.Car
err = stmt.QueryRowContext(ctx, regNumber).Scan(&car.RegistrationNumber, &car.Mark, &car.Model, &car.Year, &car.OwnerName, &car.OwnerSurname)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return model.Car{}, repository.ErrCarNotFound
}
return model.Car{}, fmt.Errorf("failed to execute get car statement: %w", err)
}
return car, nil
}
func (r *CarRepository) GetCars(ctx context.Context, limit, offset int, filterOptions filter.Options) ([]model.Car, error) {
sqlStmt := `SELECT * FROM cars`
var args []interface{}
sqlStmt, args = postgres.AddFilterToStmt(sqlStmt, args, filterOptions, model.Car{})
sqlStmt += ` ORDER BY registration_number`
sqlStmt, args = postgres.AddPaginationToStmt(sqlStmt, args, limit, offset)
stmt, err := r.postgres.Prepare(sqlStmt)
if err != nil {
return nil, fmt.Errorf("failed to prepare get cars statement: %w", err)
}
defer stmt.Close()
rows, err := stmt.QueryContext(ctx, args...)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, repository.ErrCarsNotFound
}
return nil, fmt.Errorf("failed to execute get cars statement: %w", err)
}
defer rows.Close()
var cars []model.Car
for rows.Next() {
var car model.Car
if err := rows.Scan(&car.RegistrationNumber, &car.Mark, &car.Model, &car.Year, &car.OwnerName, &car.OwnerSurname); err != nil {
return nil, fmt.Errorf("failed to scan row: %w", err)
}
cars = append(cars, car)
}
return cars, nil
}