influxdb/notification/endpoint/service/store.go

306 lines
9.2 KiB
Go

package service
import (
"context"
influxdb "github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/kv"
"github.com/influxdata/influxdb/v2/notification/endpoint"
"github.com/influxdata/influxdb/v2/snowflake"
)
var (
// ErrNotificationEndpointNotFound is used when the notification endpoint is not found.
ErrNotificationEndpointNotFound = &influxdb.Error{
Msg: "notification endpoint not found",
Code: influxdb.ENotFound,
}
notificationEndpointBucket = []byte("notificationEndpointv1")
notificationEndpointIndexBucket = []byte("notificationEndpointIndexv1")
)
var _ influxdb.NotificationEndpointService = (*Store)(nil)
func newEndpointStore() *kv.IndexStore {
const resource = "notification endpoint"
var decEndpointEntFn kv.DecodeBucketValFn = func(key, val []byte) ([]byte, interface{}, error) {
edp, err := endpoint.UnmarshalJSON(val)
return key, edp, err
}
var decValToEntFn kv.ConvertValToEntFn = func(_ []byte, v interface{}) (kv.Entity, error) {
edp, ok := v.(influxdb.NotificationEndpoint)
if err := kv.IsErrUnexpectedDecodeVal(ok); err != nil {
return kv.Entity{}, err
}
return kv.Entity{
PK: kv.EncID(edp.GetID()),
UniqueKey: kv.Encode(kv.EncID(edp.GetOrgID()), kv.EncString(edp.GetName())),
Body: edp,
}, nil
}
return &kv.IndexStore{
Resource: resource,
EntStore: kv.NewStoreBase(resource, notificationEndpointBucket, kv.EncIDKey, kv.EncBodyJSON, decEndpointEntFn, decValToEntFn),
IndexStore: kv.NewOrgNameKeyStore(resource, notificationEndpointIndexBucket, true),
}
}
type Store struct {
kv kv.Store
endpointStore *kv.IndexStore
IDGenerator influxdb.IDGenerator
TimeGenerator influxdb.TimeGenerator
}
func NewStore(store kv.Store) *Store {
return &Store{
kv: store,
endpointStore: newEndpointStore(),
IDGenerator: snowflake.NewDefaultIDGenerator(),
TimeGenerator: influxdb.RealTimeGenerator{},
}
}
// CreateNotificationEndpoint creates a new notification endpoint and sets b.ID with the new identifier.
func (s *Store) CreateNotificationEndpoint(ctx context.Context, edp influxdb.NotificationEndpoint, userID influxdb.ID) error {
return s.kv.Update(ctx, func(tx kv.Tx) error {
return s.createNotificationEndpoint(ctx, tx, edp, userID)
})
}
func (s *Store) createNotificationEndpoint(ctx context.Context, tx kv.Tx, edp influxdb.NotificationEndpoint, userID influxdb.ID) error {
id := s.IDGenerator.ID()
edp.SetID(id)
now := s.TimeGenerator.Now()
edp.SetCreatedAt(now)
edp.SetUpdatedAt(now)
edp.BackfillSecretKeys()
if err := edp.Valid(); err != nil {
return err
}
ent := kv.Entity{
PK: kv.EncID(edp.GetID()),
UniqueKey: kv.Encode(kv.EncID(edp.GetOrgID()), kv.EncString(edp.GetName())),
Body: edp,
}
if err := s.endpointStore.Put(ctx, tx, ent, kv.PutNew()); err != nil {
return err
}
return nil
}
// UpdateNotificationEndpoint updates a single notification endpoint.
// Returns the new notification endpoint after update.
func (s *Store) UpdateNotificationEndpoint(ctx context.Context, id influxdb.ID, edp influxdb.NotificationEndpoint, userID influxdb.ID) (influxdb.NotificationEndpoint, error) {
var err error
err = s.kv.Update(ctx, func(tx kv.Tx) error {
edp, err = s.updateNotificationEndpoint(ctx, tx, id, edp, userID)
return err
})
return edp, err
}
func (s *Store) updateNotificationEndpoint(ctx context.Context, tx kv.Tx, id influxdb.ID, edp influxdb.NotificationEndpoint, userID influxdb.ID) (influxdb.NotificationEndpoint, error) {
current, err := s.findNotificationEndpointByID(ctx, tx, id)
if err != nil {
return nil, err
}
// ID and OrganizationID can not be updated
edp.SetCreatedAt(current.GetCRUDLog().CreatedAt)
edp.SetUpdatedAt(s.TimeGenerator.Now())
if err := edp.Valid(); err != nil {
return nil, err
}
ent := kv.Entity{
PK: kv.EncID(edp.GetID()),
UniqueKey: kv.Encode(kv.EncID(edp.GetOrgID()), kv.EncString(edp.GetName())),
Body: edp,
}
if err := s.endpointStore.Put(ctx, tx, ent, kv.PutUpdate()); err != nil {
return nil, err
}
return edp, nil
}
// PatchNotificationEndpoint updates a single notification endpoint with changeset.
// Returns the new notification endpoint state after update.
func (s *Store) PatchNotificationEndpoint(ctx context.Context, id influxdb.ID, upd influxdb.NotificationEndpointUpdate) (influxdb.NotificationEndpoint, error) {
var edp influxdb.NotificationEndpoint
if err := s.kv.Update(ctx, func(tx kv.Tx) (err error) {
edp, err = s.patchNotificationEndpoint(ctx, tx, id, upd)
if err != nil {
return err
}
return nil
}); err != nil {
return nil, err
}
return edp, nil
}
func (s *Store) patchNotificationEndpoint(ctx context.Context, tx kv.Tx, id influxdb.ID, upd influxdb.NotificationEndpointUpdate) (influxdb.NotificationEndpoint, error) {
edp, err := s.findNotificationEndpointByID(ctx, tx, id)
if err != nil {
return nil, err
}
if upd.Name != nil {
edp.SetName(*upd.Name)
}
if upd.Description != nil {
edp.SetDescription(*upd.Description)
}
if upd.Status != nil {
edp.SetStatus(*upd.Status)
}
edp.SetUpdatedAt(s.TimeGenerator.Now())
if err := edp.Valid(); err != nil {
return nil, err
}
// TODO(jsteenb2): every above here moves into service layer
ent := kv.Entity{
PK: kv.EncID(edp.GetID()),
UniqueKey: kv.Encode(kv.EncID(edp.GetOrgID()), kv.EncString(edp.GetName())),
Body: edp,
}
if err := s.endpointStore.Put(ctx, tx, ent, kv.PutUpdate()); err != nil {
return nil, err
}
return edp, nil
}
// PutNotificationEndpoint put a notification endpoint to storage.
func (s *Store) PutNotificationEndpoint(ctx context.Context, edp influxdb.NotificationEndpoint) error {
// TODO(jsteenb2): all the stuffs before the update should be moved up into the
// service layer as well as all the id/time setting items
if err := edp.Valid(); err != nil {
return err
}
return s.kv.Update(ctx, func(tx kv.Tx) (err error) {
ent := kv.Entity{
PK: kv.EncID(edp.GetID()),
UniqueKey: kv.Encode(kv.EncID(edp.GetOrgID()), kv.EncString(edp.GetName())),
Body: edp,
}
return s.endpointStore.Put(ctx, tx, ent)
})
}
// FindNotificationEndpointByID returns a single notification endpoint by ID.
func (s *Store) FindNotificationEndpointByID(ctx context.Context, id influxdb.ID) (influxdb.NotificationEndpoint, error) {
var (
edp influxdb.NotificationEndpoint
err error
)
err = s.kv.View(ctx, func(tx kv.Tx) error {
edp, err = s.findNotificationEndpointByID(ctx, tx, id)
return err
})
return edp, err
}
func (s *Store) findNotificationEndpointByID(ctx context.Context, tx kv.Tx, id influxdb.ID) (influxdb.NotificationEndpoint, error) {
decodedEnt, err := s.endpointStore.FindEnt(ctx, tx, kv.Entity{PK: kv.EncID(id)})
if err != nil {
return nil, err
}
edp, ok := decodedEnt.(influxdb.NotificationEndpoint)
return edp, kv.IsErrUnexpectedDecodeVal(ok)
}
// FindNotificationEndpoints returns a list of notification endpoints that match isNext and the total count of matching notification endpoints.
// Additional options provide pagination & sorting.
func (s *Store) FindNotificationEndpoints(ctx context.Context, filter influxdb.NotificationEndpointFilter, opt ...influxdb.FindOptions) (edps []influxdb.NotificationEndpoint, n int, err error) {
err = s.kv.View(ctx, func(tx kv.Tx) error {
edps, n, err = s.findNotificationEndpoints(ctx, tx, filter, opt...)
return err
})
return edps, n, err
}
func (s *Store) findNotificationEndpoints(ctx context.Context, tx kv.Tx, filter influxdb.NotificationEndpointFilter, opt ...influxdb.FindOptions) ([]influxdb.NotificationEndpoint, int, error) {
var o influxdb.FindOptions
if len(opt) > 0 {
o = opt[0]
}
edps := make([]influxdb.NotificationEndpoint, 0)
err := s.endpointStore.Find(ctx, tx, kv.FindOpts{
Descending: o.Descending,
Offset: o.Offset,
Limit: o.Limit,
FilterEntFn: filterEndpointsFn(filter),
CaptureFn: func(k []byte, v interface{}) error {
edp, ok := v.(influxdb.NotificationEndpoint)
if err := kv.IsErrUnexpectedDecodeVal(ok); err != nil {
return err
}
edps = append(edps, edp)
return nil
},
})
if err != nil {
return nil, 0, err
}
return edps, len(edps), err
}
func filterEndpointsFn(filter influxdb.NotificationEndpointFilter) func([]byte, interface{}) bool {
return func(key []byte, val interface{}) bool {
edp := val.(influxdb.NotificationEndpoint)
if filter.ID != nil && edp.GetID() != *filter.ID {
return false
}
if filter.OrgID != nil && edp.GetOrgID() != *filter.OrgID {
return false
}
return true
}
}
// DeleteNotificationEndpoint removes a notification endpoint by ID.
func (s *Store) DeleteNotificationEndpoint(ctx context.Context, id influxdb.ID) (flds []influxdb.SecretField, orgID influxdb.ID, err error) {
err = s.kv.Update(ctx, func(tx kv.Tx) error {
flds, orgID, err = s.deleteNotificationEndpoint(ctx, tx, id)
return err
})
return flds, orgID, err
}
func (s *Store) deleteNotificationEndpoint(ctx context.Context, tx kv.Tx, id influxdb.ID) (flds []influxdb.SecretField, orgID influxdb.ID, err error) {
edp, err := s.findNotificationEndpointByID(ctx, tx, id)
if err != nil {
return nil, 0, err
}
if err := s.endpointStore.DeleteEnt(ctx, tx, kv.Entity{PK: kv.EncID(id)}); err != nil {
return nil, 0, err
}
return edp.SecretFields(), edp.GetOrgID(), nil
}