From a053c710c40f218a64370cc982d5701170e0c1a2 Mon Sep 17 00:00:00 2001 From: vlastahajek Date: Tue, 9 Jun 2020 13:48:33 +0200 Subject: [PATCH] feat: Adding Labels API --- CHANGELOG.md | 5 +- README.md | 1 + api/examples_test.go | 46 +++++++++ api/labels.go | 168 ++++++++++++++++++++++++++++++++ api/organizations.go | 69 ++++++++++++- client.go | 14 ++- client_e2e_test.go | 136 ++++++++++++++++++++++++++ go.mod | 1 + internal/examples/fakeclient.go | 4 + 9 files changed, 438 insertions(+), 6 deletions(-) create mode 100644 api/labels.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f522632..7cf6e693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ -## 1.2.0 [2020-05-15] +## 1.3.0 [in progress] +### Features +1. [#131](https://github.com/influxdata/influxdb-client-go/pull/131) Labels API +## 1.2.0 [2020-05-15] ### Breaking Changes - [#107](https://github.com/influxdata/influxdb-client-go/pull/107) Renamed `InfluxDBClient` interface to `Client`, so the full name `influxdb2.Client` suits better to Go naming conventions - [#125](https://github.com/influxdata/influxdb-client-go/pull/125) `WriteApi`,`WriteApiBlocking`,`QueryApi` interfaces and related objects like `Point`, `FluxTableMetadata`, `FluxTableColumn`, `FluxRecord`, moved to the `api` ( and `api/write`, `api/query`) packages diff --git a/README.md b/README.md index fc10f1c6..9a38b8f7 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ This repository contains the reference Go client for InfluxDB 2. - InfluxDB 2 API - setup - ready + - ... ## Documentation diff --git a/api/examples_test.go b/api/examples_test.go index 7af30d29..1a6b94eb 100644 --- a/api/examples_test.go +++ b/api/examples_test.go @@ -323,3 +323,49 @@ func ExampleUsersApi() { // Ensures background processes finishes client.Close() } + +func ExampleLabelsApi() { + // Create influxdb client + client := influxdb2.NewClient("http://localhost:9999", "my-token") + + ctx := context.Background() + // Get Labels API client + labelsApi := client.LabelsApi() + // Get Organizations API client + orgsApi := client.OrganizationsApi() + + // Get organization that will own label + myorg, err := orgsApi.FindOrganizationByName(ctx, "my-org") + if err != nil { + panic(err) + } + + labelName := "Active State" + props := map[string]string{"color": "33ffdd", "description": "Marks org active"} + label, err := labelsApi.CreateLabelWithName(ctx, myorg, labelName, props) + if err != nil { + panic(err) + } + + // Get organization that will have the label + org, err := orgsApi.FindOrganizationByName(ctx, "IT") + if err != nil { + panic(err) + } + + // Add label to org + _, err = orgsApi.AddLabel(ctx, org, label) + if err != nil { + panic(err) + } + + // Change color property + label.Properties.AdditionalProperties = map[string]string{"color": "ff1122"} + label, err = labelsApi.UpdateLabel(ctx, label) + if err != nil { + panic(err) + } + + // Close the client + client.Close() +} diff --git a/api/labels.go b/api/labels.go new file mode 100644 index 00000000..3d127980 --- /dev/null +++ b/api/labels.go @@ -0,0 +1,168 @@ +// Copyright 2020 InfluxData, Inc. All rights reserved. +// Use of this source code is governed by MIT +// license that can be found in the LICENSE file. + +package api + +import ( + "context" + "fmt" + "github.com/influxdata/influxdb-client-go/domain" +) + +// LabelsApi provides methods for managing labels in a InfluxDB server. +type LabelsApi interface { + // GetLabels returns all labels. + GetLabels(ctx context.Context) (*[]domain.Label, error) + // FindLabelsByOrg returns labels belonging to organization org. + FindLabelsByOrg(ctx context.Context, org *domain.Organization) (*[]domain.Label, error) + // FindLabelsByOrgId returns labels belonging to organization with id orgId. + FindLabelsByOrgId(ctx context.Context, orgID string) (*[]domain.Label, error) + // FindLabelById returns a label with labelID. + FindLabelById(ctx context.Context, labelID string) (*domain.Label, error) + // FindLabelByName returns a label with name labelName under an organization orgId. + FindLabelByName(ctx context.Context, orgId, labelName string) (*domain.Label, error) + // CreateLabel creates a new label. + CreateLabel(ctx context.Context, label *domain.LabelCreateRequest) (*domain.Label, error) + // CreateLabelWithName creates a new label with label labelName and properties, under the organization org. + // Properties example: {"color": "ffb3b3", "description": "this is a description"}. + CreateLabelWithName(ctx context.Context, org *domain.Organization, labelName string, properties map[string]string) (*domain.Label, error) + // CreateLabelWithName creates a new label with label labelName and properties, under the organization with id orgId. + // Properties example: {"color": "ffb3b3", "description": "this is a description"}. + CreateLabelWithNameWithId(ctx context.Context, orgId, labelName string, properties map[string]string) (*domain.Label, error) + // UpdateLabel updates the label. + // Properties can be removed by sending an update with an empty value. + UpdateLabel(ctx context.Context, label *domain.Label) (*domain.Label, error) + // DeleteLabelWithId deletes a label with labelId. + DeleteLabelWithId(ctx context.Context, labelID string) error + // DeleteLabel deletes a label. + DeleteLabel(ctx context.Context, label *domain.Label) error +} + +type labelsApiImpl struct { + apiClient *domain.ClientWithResponses +} + +func NewLabelsApi(apiClient *domain.ClientWithResponses) LabelsApi { + return &labelsApiImpl{ + apiClient: apiClient, + } +} + +func (u *labelsApiImpl) GetLabels(ctx context.Context) (*[]domain.Label, error) { + params := &domain.GetLabelsParams{} + return u.getLabels(ctx, params) +} + +func (u *labelsApiImpl) getLabels(ctx context.Context, params *domain.GetLabelsParams) (*[]domain.Label, error) { + response, err := u.apiClient.GetLabelsWithResponse(ctx, params) + if err != nil { + return nil, err + } + if response.JSONDefault != nil { + return nil, domain.DomainErrorToError(response.JSONDefault, response.StatusCode()) + } + return (*[]domain.Label)(response.JSON200.Labels), nil +} + +func (u *labelsApiImpl) FindLabelsByOrg(ctx context.Context, org *domain.Organization) (*[]domain.Label, error) { + return u.FindLabelsByOrgId(ctx, *org.Id) +} + +func (u *labelsApiImpl) FindLabelsByOrgId(ctx context.Context, orgID string) (*[]domain.Label, error) { + params := &domain.GetLabelsParams{OrgID: &orgID} + return u.getLabels(ctx, params) +} + +func (u *labelsApiImpl) FindLabelById(ctx context.Context, labelID string) (*domain.Label, error) { + params := &domain.GetLabelsIDParams{} + response, err := u.apiClient.GetLabelsIDWithResponse(ctx, labelID, params) + if err != nil { + return nil, err + } + if response.JSONDefault != nil { + return nil, domain.DomainErrorToError(response.JSONDefault, response.StatusCode()) + } + return response.JSON200.Label, nil +} + +func (u *labelsApiImpl) FindLabelByName(ctx context.Context, orgId, labelName string) (*domain.Label, error) { + labels, err := u.FindLabelsByOrgId(ctx, orgId) + if err != nil { + return nil, err + } + var label *domain.Label + for _, u := range *labels { + if *u.Name == labelName { + label = &u + break + } + } + if label == nil { + return nil, fmt.Errorf("label '%s' not found", labelName) + } + return label, nil +} + +func (u *labelsApiImpl) CreateLabelWithName(ctx context.Context, org *domain.Organization, labelName string, properties map[string]string) (*domain.Label, error) { + return u.CreateLabelWithNameWithId(ctx, *org.Id, labelName, properties) +} + +func (u *labelsApiImpl) CreateLabelWithNameWithId(ctx context.Context, orgId, labelName string, properties map[string]string) (*domain.Label, error) { + props := &domain.LabelCreateRequest_Properties{AdditionalProperties: properties} + label := &domain.LabelCreateRequest{Name: &labelName, OrgID: orgId, Properties: props} + return u.CreateLabel(ctx, label) +} + +func (u *labelsApiImpl) CreateLabel(ctx context.Context, label *domain.LabelCreateRequest) (*domain.Label, error) { + response, err := u.apiClient.PostLabelsWithResponse(ctx, domain.PostLabelsJSONRequestBody(*label)) + if err != nil { + return nil, err + } + if response.JSONDefault != nil { + return nil, domain.DomainErrorToError(response.JSONDefault, response.StatusCode()) + } + return response.JSON201.Label, nil +} + +func (u *labelsApiImpl) UpdateLabel(ctx context.Context, label *domain.Label) (*domain.Label, error) { + var props *domain.LabelUpdate_Properties + params := &domain.PatchLabelsIDParams{} + if label.Properties != nil { + props = &domain.LabelUpdate_Properties{AdditionalProperties: label.Properties.AdditionalProperties} + } + body := &domain.LabelUpdate{ + Name: label.Name, + Properties: props, + } + response, err := u.apiClient.PatchLabelsIDWithResponse(ctx, *label.Id, params, domain.PatchLabelsIDJSONRequestBody(*body)) + if err != nil { + return nil, err + } + if response.JSON404 != nil { + return nil, domain.DomainErrorToError(response.JSON404, response.StatusCode()) + } + if response.JSONDefault != nil { + return nil, domain.DomainErrorToError(response.JSONDefault, response.StatusCode()) + } + return response.JSON200.Label, nil +} + +func (u *labelsApiImpl) DeleteLabel(ctx context.Context, label *domain.Label) error { + return u.DeleteLabelWithId(ctx, *label.Id) +} + +func (u *labelsApiImpl) DeleteLabelWithId(ctx context.Context, labelID string) error { + params := &domain.DeleteLabelsIDParams{} + response, err := u.apiClient.DeleteLabelsIDWithResponse(ctx, labelID, params) + if err != nil { + return err + } + if response.JSON404 != nil { + return domain.DomainErrorToError(response.JSON404, response.StatusCode()) + } + if response.JSONDefault != nil { + return domain.DomainErrorToError(response.JSONDefault, response.StatusCode()) + } + return nil +} diff --git a/api/organizations.go b/api/organizations.go index 8f2db11b..0e3e5d6e 100644 --- a/api/organizations.go +++ b/api/organizations.go @@ -35,11 +35,11 @@ type OrganizationsApi interface { GetMembersWithId(ctx context.Context, orgId string) (*[]domain.ResourceMember, error) // AddMember adds a member to an organization. AddMember(ctx context.Context, org *domain.Organization, user *domain.User) (*domain.ResourceMember, error) - // AddMember adds a member with id memberId to an organization with orgId. + // AddMemberWithId adds a member with id memberId to an organization with orgId. AddMemberWithId(ctx context.Context, orgId, memberId string) (*domain.ResourceMember, error) // RemoveMember removes a member from an organization. RemoveMember(ctx context.Context, org *domain.Organization, user *domain.User) error - // RemoveMember removes a member with id memberId from an organization with orgId. + // RemoveMemberWithId removes a member with id memberId from an organization with orgId. RemoveMemberWithId(ctx context.Context, orgId, memberId string) error // GetOwners returns owners of an organization. GetOwners(ctx context.Context, org *domain.Organization) (*[]domain.ResourceOwner, error) @@ -47,12 +47,24 @@ type OrganizationsApi interface { GetOwnersWithId(ctx context.Context, orgId string) (*[]domain.ResourceOwner, error) // AddOwner adds an owner to an organization. AddOwner(ctx context.Context, org *domain.Organization, user *domain.User) (*domain.ResourceOwner, error) - // AddOwner adds an owner with id memberId to an organization with orgId. + // AddOwnerWithId adds an owner with id memberId to an organization with orgId. AddOwnerWithId(ctx context.Context, orgId, memberId string) (*domain.ResourceOwner, error) // RemoveOwner removes an owner from an organization. RemoveOwner(ctx context.Context, org *domain.Organization, user *domain.User) error - // RemoveOwner removes an owner with id memberId from an organization with orgId. + // RemoveOwnerWithId removes an owner with id memberId from an organization with orgId. RemoveOwnerWithId(ctx context.Context, orgId, memberId string) error + // GetLabels returns labels of an organization. + GetLabels(ctx context.Context, org *domain.Organization) (*[]domain.Label, error) + // GetLabelsWithId returns labels of an organization with orgId. + GetLabelsWithId(ctx context.Context, orgId string) (*[]domain.Label, error) + // AddLabel adds a label to an organization. + AddLabel(ctx context.Context, org *domain.Organization, label *domain.Label) (*domain.Label, error) + // AddLabelWithId adds a label with id labelId to an organization with orgId. + AddLabelWithId(ctx context.Context, orgId, labelId string) (*domain.Label, error) + // RemoveLabel removes an label from an organization. + RemoveLabel(ctx context.Context, org *domain.Organization, label *domain.Label) error + // RemoveLabelWithId removes an label with id labelId from an organization with orgId. + RemoveLabelWithId(ctx context.Context, orgId, labelId string) error } type organizationsApiImpl struct { @@ -273,3 +285,52 @@ func (o *organizationsApiImpl) RemoveOwnerWithId(ctx context.Context, orgId, mem } return nil } + +func (o *organizationsApiImpl) GetLabels(ctx context.Context, org *domain.Organization) (*[]domain.Label, error) { + return o.GetLabelsWithId(ctx, *org.Id) +} + +func (o *organizationsApiImpl) GetLabelsWithId(ctx context.Context, orgId string) (*[]domain.Label, error) { + params := &domain.GetOrgsIDLabelsParams{} + response, err := o.apiClient.GetOrgsIDLabelsWithResponse(ctx, orgId, params) + if err != nil { + return nil, err + } + if response.JSONDefault != nil { + return nil, domain.DomainErrorToError(response.JSONDefault, response.StatusCode()) + } + return (*[]domain.Label)(response.JSON200.Labels), nil +} + +func (o *organizationsApiImpl) AddLabel(ctx context.Context, org *domain.Organization, label *domain.Label) (*domain.Label, error) { + return o.AddLabelWithId(ctx, *org.Id, *label.Id) +} + +func (o *organizationsApiImpl) AddLabelWithId(ctx context.Context, orgId, labelId string) (*domain.Label, error) { + params := &domain.PostOrgsIDLabelsParams{} + body := &domain.PostOrgsIDLabelsJSONRequestBody{LabelID: &labelId} + response, err := o.apiClient.PostOrgsIDLabelsWithResponse(ctx, orgId, params, *body) + if err != nil { + return nil, err + } + if response.JSONDefault != nil { + return nil, domain.DomainErrorToError(response.JSONDefault, response.StatusCode()) + } + return response.JSON201.Label, nil +} + +func (o *organizationsApiImpl) RemoveLabel(ctx context.Context, org *domain.Organization, label *domain.Label) error { + return o.RemoveLabelWithId(ctx, *org.Id, *label.Id) +} + +func (o *organizationsApiImpl) RemoveLabelWithId(ctx context.Context, orgId, memberId string) error { + params := &domain.DeleteOrgsIDLabelsIDParams{} + response, err := o.apiClient.DeleteOrgsIDLabelsIDWithResponse(ctx, orgId, memberId, params) + if err != nil { + return err + } + if response.JSONDefault != nil { + return domain.DomainErrorToError(response.JSONDefault, response.StatusCode()) + } + return nil +} diff --git a/client.go b/client.go index 838001ef..0f8719c7 100644 --- a/client.go +++ b/client.go @@ -51,8 +51,10 @@ type Client interface { UsersApi() api.UsersApi // DeleteApi returns Delete API client DeleteApi() api.DeleteApi - // BucketsApi returns Delete API client + // BucketsApi returns Buckets API client BucketsApi() api.BucketsApi + // LabelsApi returns Labels API client + LabelsApi() api.LabelsApi } // clientImpl implements Client interface @@ -68,6 +70,7 @@ type clientImpl struct { usersApi api.UsersApi deleteApi api.DeleteApi bucketsApi api.BucketsApi + labelsApi api.LabelsApi } // NewClient creates Client for connecting to given serverUrl with provided authentication token, with the default options. @@ -220,3 +223,12 @@ func (c *clientImpl) BucketsApi() api.BucketsApi { } return c.bucketsApi } + +func (c *clientImpl) LabelsApi() api.LabelsApi { + c.lock.Lock() + defer c.lock.Unlock() + if c.labelsApi == nil { + c.labelsApi = api.NewLabelsApi(c.apiClient) + } + return c.labelsApi +} diff --git a/client_e2e_test.go b/client_e2e_test.go index 937043e2..4030da02 100644 --- a/client_e2e_test.go +++ b/client_e2e_test.go @@ -702,3 +702,139 @@ func TestBuckets(t *testing.T) { _, err = bucketsApi.FindBucketsByOrgName(ctx, org.Name, api.PagingWithLimit(100)) assert.NotNil(t, err) } + +func TestLabels(t *testing.T) { + client := influxdb2.NewClientWithOptions("http://localhost:9999", authToken, influxdb2.DefaultOptions().SetLogLevel(3)) + labelsApi := client.LabelsApi() + orgApi := client.OrganizationsApi() + + ctx := context.Background() + + myorg, err := orgApi.FindOrganizationByName(ctx, "my-org") + require.Nil(t, err, err) + require.NotNil(t, myorg) + + labels, err := labelsApi.GetLabels(ctx) + require.Nil(t, err, err) + require.NotNil(t, labels) + assert.Len(t, *labels, 0) + + labelName := "Active State" + props := map[string]string{"color": "#33ffddd", "description": "Marks org active"} + label, err := labelsApi.CreateLabelWithName(ctx, myorg, labelName, props) + require.Nil(t, err, err) + require.NotNil(t, label) + assert.Equal(t, labelName, *label.Name) + require.NotNil(t, label.Properties) + assert.Equal(t, props, label.Properties.AdditionalProperties) + + //remove properties + label.Properties.AdditionalProperties = map[string]string{"color": "", "description": ""} + label2, err := labelsApi.UpdateLabel(ctx, label) + require.Nil(t, err, err) + require.NotNil(t, label2) + assert.Equal(t, labelName, *label2.Name) + assert.Nil(t, label2.Properties) + + label2, err = labelsApi.FindLabelById(ctx, *label.Id) + require.Nil(t, err, err) + require.NotNil(t, label2) + assert.Equal(t, labelName, *label2.Name) + + label2, err = labelsApi.FindLabelById(ctx, "000000000000000") + require.NotNil(t, err, err) + require.Nil(t, label2) + + label2, err = labelsApi.FindLabelByName(ctx, *myorg.Id, labelName) + require.Nil(t, err, err) + require.NotNil(t, label2) + assert.Equal(t, labelName, *label2.Name) + + label2, err = labelsApi.FindLabelByName(ctx, *myorg.Id, "wrong label") + require.NotNil(t, err, err) + require.Nil(t, label2) + + labels, err = labelsApi.GetLabels(ctx) + require.Nil(t, err, err) + require.NotNil(t, labels) + assert.Len(t, *labels, 1) + + labels, err = labelsApi.FindLabelsByOrg(ctx, myorg) + require.Nil(t, err, err) + require.NotNil(t, labels) + assert.Len(t, *labels, 1) + + labels, err = labelsApi.FindLabelsByOrgId(ctx, *myorg.Id) + require.Nil(t, err, err) + require.NotNil(t, labels) + assert.Len(t, *labels, 1) + + labels, err = labelsApi.FindLabelsByOrgId(ctx, "000000000000000") + require.NotNil(t, err, err) + require.Nil(t, labels) + + // duplicate label + label2, err = labelsApi.CreateLabelWithName(ctx, myorg, labelName, nil) + require.NotNil(t, err, err) + require.Nil(t, label2) + + labels, err = orgApi.GetLabels(ctx, myorg) + require.Nil(t, err, err) + require.NotNil(t, labels) + assert.Len(t, *labels, 0) + + org, err := orgApi.CreateOrganizationWithName(ctx, "org1") + require.Nil(t, err, err) + require.NotNil(t, org) + + labels, err = orgApi.GetLabels(ctx, org) + require.Nil(t, err, err) + require.NotNil(t, labels) + assert.Len(t, *labels, 0) + + labelx, err := orgApi.AddLabel(ctx, org, label) + require.Nil(t, err, err) + require.NotNil(t, labelx) + + labels, err = orgApi.GetLabels(ctx, org) + require.Nil(t, err, err) + require.NotNil(t, labels) + assert.Len(t, *labels, 1) + + err = orgApi.RemoveLabel(ctx, org, label) + require.Nil(t, err, err) + + labels, err = orgApi.GetLabels(ctx, org) + require.Nil(t, err, err) + require.NotNil(t, labels) + assert.Len(t, *labels, 0) + + labels, err = orgApi.GetLabelsWithId(ctx, "000000000000000") + require.NotNil(t, err, err) + require.Nil(t, labels) + + label2, err = orgApi.AddLabelWithId(ctx, *org.Id, "000000000000000") + require.NotNil(t, err, err) + require.Nil(t, label2) + + label2, err = orgApi.AddLabelWithId(ctx, "000000000000000", "000000000000000") + require.NotNil(t, err, err) + require.Nil(t, label2) + + err = orgApi.RemoveLabelWithId(ctx, *org.Id, "000000000000000") + require.NotNil(t, err, err) + require.Nil(t, label2) + + err = orgApi.RemoveLabelWithId(ctx, "000000000000000", "000000000000000") + require.NotNil(t, err, err) + require.Nil(t, label2) + + err = orgApi.DeleteOrganization(ctx, org) + assert.Nil(t, err, err) + + err = labelsApi.DeleteLabel(ctx, label) + require.Nil(t, err, err) + + err = labelsApi.DeleteLabel(ctx, label) + require.NotNil(t, err, err) +} diff --git a/go.mod b/go.mod index b7ffa94c..1827bd1d 100644 --- a/go.mod +++ b/go.mod @@ -7,4 +7,5 @@ require ( github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.4.0 // test dependency + gopkg.in/yaml.v2 v2.2.5 ) diff --git a/internal/examples/fakeclient.go b/internal/examples/fakeclient.go index 7eb0e84f..9d02c655 100644 --- a/internal/examples/fakeclient.go +++ b/internal/examples/fakeclient.go @@ -92,3 +92,7 @@ func (c *fakeClient) DeleteApi() api.DeleteApi { func (c *fakeClient) BucketsApi() api.BucketsApi { return nil } + +func (c *fakeClient) LabelsApi() api.LabelsApi { + return nil +}