diff --git a/link/repository.go b/link/repository.go index e41cdef..1c780ef 100644 --- a/link/repository.go +++ b/link/repository.go @@ -7,11 +7,13 @@ import ( "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" "net/url" + "time" ) type Repository interface { Save(link *Link) error FindById(id string) (*Link, error) + FindAll(limit int, offset int) ([]Link, error) DeleteById(id string) error } @@ -20,7 +22,9 @@ type PgRepository struct { } func (r *PgRepository) Save(link *Link) error { - ctx := context.Background() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + tx, err := r.pool.Begin(ctx) if err != nil { return err @@ -45,7 +49,9 @@ func (r *PgRepository) Save(link *Link) error { } func (r *PgRepository) FindById(id string) (*Link, error) { - ctx := context.Background() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + tx, err := r.pool.Begin(ctx) if err != nil { return nil, err @@ -74,8 +80,63 @@ func (r *PgRepository) FindById(id string) (*Link, error) { return entity, nil } +func (r *PgRepository) FindAll(limit int, offset int) ([]Link, error) { + if limit < 0 { + return nil, errors.New("limit can't be negative") + } + if offset < 0 { + return nil, errors.New("offset can't be negative") + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + tx, err := r.pool.Begin(ctx) + if err != nil { + return nil, err + } + + sql := ` + SELECT id, name, redirect_url, creation_time + FROM links + LIMIT $1 + OFFSET $2 + ` + + rows, err := tx.Query(ctx, sql, limit, offset) + if err != nil { + _ = tx.Rollback(ctx) + return nil, err + } + defer rows.Close() + + links := make([]Link, 0) + for rows.Next() { + link, err := mapRowToEntity(rows) + if err != nil { + _ = tx.Rollback(ctx) + return nil, err + } + + links = append(links, *link) + } + + if err = rows.Err(); err != nil { + _ = tx.Rollback(ctx) + return nil, err + } + + if err = tx.Commit(ctx); err != nil { + return nil, err + } + + return links, nil +} + func (r *PgRepository) DeleteById(id string) error { - ctx := context.Background() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + tx, err := r.pool.Begin(ctx) if err != nil { return err @@ -98,13 +159,22 @@ func (r *PgRepository) DeleteById(id string) error { return nil } -func mapRowToEntity(r pgx.Row) (*Link, error) { +func mapRowToEntity(r interface{}) (*Link, error) { var entity Link var urlStr string var t pgtype.Timestamp - if err := r.Scan(&entity.Id, &entity.Name, &urlStr, &t); err != nil { - return nil, err + switch v := r.(type) { + case pgx.Row: + if err := v.Scan(&entity.Id, &entity.Name, &urlStr, &t); err != nil { + return nil, err + } + case pgx.Rows: + if err := v.Scan(&entity.Id, &entity.Name, &urlStr, &t); err != nil { + return nil, err + } + default: + return nil, errors.New("unsupported type") } u, err := url.Parse(urlStr)