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