Skip to content

Commit

Permalink
feat(http): add label service (#1346)
Browse files Browse the repository at this point in the history
  • Loading branch information
imogenkinsman authored Dec 1, 2018
1 parent 47d04f1 commit 70a430c
Show file tree
Hide file tree
Showing 23 changed files with 1,269 additions and 65 deletions.
5 changes: 5 additions & 0 deletions bolt/bbolt.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ func (c *Client) initialize(ctx context.Context) error {
return err
}

// Always create labels bucket.
if err := c.initializeLabels(ctx, tx); err != nil {
return err
}

// Always create Session bucket.
if err := c.initializeSessions(ctx, tx); err != nil {
return err
Expand Down
6 changes: 6 additions & 0 deletions bolt/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,12 @@ func (c *Client) deleteBucket(ctx context.Context, tx *bolt.Tx, id platform.ID)
Err: err,
}
}

if err := c.deleteLabels(ctx, tx, platform.LabelFilter{ResourceID: id}); err != nil {
return &platform.Error{
Err: err,
}
}
return nil
}

Expand Down
6 changes: 6 additions & 0 deletions bolt/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,12 @@ func (c *Client) deleteDashboard(ctx context.Context, tx *bolt.Tx, id platform.I
if err := tx.Bucket(dashboardBucket).Delete(encodedID); err != nil {
return err
}

err = c.deleteLabels(ctx, tx, platform.LabelFilter{ResourceID: id})
if err != nil {
return err
}

// TODO(desa): add DeleteKeyValueLog method and use it here.
return c.deleteUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{
ResourceID: id,
Expand Down
173 changes: 173 additions & 0 deletions bolt/label.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package bolt

import (
"context"
"encoding/json"
"fmt"

"github.com/coreos/bbolt"
"github.com/influxdata/platform"
)

var (
labelBucket = []byte("labelsv1")
)

func (c *Client) initializeLabels(ctx context.Context, tx *bolt.Tx) error {
if _, err := tx.CreateBucketIfNotExists([]byte(labelBucket)); err != nil {
return err
}
return nil
}

func filterLabelsFn(filter platform.LabelFilter) func(l *platform.Label) bool {
return func(label *platform.Label) bool {
return (filter.Name == "" || (filter.Name == label.Name)) &&
(!filter.ResourceID.Valid() || (filter.ResourceID == label.ResourceID))
}
}

// FindLabels returns a list of labels that match a filter.
func (c *Client) FindLabels(ctx context.Context, filter platform.LabelFilter, opt ...platform.FindOptions) ([]*platform.Label, error) {
ls := []*platform.Label{}
err := c.db.View(func(tx *bolt.Tx) error {
labels, err := c.findLabels(ctx, tx, filter)
if err != nil {
return err
}
ls = labels
return nil
})

if err != nil {
return nil, err
}

return ls, nil
}

func (c *Client) findLabels(ctx context.Context, tx *bolt.Tx, filter platform.LabelFilter) ([]*platform.Label, error) {
ls := []*platform.Label{}
filterFn := filterLabelsFn(filter)
err := c.forEachLabel(ctx, tx, func(l *platform.Label) bool {
if filterFn(l) {
ls = append(ls, l)
}
return true
})

if err != nil {
return nil, err
}

return ls, nil
}

func (c *Client) CreateLabel(ctx context.Context, l *platform.Label) error {
return c.db.Update(func(tx *bolt.Tx) error {
return c.createLabel(ctx, tx, l)
})
}

func (c *Client) createLabel(ctx context.Context, tx *bolt.Tx, l *platform.Label) error {
unique := c.uniqueLabel(ctx, tx, l)

if !unique {
return fmt.Errorf("label %s already exists", l.Name)
}

v, err := json.Marshal(l)
if err != nil {
return err
}

key, err := labelKey(l)
if err != nil {
return err
}

if err := tx.Bucket(labelBucket).Put(key, v); err != nil {
return err
}

return nil
}

func labelKey(l *platform.Label) ([]byte, error) {
encodedResourceID, err := l.ResourceID.Encode()
if err != nil {
return nil, err
}

key := make([]byte, len(encodedResourceID)+len(l.Name))
copy(key, encodedResourceID)
copy(key[len(encodedResourceID):], l.Name)

return key, nil
}

func (c *Client) forEachLabel(ctx context.Context, tx *bolt.Tx, fn func(*platform.Label) bool) error {
cur := tx.Bucket(labelBucket).Cursor()
for k, v := cur.First(); k != nil; k, v = cur.Next() {
l := &platform.Label{}
if err := json.Unmarshal(v, l); err != nil {
return err
}
if !fn(l) {
break
}
}

return nil
}

func (c *Client) uniqueLabel(ctx context.Context, tx *bolt.Tx, l *platform.Label) bool {
key, err := labelKey(l)
if err != nil {
return false
}

v := tx.Bucket(labelBucket).Get(key)
return len(v) == 0
}

// DeleteLabel deletes a label.
func (c *Client) DeleteLabel(ctx context.Context, l platform.Label) error {
return c.db.Update(func(tx *bolt.Tx) error {
return c.deleteLabel(ctx, tx, platform.LabelFilter(l))
})
}

func (c *Client) deleteLabel(ctx context.Context, tx *bolt.Tx, filter platform.LabelFilter) error {
ls, err := c.findLabels(ctx, tx, filter)
if err != nil {
return err
}
if len(ls) == 0 {
return fmt.Errorf("label not found")
}

key, err := labelKey(ls[0])
if err != nil {
return err
}

return tx.Bucket(labelBucket).Delete(key)
}

func (c *Client) deleteLabels(ctx context.Context, tx *bolt.Tx, filter platform.LabelFilter) error {
ls, err := c.findLabels(ctx, tx, filter)
if err != nil {
return err
}
for _, l := range ls {
key, err := labelKey(l)
if err != nil {
return err
}
if err = tx.Bucket(labelBucket).Delete(key); err != nil {
return err
}
}
return nil
}
35 changes: 35 additions & 0 deletions bolt/label_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package bolt_test

import (
"context"
"testing"

"github.com/influxdata/platform"
platformtesting "github.com/influxdata/platform/testing"
)

func initLabelService(f platformtesting.LabelFields, t *testing.T) (platform.LabelService, func()) {
c, closeFn, err := NewTestClient()
if err != nil {
t.Fatalf("failed to create new bolt client: %v", err)
}
ctx := context.Background()
for _, l := range f.Labels {
if err := c.CreateLabel(ctx, l); err != nil {
t.Fatalf("failed to populate labels")
}
}

return c, func() {
defer closeFn()
for _, l := range f.Labels {
if err := c.DeleteLabel(ctx, *l); err != nil {
t.Logf("failed to remove label: %v", err)
}
}
}
}

func TestLabelService_LabelService(t *testing.T) {
platformtesting.LabelService(initLabelService, t)
}
2 changes: 2 additions & 0 deletions cmd/influxd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ func (m *Main) run(ctx context.Context) (err error) {
scraperTargetSvc platform.ScraperTargetStoreService = m.boltClient
telegrafSvc platform.TelegrafConfigStore = m.boltClient
userResourceSvc platform.UserResourceMappingService = m.boltClient
labelSvc platform.LabelService = m.boltClient
)

chronografSvc, err := server.NewServiceV2(ctx, m.boltClient.DB())
Expand Down Expand Up @@ -370,6 +371,7 @@ func (m *Main) run(ctx context.Context) (err error) {
UserService: userSvc,
OrganizationService: orgSvc,
UserResourceMappingService: userResourceSvc,
LabelService: labelSvc,
DashboardService: dashboardSvc,
DashboardOperationLogService: dashboardLogSvc,
BucketOperationLogService: bucketLogSvc,
Expand Down
7 changes: 4 additions & 3 deletions http/api_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type APIBackend struct {
UserService platform.UserService
OrganizationService platform.OrganizationService
UserResourceMappingService platform.UserResourceMappingService
LabelService platform.LabelService
DashboardService platform.DashboardService
DashboardOperationLogService platform.DashboardOperationLogService
BucketOperationLogService platform.BucketOperationLogService
Expand All @@ -71,7 +72,7 @@ func NewAPIHandler(b *APIBackend) *APIHandler {
h.SessionHandler.SessionService = b.SessionService
h.SessionHandler.Logger = b.Logger.With(zap.String("handler", "basicAuth"))

h.BucketHandler = NewBucketHandler(b.UserResourceMappingService)
h.BucketHandler = NewBucketHandler(b.UserResourceMappingService, b.LabelService)
h.BucketHandler.BucketService = b.BucketService
h.BucketHandler.BucketOperationLogService = b.BucketOperationLogService

Expand All @@ -85,7 +86,7 @@ func NewAPIHandler(b *APIBackend) *APIHandler {
h.UserHandler.BasicAuthService = b.BasicAuthService
h.UserHandler.UserOperationLogService = b.UserOperationLogService

h.DashboardHandler = NewDashboardHandler(b.UserResourceMappingService)
h.DashboardHandler = NewDashboardHandler(b.UserResourceMappingService, b.LabelService)
h.DashboardHandler.DashboardService = b.DashboardService
h.DashboardHandler.DashboardOperationLogService = b.DashboardOperationLogService

Expand All @@ -107,7 +108,7 @@ func NewAPIHandler(b *APIBackend) *APIHandler {
h.SetupHandler = NewSetupHandler()
h.SetupHandler.OnboardingService = b.OnboardingService

h.TaskHandler = NewTaskHandler(b.UserResourceMappingService, b.Logger)
h.TaskHandler = NewTaskHandler(b.UserResourceMappingService, b.LabelService, b.Logger)
h.TaskHandler.TaskService = b.TaskService
h.TaskHandler.AuthorizationService = b.AuthorizationService
h.TaskHandler.UserResourceMappingService = b.UserResourceMappingService
Expand Down
24 changes: 16 additions & 8 deletions http/bucket_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,27 @@ type BucketHandler struct {
BucketService platform.BucketService
BucketOperationLogService platform.BucketOperationLogService
UserResourceMappingService platform.UserResourceMappingService
LabelService platform.LabelService
}

const (
bucketsPath = "/api/v2/buckets"
bucketsIDPath = "/api/v2/buckets/:id"
bucketsIDLogPath = "/api/v2/buckets/:id/log"
bucketsIDMembersPath = "/api/v2/buckets/:id/members"
bucketsIDMembersIDPath = "/api/v2/buckets/:id/members/:userID"
bucketsIDOwnersPath = "/api/v2/buckets/:id/owners"
bucketsIDOwnersIDPath = "/api/v2/buckets/:id/owners/:userID"
bucketsPath = "/api/v2/buckets"
bucketsIDPath = "/api/v2/buckets/:id"
bucketsIDLogPath = "/api/v2/buckets/:id/log"
bucketsIDMembersPath = "/api/v2/buckets/:id/members"
bucketsIDMembersIDPath = "/api/v2/buckets/:id/members/:userID"
bucketsIDOwnersPath = "/api/v2/buckets/:id/owners"
bucketsIDOwnersIDPath = "/api/v2/buckets/:id/owners/:userID"
bucketsIDLabelsPath = "/api/v2/buckets/:id/labels"
bucketsIDLabelsNamePath = "/api/v2/buckets/:id/labels/:name"
)

// NewBucketHandler returns a new instance of BucketHandler.
func NewBucketHandler(mappingService platform.UserResourceMappingService) *BucketHandler {
func NewBucketHandler(mappingService platform.UserResourceMappingService, labelService platform.LabelService) *BucketHandler {
h := &BucketHandler{
Router: httprouter.New(),
UserResourceMappingService: mappingService,
LabelService: labelService,
}

h.HandlerFunc("POST", bucketsPath, h.handlePostBucket)
Expand All @@ -56,6 +60,10 @@ func NewBucketHandler(mappingService platform.UserResourceMappingService) *Bucke
h.HandlerFunc("GET", bucketsIDOwnersPath, newGetMembersHandler(h.UserResourceMappingService, platform.Owner))
h.HandlerFunc("DELETE", bucketsIDOwnersIDPath, newDeleteMemberHandler(h.UserResourceMappingService, platform.Owner))

h.HandlerFunc("GET", bucketsIDLabelsPath, newGetLabelsHandler(h.LabelService))
h.HandlerFunc("POST", bucketsIDLabelsPath, newPostLabelHandler(h.LabelService))
h.HandlerFunc("DELETE", bucketsIDLabelsNamePath, newDeleteLabelHandler(h.LabelService))

return h
}

Expand Down
Loading

0 comments on commit 70a430c

Please sign in to comment.