Gisle Aune
4 years ago
8 changed files with 427 additions and 11 deletions
-
8database/mongodb/changes.go
-
184database/postgres/changes.go
-
8database/postgres/db.go
-
18database/postgres/migrations/20210322193630_create_table_change.sql
-
138database/postgres/psqlcore/changes.sql.go
-
13database/postgres/psqlcore/models.go
-
21database/postgres/queries/changes.sql
-
48models/change.go
@ -0,0 +1,184 @@ |
|||||
|
package postgres |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"database/sql" |
||||
|
"encoding/json" |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"git.aiterp.net/rpdata/api/database/postgres/psqlcore" |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"strconv" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
type changeRepository struct { |
||||
|
insertWithIDs bool |
||||
|
db *sql.DB |
||||
|
} |
||||
|
|
||||
|
func (r *changeRepository) Find(ctx context.Context, id string) (*models.Change, error) { |
||||
|
row, err := psqlcore.New(r.db).SelectChangeByID(ctx, id) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return r.change(row) |
||||
|
} |
||||
|
|
||||
|
func (r *changeRepository) List(ctx context.Context, filter models.ChangeFilter) ([]*models.Change, error) { |
||||
|
params := psqlcore.SelectChangesParams{LimitSize: 100} |
||||
|
if filter.Keys != nil { |
||||
|
params.FilterKeys = true |
||||
|
keys := make([]string, len(filter.Keys)) |
||||
|
for i, ck := range filter.Keys { |
||||
|
keys[i] = ck.String() |
||||
|
} |
||||
|
params.Keys = keys |
||||
|
} |
||||
|
if filter.Author != nil { |
||||
|
params.FilterAuthor = true |
||||
|
params.Author = *filter.Author |
||||
|
} |
||||
|
if filter.EarliestDate != nil { |
||||
|
params.FilterEarliestDate = true |
||||
|
params.EarliestDate = filter.EarliestDate.In(time.UTC) |
||||
|
} |
||||
|
if filter.LatestDate != nil { |
||||
|
params.FilterLatestDate = true |
||||
|
params.LatestDate = filter.LatestDate.In(time.UTC) |
||||
|
} |
||||
|
if filter.Limit != nil { |
||||
|
if *filter.Limit <= 0 { |
||||
|
params.LimitSize = 10000 |
||||
|
} else { |
||||
|
params.LimitSize = int32(*filter.Limit) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
rows, err := psqlcore.New(r.db).SelectChanges(ctx, params) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return r.changes(rows) |
||||
|
} |
||||
|
|
||||
|
func (r *changeRepository) Insert(ctx context.Context, change models.Change) (*models.Change, error) { |
||||
|
tx, err := r.db.BeginTx(ctx, nil) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
defer func() { _ = tx.Rollback() }() |
||||
|
|
||||
|
q := psqlcore.New(tx) |
||||
|
|
||||
|
if !r.insertWithIDs || change.ID == "" { |
||||
|
next, err := q.IncrementCounter(ctx, "data_change_id") |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
change.ID = fmt.Sprintf("Change_%d", next) |
||||
|
} else { |
||||
|
if len(change.ID) < 8 { |
||||
|
return nil, errors.New("invalid change id") |
||||
|
} |
||||
|
|
||||
|
n, err := strconv.Atoi(change.ID[7:]) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = q.BumpCounter(ctx, psqlcore.BumpCounterParams{ID: "data_change_id", Value: int32(n)}) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
data, err := r.dataChange(change) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = q.InsertChange(ctx, psqlcore.InsertChangeParams(*data)) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return &change, tx.Commit() |
||||
|
} |
||||
|
|
||||
|
func (r *changeRepository) Remove(ctx context.Context, change models.Change) error { |
||||
|
return psqlcore.New(r.db).DeleteChange(ctx, change.ID) |
||||
|
} |
||||
|
|
||||
|
func (r *changeRepository) dataChange(change models.Change) (*psqlcore.DataChange, error) { |
||||
|
objectsJSON, err := json.Marshal(change.ChangeObjectSet) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
keys := make([]string, len(change.Keys)) |
||||
|
for i, ck := range change.Keys { |
||||
|
keys[i] = ck.String() |
||||
|
} |
||||
|
|
||||
|
return &psqlcore.DataChange{ |
||||
|
ID: change.ID, |
||||
|
Model: string(change.Model), |
||||
|
Op: change.Op, |
||||
|
Author: change.Author, |
||||
|
Listed: change.Listed, |
||||
|
Date: change.Date.In(time.UTC), |
||||
|
Keys: keys, |
||||
|
Objects: objectsJSON, |
||||
|
}, nil |
||||
|
} |
||||
|
|
||||
|
func (r *changeRepository) change(row psqlcore.DataChange) (*models.Change, error) { |
||||
|
objects := models.ChangeObjectSet{} |
||||
|
|
||||
|
err := json.Unmarshal(row.Objects, &objects) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
keys := make([]models.ChangeKey, len(row.Keys)) |
||||
|
for i := range keys { |
||||
|
err := keys[i].Decode(row.Keys[i]) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
model := models.ChangeModel(row.Model) |
||||
|
if !model.IsValid() { |
||||
|
return nil, errors.New("invalid model") |
||||
|
} |
||||
|
|
||||
|
return &models.Change{ |
||||
|
ID: row.ID, |
||||
|
Model: model, |
||||
|
Op: row.Op, |
||||
|
Author: row.Author, |
||||
|
Listed: row.Listed, |
||||
|
Date: row.Date, |
||||
|
Keys: keys, |
||||
|
ChangeObjectSet: objects, |
||||
|
}, nil |
||||
|
} |
||||
|
|
||||
|
func (r *changeRepository) changes(rows []psqlcore.DataChange) ([]*models.Change, error) { |
||||
|
results := make([]*models.Change, len(rows)) |
||||
|
for i, row := range rows { |
||||
|
change, err := r.change(row) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
results[i] = change |
||||
|
} |
||||
|
|
||||
|
return results, nil |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
-- +goose Up |
||||
|
-- +goose StatementBegin |
||||
|
CREATE TABLE data_change ( |
||||
|
id TEXT NOT NULL PRIMARY KEY, |
||||
|
model TEXT NOT NULL, |
||||
|
op TEXT NOT NULL, |
||||
|
author TEXT NOT NULL, |
||||
|
listed BOOLEAN NOT NULL, |
||||
|
date TIMESTAMP NOT NULL, |
||||
|
keys TEXT[] NOT NULL, |
||||
|
objects JSONB NOT NULL |
||||
|
); |
||||
|
-- +goose StatementEnd |
||||
|
|
||||
|
-- +goose Down |
||||
|
-- +goose StatementBegin |
||||
|
DROP TABLE IF EXISTS data_change; |
||||
|
-- +goose StatementEnd |
@ -0,0 +1,138 @@ |
|||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||
|
// source: changes.sql
|
||||
|
|
||||
|
package psqlcore |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"encoding/json" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/lib/pq" |
||||
|
) |
||||
|
|
||||
|
const deleteChange = `-- name: DeleteChange :exec |
||||
|
DELETE FROM data_change WHERE id = $1 |
||||
|
` |
||||
|
|
||||
|
func (q *Queries) DeleteChange(ctx context.Context, id string) error { |
||||
|
_, err := q.db.ExecContext(ctx, deleteChange, id) |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
const insertChange = `-- name: InsertChange :exec |
||||
|
INSERT INTO data_change (id, model, op, author, listed, date, keys, objects) |
||||
|
VALUES ( |
||||
|
$1::text, $2::text, $3::text, $4::text, |
||||
|
$5::boolean, $6::timestamp, $7::text[], |
||||
|
$8::jsonb |
||||
|
) |
||||
|
` |
||||
|
|
||||
|
type InsertChangeParams struct { |
||||
|
ID string `json:"id"` |
||||
|
Model string `json:"model"` |
||||
|
Op string `json:"op"` |
||||
|
Author string `json:"author"` |
||||
|
Listed bool `json:"listed"` |
||||
|
Date time.Time `json:"date"` |
||||
|
Keys []string `json:"keys"` |
||||
|
Objects json.RawMessage `json:"objects"` |
||||
|
} |
||||
|
|
||||
|
func (q *Queries) InsertChange(ctx context.Context, arg InsertChangeParams) error { |
||||
|
_, err := q.db.ExecContext(ctx, insertChange, |
||||
|
arg.ID, |
||||
|
arg.Model, |
||||
|
arg.Op, |
||||
|
arg.Author, |
||||
|
arg.Listed, |
||||
|
arg.Date, |
||||
|
pq.Array(arg.Keys), |
||||
|
arg.Objects, |
||||
|
) |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
const selectChangeByID = `-- name: SelectChangeByID :one |
||||
|
SELECT id, model, op, author, listed, date, keys, objects FROM data_change WHERE id = $1 LIMIT 1 |
||||
|
` |
||||
|
|
||||
|
func (q *Queries) SelectChangeByID(ctx context.Context, id string) (DataChange, error) { |
||||
|
row := q.db.QueryRowContext(ctx, selectChangeByID, id) |
||||
|
var i DataChange |
||||
|
err := row.Scan( |
||||
|
&i.ID, |
||||
|
&i.Model, |
||||
|
&i.Op, |
||||
|
&i.Author, |
||||
|
&i.Listed, |
||||
|
&i.Date, |
||||
|
pq.Array(&i.Keys), |
||||
|
&i.Objects, |
||||
|
) |
||||
|
return i, err |
||||
|
} |
||||
|
|
||||
|
const selectChanges = `-- name: SelectChanges :many |
||||
|
SELECT id, model, op, author, listed, date, keys, objects FROM data_change |
||||
|
WHERE ($1::bool = false OR keys && ($2::text[])) |
||||
|
AND ($3::bool = false OR date >= $4::timestamp) |
||||
|
AND ($5::bool = false OR date <= $6::timestamp) |
||||
|
AND ($7::bool = false OR author = $8::text) |
||||
|
LIMIT $9::int |
||||
|
` |
||||
|
|
||||
|
type SelectChangesParams struct { |
||||
|
FilterKeys bool `json:"filter_keys"` |
||||
|
Keys []string `json:"keys"` |
||||
|
FilterEarliestDate bool `json:"filter_earliest_date"` |
||||
|
EarliestDate time.Time `json:"earliest_date"` |
||||
|
FilterLatestDate bool `json:"filter_latest_date"` |
||||
|
LatestDate time.Time `json:"latest_date"` |
||||
|
FilterAuthor bool `json:"filter_author"` |
||||
|
Author string `json:"author"` |
||||
|
LimitSize int32 `json:"limit_size"` |
||||
|
} |
||||
|
|
||||
|
func (q *Queries) SelectChanges(ctx context.Context, arg SelectChangesParams) ([]DataChange, error) { |
||||
|
rows, err := q.db.QueryContext(ctx, selectChanges, |
||||
|
arg.FilterKeys, |
||||
|
pq.Array(arg.Keys), |
||||
|
arg.FilterEarliestDate, |
||||
|
arg.EarliestDate, |
||||
|
arg.FilterLatestDate, |
||||
|
arg.LatestDate, |
||||
|
arg.FilterAuthor, |
||||
|
arg.Author, |
||||
|
arg.LimitSize, |
||||
|
) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
defer rows.Close() |
||||
|
items := []DataChange{} |
||||
|
for rows.Next() { |
||||
|
var i DataChange |
||||
|
if err := rows.Scan( |
||||
|
&i.ID, |
||||
|
&i.Model, |
||||
|
&i.Op, |
||||
|
&i.Author, |
||||
|
&i.Listed, |
||||
|
&i.Date, |
||||
|
pq.Array(&i.Keys), |
||||
|
&i.Objects, |
||||
|
); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
items = append(items, i) |
||||
|
} |
||||
|
if err := rows.Close(); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
if err := rows.Err(); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return items, nil |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
-- name: SelectChangeByID :one |
||||
|
SELECT * FROM data_change WHERE id = $1 LIMIT 1; |
||||
|
|
||||
|
-- name: SelectChanges :many |
||||
|
SELECT * FROM data_change |
||||
|
WHERE (sqlc.arg(filter_keys)::bool = false OR keys && (sqlc.arg(keys)::text[])) |
||||
|
AND (sqlc.arg(filter_earliest_date)::bool = false OR date >= sqlc.arg(earliest_date)::timestamp) |
||||
|
AND (sqlc.arg(filter_latest_date)::bool = false OR date <= sqlc.arg(latest_date)::timestamp) |
||||
|
AND (sqlc.arg(filter_author)::bool = false OR author = sqlc.arg(author)::text) |
||||
|
LIMIT sqlc.arg(limit_size)::int; |
||||
|
|
||||
|
-- name: InsertChange :exec |
||||
|
INSERT INTO data_change (id, model, op, author, listed, date, keys, objects) |
||||
|
VALUES ( |
||||
|
sqlc.arg(id)::text, sqlc.arg(model)::text, sqlc.arg(op)::text, sqlc.arg(author)::text, |
||||
|
sqlc.arg(listed)::boolean, sqlc.arg(date)::timestamp, sqlc.arg(keys)::text[], |
||||
|
sqlc.arg(objects)::jsonb |
||||
|
); |
||||
|
|
||||
|
-- name: DeleteChange :exec |
||||
|
DELETE FROM data_change WHERE id = $1; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue