mirror of
https://github.com/safing/portbase
synced 2025-09-10 14:57:46 +00:00
Finish minimal feature set, start with tests
This commit is contained in:
parent
3d60431376
commit
06a34f931e
34 changed files with 651 additions and 346 deletions
|
@ -1,9 +1,19 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/Safing/portbase/database/accessor"
|
||||
"github.com/Safing/portbase/database/iterator"
|
||||
"github.com/Safing/portbase/database/query"
|
||||
"github.com/Safing/portbase/database/record"
|
||||
)
|
||||
|
||||
const (
|
||||
getDBFromKey = ""
|
||||
)
|
||||
|
||||
// Interface provides a method to access the database with attached options.
|
||||
type Interface struct {
|
||||
options *Options
|
||||
|
@ -30,7 +40,7 @@ func NewInterface(opts *Options) *Interface {
|
|||
|
||||
// Exists return whether a record with the given key exists.
|
||||
func (i *Interface) Exists(key string) (bool, error) {
|
||||
_, err := i.getRecord(key)
|
||||
_, _, err := i.getRecord(getDBFromKey, key, false, false)
|
||||
if err != nil {
|
||||
if err == ErrNotFound {
|
||||
return false, nil
|
||||
|
@ -42,28 +52,161 @@ func (i *Interface) Exists(key string) (bool, error) {
|
|||
|
||||
// Get return the record with the given key.
|
||||
func (i *Interface) Get(key string) (record.Record, error) {
|
||||
r, err := i.getRecord(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !r.Meta().CheckPermission(i.options.Local, i.options.Internal) {
|
||||
return nil, ErrPermissionDenied
|
||||
}
|
||||
|
||||
return r, nil
|
||||
r, _, err := i.getRecord(getDBFromKey, key, true, false)
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (i *Interface) getRecord(key string) (record.Record, error) {
|
||||
dbKey, db, err := splitKeyAndGetDatabase(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (i *Interface) getRecord(dbName string, dbKey string, check bool, mustBeWriteable bool) (r record.Record, db *Controller, err error) {
|
||||
if dbName == "" {
|
||||
dbName, dbKey = record.ParseKey(dbKey)
|
||||
}
|
||||
|
||||
r, err := db.Get(dbKey)
|
||||
db, err = getDatabase(dbName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
if mustBeWriteable && db.ReadOnly() {
|
||||
return nil, nil, ErrReadOnly
|
||||
}
|
||||
|
||||
r, err = db.Get(dbKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if check && !r.Meta().CheckPermission(i.options.Local, i.options.Internal) {
|
||||
return nil, nil, ErrPermissionDenied
|
||||
}
|
||||
|
||||
return r, db, nil
|
||||
}
|
||||
|
||||
// InsertValue inserts a value into a record.
|
||||
func (i *Interface) InsertValue(key string, attribute string, value interface{}) error {
|
||||
r, db, err := i.getRecord(getDBFromKey, key, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
var acc accessor.Accessor
|
||||
if r.IsWrapped() {
|
||||
wrapper, ok := r.(*record.Wrapper)
|
||||
if !ok {
|
||||
return errors.New("record is malformed (reports to be wrapped but is not of type *record.Wrapper)")
|
||||
}
|
||||
acc = accessor.NewJSONBytesAccessor(&wrapper.Data)
|
||||
} else {
|
||||
acc = accessor.NewStructAccessor(r)
|
||||
}
|
||||
|
||||
err = acc.Set(attribute, value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set value with %s: %s", acc.Type(), err)
|
||||
}
|
||||
|
||||
return db.Put(r)
|
||||
}
|
||||
|
||||
// Put saves a record to the database.
|
||||
func (i *Interface) Put(r record.Record) error {
|
||||
_, db, err := i.getRecord(r.DatabaseName(), r.DatabaseKey(), true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Put(r)
|
||||
}
|
||||
|
||||
// PutNew saves a record to the database as a new record (ie. with a new creation timestamp).
|
||||
func (i *Interface) PutNew(r record.Record) error {
|
||||
_, db, err := i.getRecord(r.DatabaseName(), r.DatabaseKey(), true, true)
|
||||
if err != nil && err != ErrNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
r.SetMeta(&record.Meta{})
|
||||
return db.Put(r)
|
||||
}
|
||||
|
||||
// SetAbsoluteExpiry sets an absolute record expiry.
|
||||
func (i *Interface) SetAbsoluteExpiry(key string, time int64) error {
|
||||
r, db, err := i.getRecord(getDBFromKey, key, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Meta().SetAbsoluteExpiry(time)
|
||||
return db.Put(r)
|
||||
}
|
||||
|
||||
// SetRelativateExpiry sets a relative (self-updating) record expiry.
|
||||
func (i *Interface) SetRelativateExpiry(key string, duration int64) error {
|
||||
r, db, err := i.getRecord(getDBFromKey, key, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Meta().SetRelativateExpiry(duration)
|
||||
return db.Put(r)
|
||||
}
|
||||
|
||||
// MakeSecret marks the record as a secret, meaning interfacing processes, such as an UI, are denied access to the record.
|
||||
func (i *Interface) MakeSecret(key string) error {
|
||||
r, db, err := i.getRecord(getDBFromKey, key, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Meta().MakeSecret()
|
||||
return db.Put(r)
|
||||
}
|
||||
|
||||
// MakeCrownJewel marks a record as a crown jewel, meaning it will only be accessible locally.
|
||||
func (i *Interface) MakeCrownJewel(key string) error {
|
||||
r, db, err := i.getRecord(getDBFromKey, key, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Meta().MakeCrownJewel()
|
||||
return db.Put(r)
|
||||
}
|
||||
|
||||
// Delete deletes a record from the database.
|
||||
func (i *Interface) Delete(key string) error {
|
||||
r, db, err := i.getRecord(getDBFromKey, key, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.Meta().Delete()
|
||||
return db.Put(r)
|
||||
}
|
||||
|
||||
// Query executes the given query on the database.
|
||||
func (i *Interface) Query(q *query.Query) (*iterator.Iterator, error) {
|
||||
db, err := getDatabase(q.DatabaseName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db.Query(q, i.options.Local, i.options.Internal)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue