diff --git a/http/bucket_service.go b/http/bucket_service.go index 9fce8413855..1ebe05b8c34 100644 --- a/http/bucket_service.go +++ b/http/bucket_service.go @@ -188,17 +188,26 @@ func newBucketUpdate(pb *platform.BucketUpdate) *bucketUpdate { type bucketResponse struct { Links map[string]string `json:"links"` bucket + Labels []platform.Label `json:"labels"` } -func newBucketResponse(b *platform.Bucket) *bucketResponse { - return &bucketResponse{ +func newBucketResponse(b *platform.Bucket, labels []*platform.Label) *bucketResponse { + res := &bucketResponse{ Links: map[string]string{ - "self": fmt.Sprintf("/api/v2/buckets/%s", b.ID), - "log": fmt.Sprintf("/api/v2/buckets/%s/log", b.ID), - "org": fmt.Sprintf("/api/v2/orgs/%s", b.OrganizationID), + "self": fmt.Sprintf("/api/v2/buckets/%s", b.ID), + "log": fmt.Sprintf("/api/v2/buckets/%s/log", b.ID), + "labels": fmt.Sprintf("/api/v2/buckets/%s/labels", b.ID), + "org": fmt.Sprintf("/api/v2/orgs/%s", b.OrganizationID), }, bucket: *newBucket(b), + Labels: []platform.Label{}, } + + for _, l := range labels { + res.Labels = append(res.Labels, *l) + } + + return res } type bucketsResponse struct { @@ -206,10 +215,11 @@ type bucketsResponse struct { Buckets []*bucketResponse `json:"buckets"` } -func newBucketsResponse(opts platform.FindOptions, f platform.BucketFilter, bs []*platform.Bucket) *bucketsResponse { +func newBucketsResponse(ctx context.Context, opts platform.FindOptions, f platform.BucketFilter, bs []*platform.Bucket, labelService platform.LabelService) *bucketsResponse { rs := make([]*bucketResponse, 0, len(bs)) for _, b := range bs { - rs = append(rs, newBucketResponse(b)) + labels, _ := labelService.FindLabels(ctx, platform.LabelFilter{ResourceID: b.ID}) + rs = append(rs, newBucketResponse(b, labels)) } return &bucketsResponse{ Links: newPagingLinks(bucketsPath, opts, f, len(bs)), @@ -232,7 +242,7 @@ func (h *BucketHandler) handlePostBucket(w http.ResponseWriter, r *http.Request) return } - if err := encodeResponse(ctx, w, http.StatusCreated, newBucketResponse(req.Bucket)); err != nil { + if err := encodeResponse(ctx, w, http.StatusCreated, newBucketResponse(req.Bucket, []*platform.Label{})); err != nil { logEncodingError(h.Logger, r, err) return } @@ -283,7 +293,13 @@ func (h *BucketHandler) handleGetBucket(w http.ResponseWriter, r *http.Request) return } - if err := encodeResponse(ctx, w, http.StatusOK, newBucketResponse(b)); err != nil { + labels, err := h.LabelService.FindLabels(ctx, platform.LabelFilter{ResourceID: b.ID}) + if err != nil { + EncodeError(ctx, err, w) + return + } + + if err := encodeResponse(ctx, w, http.StatusOK, newBucketResponse(b, labels)); err != nil { logEncodingError(h.Logger, r, err) return } @@ -367,7 +383,7 @@ func (h *BucketHandler) handleGetBuckets(w http.ResponseWriter, r *http.Request) return } - if err := encodeResponse(ctx, w, http.StatusOK, newBucketsResponse(req.opts, req.filter, bs)); err != nil { + if err := encodeResponse(ctx, w, http.StatusOK, newBucketsResponse(ctx, req.opts, req.filter, bs, h.LabelService)); err != nil { logEncodingError(h.Logger, r, err) return } @@ -424,7 +440,13 @@ func (h *BucketHandler) handlePatchBucket(w http.ResponseWriter, r *http.Request return } - if err := encodeResponse(ctx, w, http.StatusOK, newBucketResponse(b)); err != nil { + labels, err := h.LabelService.FindLabels(ctx, platform.LabelFilter{ResourceID: b.ID}) + if err != nil { + EncodeError(ctx, err, w) + return + } + + if err := encodeResponse(ctx, w, http.StatusOK, newBucketResponse(b, labels)); err != nil { logEncodingError(h.Logger, r, err) return } diff --git a/http/bucket_test.go b/http/bucket_test.go index 3f5cc99c80e..eac5985f451 100644 --- a/http/bucket_test.go +++ b/http/bucket_test.go @@ -21,6 +21,7 @@ import ( func TestService_handleGetBuckets(t *testing.T) { type fields struct { BucketService platform.BucketService + LabelService platform.LabelService } type args struct { queryParams map[string][]string @@ -58,6 +59,20 @@ func TestService_handleGetBuckets(t *testing.T) { }, 2, nil }, }, + &mock.LabelService{ + FindLabelsFn: func(ctx context.Context, f platform.LabelFilter) ([]*platform.Label, error) { + labels := []*platform.Label{ + { + ResourceID: f.ResourceID, + Name: "label", + Properties: map[string]string{ + "color": "fff000", + }, + }, + } + return labels, nil + }, + }, }, args: args{ map[string][]string{ @@ -78,23 +93,43 @@ func TestService_handleGetBuckets(t *testing.T) { "links": { "org": "/api/v2/orgs/50f7ba1150f7ba11", "self": "/api/v2/buckets/0b501e7e557ab1ed", - "log": "/api/v2/buckets/0b501e7e557ab1ed/log" + "log": "/api/v2/buckets/0b501e7e557ab1ed/log", + "labels": "/api/v2/buckets/0b501e7e557ab1ed/labels" }, "id": "0b501e7e557ab1ed", "organizationID": "50f7ba1150f7ba11", "name": "hello", - "retentionRules": [{"type": "expire", "everySeconds": 2}] + "retentionRules": [{"type": "expire", "everySeconds": 2}], + "labels": [ + { + "resourceID": "0b501e7e557ab1ed", + "name": "label", + "properties": { + "color": "fff000" + } + } + ] }, { "links": { "org": "/api/v2/orgs/7e55e118dbabb1ed", "self": "/api/v2/buckets/c0175f0077a77005", - "log": "/api/v2/buckets/c0175f0077a77005/log" + "log": "/api/v2/buckets/c0175f0077a77005/log", + "labels": "/api/v2/buckets/c0175f0077a77005/labels" }, "id": "c0175f0077a77005", "organizationID": "7e55e118dbabb1ed", "name": "example", - "retentionRules": [{"type": "expire", "everySeconds": 86400}] + "retentionRules": [{"type": "expire", "everySeconds": 86400}], + "labels": [ + { + "resourceID": "c0175f0077a77005", + "name": "label", + "properties": { + "color": "fff000" + } + } + ] } ] } @@ -109,6 +144,7 @@ func TestService_handleGetBuckets(t *testing.T) { return []*platform.Bucket{}, 0, nil }, }, + &mock.LabelService{}, }, args: args{ map[string][]string{ @@ -132,7 +168,7 @@ func TestService_handleGetBuckets(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mappingService := mock.NewUserResourceMappingService() - labelService := mock.NewLabelService() + labelService := tt.fields.LabelService userService := mock.NewUserService() h := NewBucketHandler(mappingService, labelService, userService) h.BucketService = tt.fields.BucketService @@ -161,8 +197,8 @@ func TestService_handleGetBuckets(t *testing.T) { if tt.wants.contentType != "" && content != tt.wants.contentType { t.Errorf("%q. handleGetBuckets() = %v, want %v", tt.name, content, tt.wants.contentType) } - if eq, diff, _ := jsonEqual(string(body), tt.wants.body); tt.wants.body != "" && !eq { - t.Errorf("%q. handleGetBuckets() = ***%s***", tt.name, diff) + if eq, diff, err := jsonEqual(string(body), tt.wants.body); err != nil || tt.wants.body != "" && !eq { + t.Errorf("%q. handleGetBuckets() = ***%v***", tt.name, diff) } }) } @@ -216,12 +252,14 @@ func TestService_handleGetBucket(t *testing.T) { "links": { "org": "/api/v2/orgs/020f755c3c082000", "self": "/api/v2/buckets/020f755c3c082000", - "log": "/api/v2/buckets/020f755c3c082000/log" + "log": "/api/v2/buckets/020f755c3c082000/log", + "labels": "/api/v2/buckets/020f755c3c082000/labels" }, "id": "020f755c3c082000", "organizationID": "020f755c3c082000", "name": "hello", - "retentionRules": [{"type": "expire", "everySeconds": 30}] + "retentionRules": [{"type": "expire", "everySeconds": 30}], + "labels": [] } `, }, @@ -332,12 +370,14 @@ func TestService_handlePostBucket(t *testing.T) { "links": { "org": "/api/v2/orgs/6f626f7274697320", "self": "/api/v2/buckets/020f755c3c082000", - "log": "/api/v2/buckets/020f755c3c082000/log" + "log": "/api/v2/buckets/020f755c3c082000/log", + "labels": "/api/v2/buckets/020f755c3c082000/labels" }, "id": "020f755c3c082000", "organizationID": "6f626f7274697320", "name": "hello", - "retentionRules": [] + "retentionRules": [], + "labels": [] } `, }, @@ -542,12 +582,14 @@ func TestService_handlePatchBucket(t *testing.T) { "links": { "org": "/api/v2/orgs/020f755c3c082000", "self": "/api/v2/buckets/020f755c3c082000", - "log": "/api/v2/buckets/020f755c3c082000/log" + "log": "/api/v2/buckets/020f755c3c082000/log", + "labels": "/api/v2/buckets/020f755c3c082000/labels" }, "id": "020f755c3c082000", "organizationID": "020f755c3c082000", "name": "example", - "retentionRules": [{"type": "expire", "everySeconds": 2}] + "retentionRules": [{"type": "expire", "everySeconds": 2}], + "labels": [] } `, }, @@ -613,12 +655,14 @@ func TestService_handlePatchBucket(t *testing.T) { "links": { "org": "/api/v2/orgs/020f755c3c082000", "self": "/api/v2/buckets/020f755c3c082000", - "log": "/api/v2/buckets/020f755c3c082000/log" + "log": "/api/v2/buckets/020f755c3c082000/log", + "labels": "/api/v2/buckets/020f755c3c082000/labels" }, "id": "020f755c3c082000", "organizationID": "020f755c3c082000", "name": "bucket with no retention", - "retentionRules": [] + "retentionRules": [], + "labels": [] } `, }, @@ -665,12 +709,14 @@ func TestService_handlePatchBucket(t *testing.T) { "links": { "org": "/api/v2/orgs/020f755c3c082000", "self": "/api/v2/buckets/020f755c3c082000", - "log": "/api/v2/buckets/020f755c3c082000/log" + "log": "/api/v2/buckets/020f755c3c082000/log", + "labels": "/api/v2/buckets/020f755c3c082000/labels" }, "id": "020f755c3c082000", "organizationID": "020f755c3c082000", "name": "b1", - "retentionRules": [] + "retentionRules": [], + "labels": [] } `, }, diff --git a/http/onboarding.go b/http/onboarding.go index f04dca24b34..98d437f3be5 100644 --- a/http/onboarding.go +++ b/http/onboarding.go @@ -92,7 +92,7 @@ func newOnboardingResponse(results *platform.OnboardingResults) *onboardingRespo } return &onboardingResponse{ User: newUserResponse(results.User), - Bucket: newBucketResponse(results.Bucket), + Bucket: newBucketResponse(results.Bucket, []*platform.Label{}), Organization: newOrgResponse(results.Org), Auth: newAuthResponse(results.Auth, results.Org, results.User, ps), } diff --git a/http/swagger.yml b/http/swagger.yml index cefbe7605b9..3c8ba280ef1 100644 --- a/http/swagger.yml +++ b/http/swagger.yml @@ -4770,6 +4770,8 @@ components: example: 86400 minimum: 1 required: [type, everySeconds] + labels: + $ref: "#/components/schemas/Labels" required: [name, retentionRules] Buckets: type: object @@ -6600,8 +6602,10 @@ components: properties: self: type: string - owners: - $ref: "#/components/schemas/Owners" + labels: + type: string + labels: + $ref: "#/components/schemas/Labels" Telegrafs: type: object properties: diff --git a/http/telegraf.go b/http/telegraf.go index b0507566b4f..c0d339ab80a 100644 --- a/http/telegraf.go +++ b/http/telegraf.go @@ -76,34 +76,45 @@ func NewTelegrafHandler( return h } -type link struct { - Self string `json:"self"` +type telegrafLinks struct { + Self string `json:"self"` + Labels string `json:"labels"` } type telegrafResponse struct { *platform.TelegrafConfig - Links link `json:"links"` + Labels []platform.Label `json:"labels"` + Links telegrafLinks `json:"links"` } type telegrafResponses struct { TelegrafConfigs []telegrafResponse `json:"configurations"` } -func newTelegrafResponse(tc *platform.TelegrafConfig) telegrafResponse { - return telegrafResponse{ +func newTelegrafResponse(tc *platform.TelegrafConfig, labels []*platform.Label) telegrafResponse { + res := telegrafResponse{ TelegrafConfig: tc, - Links: link{ - Self: fmt.Sprintf("/api/v2/telegrafs/%s", tc.ID.String()), + Links: telegrafLinks{ + Self: fmt.Sprintf("/api/v2/telegrafs/%s", tc.ID), + Labels: fmt.Sprintf("/api/v2/telegrafs/%s/labels", tc.ID), }, + Labels: []platform.Label{}, } + + for _, l := range labels { + res.Labels = append(res.Labels, *l) + } + + return res } -func newTelegrafResponses(tcs []*platform.TelegrafConfig) telegrafResponses { +func newTelegrafResponses(ctx context.Context, tcs []*platform.TelegrafConfig, labelService platform.LabelService) telegrafResponses { resp := telegrafResponses{ TelegrafConfigs: make([]telegrafResponse, len(tcs)), } for i, c := range tcs { - resp.TelegrafConfigs[i] = newTelegrafResponse(c) + labels, _ := labelService.FindLabels(ctx, platform.LabelFilter{ResourceID: c.ID}) + resp.TelegrafConfigs[i] = newTelegrafResponse(c, labels) } return resp } @@ -134,7 +145,7 @@ func (h *TelegrafHandler) handleGetTelegrafs(w http.ResponseWriter, r *http.Requ EncodeError(ctx, err, w) return } - if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponses(tcs)); err != nil { + if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponses(ctx, tcs, h.LabelService)); err != nil { logEncodingError(h.Logger, r, err) return } @@ -163,7 +174,13 @@ func (h *TelegrafHandler) handleGetTelegraf(w http.ResponseWriter, r *http.Reque w.WriteHeader(http.StatusOK) w.Write([]byte(tc.TOML())) case "application/json": - if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc)); err != nil { + labels, err := h.LabelService.FindLabels(ctx, platform.LabelFilter{ResourceID: tc.ID}) + if err != nil { + EncodeError(ctx, err, w) + return + } + + if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc, labels)); err != nil { logEncodingError(h.Logger, r, err) return } @@ -241,7 +258,7 @@ func (h *TelegrafHandler) handlePostTelegraf(w http.ResponseWriter, r *http.Requ return } - if err := encodeResponse(ctx, w, http.StatusCreated, newTelegrafResponse(tc)); err != nil { + if err := encodeResponse(ctx, w, http.StatusCreated, newTelegrafResponse(tc, []*platform.Label{})); err != nil { logEncodingError(h.Logger, r, err) return } @@ -268,7 +285,13 @@ func (h *TelegrafHandler) handlePutTelegraf(w http.ResponseWriter, r *http.Reque return } - if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc)); err != nil { + labels, err := h.LabelService.FindLabels(ctx, platform.LabelFilter{ResourceID: tc.ID}) + if err != nil { + EncodeError(ctx, err, w) + return + } + + if err := encodeResponse(ctx, w, http.StatusOK, newTelegrafResponse(tc, labels)); err != nil { logEncodingError(h.Logger, r, err) return } diff --git a/http/telegraf_test.go b/http/telegraf_test.go index 8db0c4dc9c6..b3b6820b6ca 100644 --- a/http/telegraf_test.go +++ b/http/telegraf_test.go @@ -724,7 +724,8 @@ func Test_newTelegrafResponses(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - res := newTelegrafResponses(tt.args.tcs) + ctx := context.Background() + res := newTelegrafResponses(ctx, tt.args.tcs, mock.NewLabelService()) got, err := json.Marshal(res) if err != nil { t.Fatalf("newTelegrafResponses() JSON marshal %v", err) @@ -802,7 +803,7 @@ func Test_newTelegrafResponse(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - res := newTelegrafResponse(tt.args.tc) + res := newTelegrafResponse(tt.args.tc, []*platform.Label{}) got, err := json.Marshal(res) if err != nil { t.Fatalf("newTelegrafResponse() JSON marshal %v", err)