Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
3654383ad2 |
@@ -61,10 +61,8 @@ type Repo[T any] interface {
|
|||||||
Read(ctx context.Context, id string) (*T, error)
|
Read(ctx context.Context, id string) (*T, error)
|
||||||
// Update updates object with id.
|
// Update updates object with id.
|
||||||
Update(ctx context.Context, id string, v *T) error
|
Update(ctx context.Context, id string, v *T) error
|
||||||
// Delete performs soft-delete (actually marks object as unavailable).
|
// Delete deletes object with id.
|
||||||
Delete(ctx context.Context, id string) error
|
Delete(ctx context.Context, id string) error
|
||||||
// Purge actually deletes database record with id.
|
|
||||||
Purge(ctx context.Context, id string) error
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -13,9 +13,9 @@ CREATE TABLE IF NOT EXISTS -- (
|
|||||||
id text PRIMARY KEY,
|
id text PRIMARY KEY,
|
||||||
created_at timestamp with time zone,
|
created_at timestamp with time zone,
|
||||||
updated_at timestamp with time zone,
|
updated_at timestamp with time zone,
|
||||||
deleted_at timestamp with time zone,
|
|
||||||
payload jsonb
|
payload jsonb
|
||||||
)`
|
)
|
||||||
|
`
|
||||||
|
|
||||||
func initDB(ctx context.Context, db *sql.DB, tablename string) error {
|
func initDB(ctx context.Context, db *sql.DB, tablename string) error {
|
||||||
if tablename == "" {
|
if tablename == "" {
|
||||||
|
26
repo.go
26
repo.go
@@ -16,17 +16,15 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Repo[T any] interface {
|
type Repo[T any] interface {
|
||||||
// Create inserts object into repository table. If id already exists
|
// Create saves object to the repository. If id already exists
|
||||||
// in the database it is an error.
|
// in the database it is an error.
|
||||||
Create(ctx context.Context, id string, v *T) error
|
Create(ctx context.Context, id string, v *T) error
|
||||||
// Read returns object with specified id or ErrNotFound
|
// Read returns object with specified id or ErrNotFound
|
||||||
Read(ctx context.Context, id string) (*T, error)
|
Read(ctx context.Context, id string) (*T, error)
|
||||||
// Update updates object with id.
|
// Update updates object with id.
|
||||||
Update(ctx context.Context, id string, v *T) error
|
Update(ctx context.Context, id string, v *T) error
|
||||||
// Delete performs soft-delete (actually marks object as unavailable).
|
// Delete deletes object from the database.
|
||||||
Delete(ctx context.Context, id string) error
|
Delete(ctx context.Context, id string) error
|
||||||
// Purge actually deletes database record with id.
|
|
||||||
Purge(ctx context.Context, id string) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenOrCreate accepts *sql.DB and tablename and tries to create the named table
|
// OpenOrCreate accepts *sql.DB and tablename and tries to create the named table
|
||||||
@@ -65,7 +63,7 @@ func (r *repo[T]) Create(ctx context.Context, id string, v *T) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *repo[T]) Read(ctx context.Context, id string) (*T, error) {
|
func (r *repo[T]) Read(ctx context.Context, id string) (*T, error) {
|
||||||
query := "SELECT payload FROM " + r.table + " WHERE id = $1 AND deleted_at is NULL"
|
query := "SELECT payload FROM " + r.table + " WHERE id = $1"
|
||||||
|
|
||||||
row := r.db.QueryRowContext(ctx, query, id)
|
row := r.db.QueryRowContext(ctx, query, id)
|
||||||
|
|
||||||
@@ -94,7 +92,7 @@ func (r *repo[T]) Update(ctx context.Context, id string, v *T) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := "UPDATE " + r.table + " SET updated_at = $1, payload = $2 WHERE id = $3 AND deleted_at IS NULL"
|
query := "UPDATE " + r.table + " SET updated_at = $1, payload = $2 WHERE id = $3"
|
||||||
|
|
||||||
if err := r.execContext(ctx, query, now, string(b), id); err != nil {
|
if err := r.execContext(ctx, query, now, string(b), id); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -103,21 +101,8 @@ func (r *repo[T]) Update(ctx context.Context, id string, v *T) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete performs soft-delete. It just marks the record as deleted and it will
|
// Delete deletes record with cpecified id.
|
||||||
// no longer be available to Read and Update methods.
|
|
||||||
func (r *repo[T]) Delete(ctx context.Context, id string) error {
|
func (r *repo[T]) Delete(ctx context.Context, id string) error {
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
query := "UPDATE " + r.table + " SET deleted_at = $1 WHERE id = $2 AND deleted_at IS NULL"
|
|
||||||
|
|
||||||
if err := r.execContext(ctx, query, now, id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *repo[T]) Purge(ctx context.Context, id string) error {
|
|
||||||
query := "DELETE FROM " + r.table + " WHERE id = $1"
|
query := "DELETE FROM " + r.table + " WHERE id = $1"
|
||||||
|
|
||||||
if err := r.execContext(ctx, query, id); err != nil {
|
if err := r.execContext(ctx, query, id); err != nil {
|
||||||
@@ -132,6 +117,7 @@ func (r *repo[T]) execContext(ctx context.Context, query string, args ...any) er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Join(ErrExecQuery, err)
|
return errors.Join(ErrExecQuery, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := res.RowsAffected()
|
affected, err := res.RowsAffected()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Join(ErrExecQuery, err)
|
return errors.Join(ErrExecQuery, err)
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
docker run --rm -d -e POSTGRES_PASSWORD=postgres --name test-postgres -p 5432:5432 postgres:15.6-bookworm
|
docker run --rm -d -e POSTGRES_PASSWORD=postgres --name test-postgres -p 5432:5432 postgres:15.6-bookworm
|
||||||
|
# wait for postgres to launch
|
||||||
sleep 10
|
sleep 10
|
||||||
PGX_DSN=postgres://postgres:postgres@127.0.0.1:5432 go test -v -cover
|
PGX_DSN=postgres://postgres:postgres@127.0.0.1:5432 go test -v -cover
|
||||||
docker stop test-postgres
|
docker stop test-postgres
|
||||||
|
@@ -3,7 +3,7 @@ module pkgtest
|
|||||||
go 1.24.2
|
go 1.24.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
code.uint32.ru/tiny/repo v0.0.0-20250504181319-b6dc6d3fce1a
|
code.uint32.ru/tiny/repo v1.0.0
|
||||||
github.com/jackc/pgx/v5 v5.7.4
|
github.com/jackc/pgx/v5 v5.7.4
|
||||||
github.com/mattn/go-sqlite3 v1.14.28
|
github.com/mattn/go-sqlite3 v1.14.28
|
||||||
)
|
)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
code.uint32.ru/tiny/repo v0.0.0-20250504181319-b6dc6d3fce1a h1:xlO71KbJ4fl3F05JN/I8rY3MjVF0CV3B1awa4TB6GN0=
|
code.uint32.ru/tiny/repo v1.0.0 h1:hWLL+atKth6XwKawQcnaBkQyuiGlS7+r4pqNhvUl4XE=
|
||||||
code.uint32.ru/tiny/repo v0.0.0-20250504181319-b6dc6d3fce1a/go.mod h1:yk97QS1fB9mdT/5iRN4ufo6IwZRDYHFl1kDtBSTpuk0=
|
code.uint32.ru/tiny/repo v1.0.0/go.mod h1:yk97QS1fB9mdT/5iRN4ufo6IwZRDYHFl1kDtBSTpuk0=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
6
test/go.work
Normal file
6
test/go.work
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
go 1.24.5
|
||||||
|
|
||||||
|
use (
|
||||||
|
.
|
||||||
|
..
|
||||||
|
)
|
15
test/go.work.sum
Normal file
15
test/go.work.sum
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||||
|
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||||
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||||
|
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
@@ -127,15 +127,6 @@ func dotest(ctx context.Context, db *sql.DB, t *testing.T) {
|
|||||||
t.Errorf("incorrect error on delete for non-existing id, want: %v, have: %v", repo.ErrNotFound, err)
|
t.Errorf("incorrect error on delete for non-existing id, want: %v, have: %v", repo.ErrNotFound, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.Purge(ctx, testID); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := r.Purge(ctx, testID); err == nil || !errors.Is(err, repo.ErrNotFound) {
|
|
||||||
// can only purge once
|
|
||||||
t.Errorf("incorrect error on delete for non-existing id, want: %v, have: %v", repo.ErrNotFound, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := r.Read(ctx, incorrectID); err == nil || !errors.Is(err, repo.ErrNotFound) {
|
if _, err := r.Read(ctx, incorrectID); err == nil || !errors.Is(err, repo.ErrNotFound) {
|
||||||
t.Errorf("incorrect error on read for non-existing id, want: %v, have: %v", repo.ErrNotFound, err)
|
t.Errorf("incorrect error on read for non-existing id, want: %v, have: %v", repo.ErrNotFound, err)
|
||||||
}
|
}
|
||||||
@@ -148,7 +139,4 @@ func dotest(ctx context.Context, db *sql.DB, t *testing.T) {
|
|||||||
t.Errorf("incorrect error on delete for non-existing id, want: %v, have: %v", repo.ErrNotFound, err)
|
t.Errorf("incorrect error on delete for non-existing id, want: %v, have: %v", repo.ErrNotFound, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.Purge(ctx, incorrectID); err == nil || !errors.Is(err, repo.ErrNotFound) {
|
|
||||||
t.Errorf("incorrect error on purge for non-existing id, want: %v, have: %v", repo.ErrNotFound, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user