github.com/safing/portbase@v0.19.5/database/hook.go (about)

     1  package database
     2  
     3  import (
     4  	"github.com/safing/portbase/database/query"
     5  	"github.com/safing/portbase/database/record"
     6  )
     7  
     8  // Hook can be registered for a database query and
     9  // will be executed at certain points during the life
    10  // cycle of a database record.
    11  type Hook interface {
    12  	// UsesPreGet should return true if the hook's PreGet
    13  	// should be called prior to loading a database record
    14  	// from the underlying storage.
    15  	UsesPreGet() bool
    16  	// PreGet is called before a database record is loaded from
    17  	// the underlying storage. A PreGet hookd may be used to
    18  	// implement more advanced access control on database keys.
    19  	PreGet(dbKey string) error
    20  	// UsesPostGet should return true if the hook's PostGet
    21  	// should be called after loading a database record from
    22  	// the underlying storage.
    23  	UsesPostGet() bool
    24  	// PostGet is called after a record has been loaded form the
    25  	// underlying storage and may perform additional mutation
    26  	// or access check based on the records data.
    27  	// The passed record is already locked by the database system
    28  	// so users can safely access all data of r.
    29  	PostGet(r record.Record) (record.Record, error)
    30  	// UsesPrePut should return true if the hook's PrePut method
    31  	// should be called prior to saving a record in the database.
    32  	UsesPrePut() bool
    33  	// PrePut is called prior to saving (creating or updating) a
    34  	// record in the database storage. It may be used to perform
    35  	// extended validation or mutations on the record.
    36  	// The passed record is already locked by the database system
    37  	// so users can safely access all data of r.
    38  	PrePut(r record.Record) (record.Record, error)
    39  }
    40  
    41  // RegisteredHook is a registered database hook.
    42  type RegisteredHook struct {
    43  	q *query.Query
    44  	h Hook
    45  }
    46  
    47  // RegisterHook registers a hook for records matching the given
    48  // query in the database.
    49  func RegisterHook(q *query.Query, hook Hook) (*RegisteredHook, error) {
    50  	_, err := q.Check()
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	c, err := getController(q.DatabaseName())
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	rh := &RegisteredHook{
    61  		q: q,
    62  		h: hook,
    63  	}
    64  
    65  	c.hooksLock.Lock()
    66  	defer c.hooksLock.Unlock()
    67  	c.hooks = append(c.hooks, rh)
    68  
    69  	return rh, nil
    70  }
    71  
    72  // Cancel unregisteres the hook from the database. Once
    73  // Cancel returned the hook's methods will not be called
    74  // anymore for updates that matched the registered query.
    75  func (h *RegisteredHook) Cancel() error {
    76  	c, err := getController(h.q.DatabaseName())
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	c.hooksLock.Lock()
    82  	defer c.hooksLock.Unlock()
    83  
    84  	for key, hook := range c.hooks {
    85  		if hook.q == h.q {
    86  			c.hooks = append(c.hooks[:key], c.hooks[key+1:]...)
    87  			return nil
    88  		}
    89  	}
    90  	return nil
    91  }