150 lines
3.7 KiB
Go
150 lines
3.7 KiB
Go
package notebooks
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/influxdata/influxdb/v2"
|
|
"github.com/influxdata/influxdb/v2/kit/platform"
|
|
"github.com/influxdata/influxdb/v2/snowflake"
|
|
"github.com/influxdata/influxdb/v2/sqlite"
|
|
)
|
|
|
|
var _ influxdb.NotebookService = (*Service)(nil)
|
|
|
|
type Service struct {
|
|
store *sqlite.SqlStore
|
|
idGenerator platform.IDGenerator
|
|
}
|
|
|
|
func NewService(store *sqlite.SqlStore) *Service {
|
|
return &Service{
|
|
store: store,
|
|
idGenerator: snowflake.NewIDGenerator(),
|
|
}
|
|
}
|
|
|
|
func (s *Service) GetNotebook(ctx context.Context, id platform.ID) (*influxdb.Notebook, error) {
|
|
var n influxdb.Notebook
|
|
|
|
query := `
|
|
SELECT id, org_id, name, spec, created_at, updated_at
|
|
FROM notebooks WHERE id = $1`
|
|
|
|
if err := s.store.DB.GetContext(ctx, &n, query, id); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, influxdb.ErrNotebookNotFound
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
return &n, nil
|
|
}
|
|
|
|
// CreateNotebook creates a notebook. Note that this and all "write" operations on the database need to use the Mutex lock,
|
|
// since sqlite can only handle 1 concurrent write operation at a time.
|
|
func (s *Service) CreateNotebook(ctx context.Context, create *influxdb.NotebookReqBody) (*influxdb.Notebook, error) {
|
|
s.store.Mu.Lock()
|
|
defer s.store.Mu.Unlock()
|
|
|
|
nowTime := time.Now().UTC()
|
|
n := influxdb.Notebook{
|
|
ID: s.idGenerator.ID(),
|
|
OrgID: create.OrgID,
|
|
Name: create.Name,
|
|
Spec: create.Spec,
|
|
CreatedAt: nowTime,
|
|
UpdatedAt: nowTime,
|
|
}
|
|
|
|
query := `
|
|
INSERT INTO notebooks (id, org_id, name, spec, created_at, updated_at)
|
|
VALUES (:id, :org_id, :name, :spec, :created_at, :updated_at)`
|
|
|
|
_, err := s.store.DB.NamedExecContext(ctx, query, &n)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Ideally, the create query would use "RETURNING" in order to avoid making a separate query.
|
|
// Unfortunately this breaks the scanning of values into the result struct, so we have to make a separate
|
|
// SELECT request to return the result from the database.
|
|
return s.GetNotebook(ctx, n.ID)
|
|
}
|
|
|
|
// UpdateNotebook updates a notebook.
|
|
func (s *Service) UpdateNotebook(ctx context.Context, id platform.ID, update *influxdb.NotebookReqBody) (*influxdb.Notebook, error) {
|
|
s.store.Mu.Lock()
|
|
defer s.store.Mu.Unlock()
|
|
|
|
nowTime := time.Now().UTC()
|
|
n := influxdb.Notebook{
|
|
ID: id,
|
|
OrgID: update.OrgID,
|
|
Name: update.Name,
|
|
Spec: update.Spec,
|
|
UpdatedAt: nowTime,
|
|
}
|
|
|
|
query := `
|
|
UPDATE notebooks SET org_id = :org_id, name = :name, spec = :spec, updated_at = :updated_at
|
|
WHERE id = :id`
|
|
|
|
_, err := s.store.DB.NamedExecContext(ctx, query, &n)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, influxdb.ErrNotebookNotFound
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
return s.GetNotebook(ctx, n.ID)
|
|
}
|
|
|
|
// DeleteNotebook deletes a notebook.
|
|
func (s *Service) DeleteNotebook(ctx context.Context, id platform.ID) error {
|
|
s.store.Mu.Lock()
|
|
defer s.store.Mu.Unlock()
|
|
|
|
query := `
|
|
DELETE FROM notebooks
|
|
WHERE id = $1`
|
|
|
|
res, err := s.store.DB.ExecContext(ctx, query, id.String())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r, err := res.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if r == 0 {
|
|
return influxdb.ErrNotebookNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ListNotebooks lists notebooks matching the provided filter. Currently, only org_id is used in the filter.
|
|
// Future uses may support pagination via this filter as well.
|
|
func (s *Service) ListNotebooks(ctx context.Context, filter influxdb.NotebookListFilter) ([]*influxdb.Notebook, error) {
|
|
ns := []*influxdb.Notebook{}
|
|
|
|
query := `
|
|
SELECT id, org_id, name, spec, created_at, updated_at
|
|
FROM notebooks
|
|
WHERE org_id = $1`
|
|
|
|
if err := s.store.DB.SelectContext(ctx, &ns, query, filter.OrgID); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ns, nil
|
|
}
|