package ledger | |
import ( | |
"encoding/json" | |
"time" | |
"github.com/jmoiron/sqlx" | |
"github.com/jmoiron/sqlx/types" | |
uuid "github.com/satori/go.uuid" | |
) | |
// Identifider is the interface that wraps methods used to identify items. | |
type Identifider interface { | |
IdentifyID() string | |
IdentifyType() string | |
} | |
// Applyer is the interface that wraps methods for applying ledger values to | |
// the underlying types they wrap. | |
type Applyer interface { | |
ApplyID(id string) | |
ApplyTime(created, modified time.Time) | |
} | |
// Record is a storable record that typically wraps a given blob of data. | |
// It maintains an unique identifier and a creation time. | |
type Record struct { | |
db *sqlx.DB | |
err error | |
DataType string `db:"datatype"` | |
Data types.JSONText `db:"data"` | |
Time time.Time `db:"time"` | |
ID string `db:"id"` | |
} | |
// Options describes options for Record. | |
type Options struct { | |
DB *sqlx.DB | |
} | |
// Schema describes the SQL table used to store records. | |
const Schema = ` | |
CREATE TABLE IF NOT EXISTS record ( | |
added_id serial PRIMARY KEY, | |
id uuid NOT NULL, | |
datatype varchar(32) NOT NULL, | |
data jsonb, | |
time timestamp NOT NULL DEFAULT current_timestamp | |
)` | |
// NewRecord returns a new Record that wraps data. | |
func NewRecord(data Identifider, options Options) *Record { | |
r := Record{ | |
db: options.DB, | |
ID: data.IdentifyID(), | |
DataType: data.IdentifyType(), | |
} | |
if r.ID == "" { | |
r.ID = uuid.NewV4().String() | |
} | |
r.Data, r.err = json.Marshal(data) | |
return &r | |
} | |
// Read returns an existing Record that matches id. | |
func Read(id string, options Options) *Record { | |
r := Record{ID: id, db: options.DB} | |
row := r.db.QueryRow(`SELECT id,datatype,data,time FROM record WHERE id = $1 ORDER BY time DESC LIMIT 1`, r.ID) | |
r.err = row.Scan(&r.ID, &r.DataType, &r.Data, &r.Time) | |
return &r | |
} | |
// Write stores a copy of the current Record. | |
func (r *Record) Write() { | |
if r.err != nil { | |
return | |
} | |
row := r.db.QueryRow(`INSERT INTO record (id, datatype, data) VALUES ($1, $2, $3) RETURNING time`, r.ID, r.DataType, r.Data) | |
r.err = row.Scan(&r.Time) | |
} | |
// Delete clears the record data. | |
func (r *Record) Delete() { | |
if r.err != nil { | |
return | |
} | |
r.Data = []byte("{}") | |
} | |
// Restore restores the record to a given time. | |
func (r *Record) Restore(t time.Time) { | |
if r.err != nil { | |
return | |
} | |
row := r.db.QueryRow(`SELECT id,datatype,data FROM record WHERE id = $1 AND time = $2 LIMIT 1`, r.ID, t) | |
r.err = row.Scan(&r.ID, &r.DataType, &r.Data) | |
} | |
// Unmarshal parses the JSON-encoded data and stores the result in the | |
// value pointed to by v. The Record ID is applied to the data as well | |
// as the Created and Modified times. | |
func (r *Record) Unmarshal(v Applyer) { | |
if r.err != nil { | |
return | |
} | |
err := r.Data.Unmarshal(v) | |
if err != nil { | |
r.err = err | |
return | |
} | |
var created time.Time | |
row := r.db.QueryRow(`SELECT time FROM record WHERE id = $1 ORDER BY time ASC LIMIT 1`, r.ID) | |
err = row.Scan(&created) | |
if err != nil { | |
r.err = err | |
return | |
} | |
v.ApplyID(r.ID) | |
v.ApplyTime(created, r.Time) | |
} | |
// Err returns the first error that was encountered by the Record. | |
func (r *Record) Err() error { | |
return r.err | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment