Skip to content

Commit f59e7f2

Browse files
ecmadaod-bytebase
andauthored
feat: support filter for list databases/projects/instances/users (#104)
* feat: support filter for list databases/projects/instances/users * fix: lint * Update api/client.go --------- Co-authored-by: Danny Xu <[email protected]>
1 parent dc311ee commit f59e7f2

30 files changed

+577
-76
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.21
1+
1.0.22

api/client.go

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,50 @@ import (
77
v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
88
)
99

10+
// InstanceFilter is the filter for list instances API.
11+
type InstanceFilter struct {
12+
Query string
13+
Environment string
14+
Project string
15+
State v1pb.State
16+
Engines []v1pb.Engine
17+
Host string
18+
Port string
19+
}
20+
21+
// ProjectFilter is the filter for list projects API.
22+
type ProjectFilter struct {
23+
Query string
24+
ExcludeDefault bool
25+
State v1pb.State
26+
}
27+
28+
// Label is the database label.
29+
type Label struct {
30+
Key string
31+
Value string
32+
}
33+
34+
// DatabaseFilter is the filter for list databases API.
35+
type DatabaseFilter struct {
36+
Query string
37+
Environment string
38+
Project string
39+
Instance string
40+
Engines []v1pb.Engine
41+
Labels []*Label
42+
ExcludeUnassigned bool
43+
}
44+
45+
// UserFilter is the filter for list users API.
46+
type UserFilter struct {
47+
Name string
48+
Email string
49+
Project string
50+
UserTypes []v1pb.UserType
51+
State v1pb.State
52+
}
53+
1054
// Client is the API message for Bytebase OpenAPI client.
1155
type Client interface {
1256
// GetCaller returns the API caller.
@@ -28,7 +72,7 @@ type Client interface {
2872

2973
// Instance
3074
// ListInstance will return instances.
31-
ListInstance(ctx context.Context, showDeleted bool) (*v1pb.ListInstancesResponse, error)
75+
ListInstance(ctx context.Context, filter *InstanceFilter) ([]*v1pb.Instance, error)
3276
// GetInstance gets the instance by full name.
3377
GetInstance(ctx context.Context, instanceName string) (*v1pb.Instance, error)
3478
// CreateInstance creates the instance.
@@ -56,7 +100,7 @@ type Client interface {
56100
// GetDatabase gets the database by instance resource id and the database name.
57101
GetDatabase(ctx context.Context, databaseName string) (*v1pb.Database, error)
58102
// ListDatabase list the databases.
59-
ListDatabase(ctx context.Context, instanceID, filter string, listAll bool) ([]*v1pb.Database, error)
103+
ListDatabase(ctx context.Context, instanceID string, filter *DatabaseFilter, listAll bool) ([]*v1pb.Database, error)
60104
// UpdateDatabase patches the database.
61105
UpdateDatabase(ctx context.Context, patch *v1pb.Database, updateMasks []string) (*v1pb.Database, error)
62106
// BatchUpdateDatabases batch updates databases.
@@ -70,7 +114,7 @@ type Client interface {
70114
// GetProject gets the project by project full name.
71115
GetProject(ctx context.Context, projectName string) (*v1pb.Project, error)
72116
// ListProject list all projects,
73-
ListProject(ctx context.Context, showDeleted bool) ([]*v1pb.Project, error)
117+
ListProject(ctx context.Context, filter *ProjectFilter) ([]*v1pb.Project, error)
74118
// CreateProject creates the project.
75119
CreateProject(ctx context.Context, projectID string, project *v1pb.Project) (*v1pb.Project, error)
76120
// UpdateProject updates the project.
@@ -98,7 +142,7 @@ type Client interface {
98142

99143
// User
100144
// ListUser list all users.
101-
ListUser(ctx context.Context, showDeleted bool) ([]*v1pb.User, error)
145+
ListUser(ctx context.Context, filter *UserFilter) ([]*v1pb.User, error)
102146
// CreateUser creates the user.
103147
CreateUser(ctx context.Context, user *v1pb.User) (*v1pb.User, error)
104148
// GetUser gets the user by name.

client/database.go

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
v1pb "github.com/bytebase/bytebase/proto/generated-go/v1"
1212
"github.com/hashicorp/terraform-plugin-log/tflog"
1313
"google.golang.org/protobuf/encoding/protojson"
14+
15+
"github.com/bytebase/terraform-provider-bytebase/api"
1416
)
1517

1618
// GetDatabase gets the database by the database full name.
@@ -28,15 +30,57 @@ func (c *client) GetDatabase(ctx context.Context, databaseName string) (*v1pb.Da
2830
return &res, nil
2931
}
3032

33+
func buildDatabaseQuery(filter *api.DatabaseFilter) string {
34+
params := []string{}
35+
36+
if v := filter.Query; v != "" {
37+
params = append(params, fmt.Sprintf(`name.matches("%s")`, strings.ToLower(v)))
38+
}
39+
if v := filter.Environment; v != "" {
40+
params = append(params, fmt.Sprintf(`environment == "%s"`, v))
41+
}
42+
if v := filter.Project; v != "" {
43+
params = append(params, fmt.Sprintf(`project == "%s"`, v))
44+
}
45+
if v := filter.Instance; v != "" {
46+
params = append(params, fmt.Sprintf(`instance == "%s"`, v))
47+
}
48+
if filter.ExcludeUnassigned {
49+
params = append(params, "exclude_unassigned == true")
50+
}
51+
if v := filter.Engines; len(v) > 0 {
52+
engines := []string{}
53+
for _, e := range v {
54+
engines = append(engines, fmt.Sprintf(`"%s"`, e.String()))
55+
}
56+
params = append(params, fmt.Sprintf(`engine in [%s]`, strings.Join(engines, ", ")))
57+
}
58+
if v := filter.Labels; len(v) > 0 {
59+
labelMap := map[string][]string{}
60+
for _, label := range v {
61+
if _, ok := labelMap[label.Key]; !ok {
62+
labelMap[label.Key] = []string{}
63+
}
64+
labelMap[label.Key] = append(labelMap[label.Key], label.Value)
65+
}
66+
for key, values := range labelMap {
67+
params = append(params, fmt.Sprintf(`label == "%s:%s"`, key, strings.Join(values, ",")))
68+
}
69+
}
70+
71+
return fmt.Sprintf("filter=%s", url.QueryEscape(strings.Join(params, " && ")))
72+
}
73+
3174
// ListDatabase list all databases.
32-
func (c *client) ListDatabase(ctx context.Context, parent, filter string, listAll bool) ([]*v1pb.Database, error) {
75+
func (c *client) ListDatabase(ctx context.Context, parent string, filter *api.DatabaseFilter, listAll bool) ([]*v1pb.Database, error) {
3376
res := []*v1pb.Database{}
3477
pageToken := ""
3578
startTime := time.Now()
79+
query := buildDatabaseQuery(filter)
3680

3781
for {
3882
startTimePerPage := time.Now()
39-
resp, err := c.listDatabasePerPage(ctx, parent, filter, pageToken, 500)
83+
resp, err := c.listDatabasePerPage(ctx, parent, query, pageToken, 500)
4084
if err != nil {
4185
return nil, err
4286
}
@@ -63,11 +107,11 @@ func (c *client) ListDatabase(ctx context.Context, parent, filter string, listAl
63107
// listDatabasePerPage list the databases.
64108
func (c *client) listDatabasePerPage(ctx context.Context, parent, filter, pageToken string, pageSize int) (*v1pb.ListDatabasesResponse, error) {
65109
requestURL := fmt.Sprintf(
66-
"%s/%s/%s/databases?filter=%s&page_size=%d&page_token=%s",
110+
"%s/%s/%s/databases?%s&page_size=%d&page_token=%s",
67111
c.url,
68112
c.version,
69113
parent,
70-
url.QueryEscape(filter),
114+
filter,
71115
pageSize,
72116
url.QueryEscape(pageToken),
73117
)

client/instance.go

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,99 @@ import (
44
"context"
55
"fmt"
66
"net/http"
7+
"net/url"
78
"strings"
9+
"time"
810

911
v1pb "github.com/bytebase/bytebase/proto/generated-go/v1"
12+
"github.com/hashicorp/terraform-plugin-log/tflog"
1013
"google.golang.org/protobuf/encoding/protojson"
14+
15+
"github.com/bytebase/terraform-provider-bytebase/api"
1116
)
1217

18+
func buildInstanceQuery(filter *api.InstanceFilter) string {
19+
params := []string{}
20+
showDeleted := v1pb.State_DELETED == filter.State
21+
22+
if v := filter.Query; v != "" {
23+
params = append(params, fmt.Sprintf(`(name.matches("%s") || resource_id.matches("%s"))`, strings.ToLower(v), strings.ToLower(v)))
24+
}
25+
if v := filter.Project; v != "" {
26+
params = append(params, fmt.Sprintf(`project == "%s"`, v))
27+
}
28+
if v := filter.Environment; v != "" {
29+
params = append(params, fmt.Sprintf(`environment == "%s"`, v))
30+
}
31+
if v := filter.Host; v != "" {
32+
params = append(params, fmt.Sprintf(`host == "%s"`, v))
33+
}
34+
if v := filter.Port; v != "" {
35+
params = append(params, fmt.Sprintf(`port == "%s"`, v))
36+
}
37+
if v := filter.Engines; len(v) > 0 {
38+
engines := []string{}
39+
for _, e := range v {
40+
engines = append(engines, fmt.Sprintf(`"%s"`, e.String()))
41+
}
42+
params = append(params, fmt.Sprintf(`engine in [%s]`, strings.Join(engines, ", ")))
43+
}
44+
if showDeleted {
45+
params = append(params, fmt.Sprintf(`state == "%s"`, filter.State.String()))
46+
}
47+
48+
if len(params) == 0 {
49+
return fmt.Sprintf("showDeleted=%v", showDeleted)
50+
}
51+
52+
return fmt.Sprintf("filter=%s&showDeleted=%v", url.QueryEscape(strings.Join(params, " && ")), showDeleted)
53+
}
54+
1355
// ListInstance will return instances.
14-
func (c *client) ListInstance(ctx context.Context, showDeleted bool) (*v1pb.ListInstancesResponse, error) {
15-
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/%s/instances?showDeleted=%v", c.url, c.version, showDeleted), nil)
56+
func (c *client) ListInstance(ctx context.Context, filter *api.InstanceFilter) ([]*v1pb.Instance, error) {
57+
res := []*v1pb.Instance{}
58+
pageToken := ""
59+
startTime := time.Now()
60+
query := buildInstanceQuery(filter)
61+
62+
for {
63+
startTimePerPage := time.Now()
64+
resp, err := c.listInstancePerPage(ctx, query, pageToken, 500)
65+
if err != nil {
66+
return nil, err
67+
}
68+
res = append(res, resp.Instances...)
69+
tflog.Debug(ctx, "[list instance per page]", map[string]interface{}{
70+
"count": len(resp.Instances),
71+
"ms": time.Since(startTimePerPage).Milliseconds(),
72+
})
73+
74+
pageToken = resp.NextPageToken
75+
if pageToken == "" {
76+
break
77+
}
78+
}
79+
80+
tflog.Debug(ctx, "[list instance]", map[string]interface{}{
81+
"total": len(res),
82+
"ms": time.Since(startTime).Milliseconds(),
83+
})
84+
85+
return res, nil
86+
}
87+
88+
// listInstancePerPage list the instance.
89+
func (c *client) listInstancePerPage(ctx context.Context, query, pageToken string, pageSize int) (*v1pb.ListInstancesResponse, error) {
90+
requestURL := fmt.Sprintf(
91+
"%s/%s/instances?%s&page_size=%d&page_token=%s",
92+
c.url,
93+
c.version,
94+
query,
95+
pageSize,
96+
url.QueryEscape(pageToken),
97+
)
98+
99+
req, err := http.NewRequestWithContext(ctx, "GET", requestURL, nil)
16100
if err != nil {
17101
return nil, err
18102
}

client/project.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
v1pb "github.com/bytebase/bytebase/proto/generated-go/v1"
1212
"github.com/hashicorp/terraform-plugin-log/tflog"
1313
"google.golang.org/protobuf/encoding/protojson"
14+
15+
"github.com/bytebase/terraform-provider-bytebase/api"
1416
)
1517

1618
// GetProject gets the project by project full name.
@@ -69,15 +71,37 @@ func (c *client) SetProjectIAMPolicy(ctx context.Context, projectName string, up
6971
return &res, nil
7072
}
7173

74+
func buildProjectQuery(filter *api.ProjectFilter) string {
75+
params := []string{}
76+
showDeleted := v1pb.State_DELETED == filter.State
77+
78+
if v := filter.Query; v != "" {
79+
params = append(params, fmt.Sprintf(`(name.matches("%s") || resource_id.matches("%s"))`, strings.ToLower(v), strings.ToLower(v)))
80+
}
81+
if filter.ExcludeDefault {
82+
params = append(params, "exclude_default == true")
83+
}
84+
if showDeleted {
85+
params = append(params, fmt.Sprintf(`state == "%s"`, filter.State.String()))
86+
}
87+
88+
if len(params) == 0 {
89+
return fmt.Sprintf("showDeleted=%v", showDeleted)
90+
}
91+
92+
return fmt.Sprintf("filter=%s&showDeleted=%v", url.QueryEscape(strings.Join(params, " && ")), showDeleted)
93+
}
94+
7295
// ListProject list all projects.
73-
func (c *client) ListProject(ctx context.Context, showDeleted bool) ([]*v1pb.Project, error) {
96+
func (c *client) ListProject(ctx context.Context, filter *api.ProjectFilter) ([]*v1pb.Project, error) {
7497
res := []*v1pb.Project{}
7598
pageToken := ""
7699
startTime := time.Now()
100+
query := buildProjectQuery(filter)
77101

78102
for {
79103
startTimePerPage := time.Now()
80-
resp, err := c.listProjectPerPage(ctx, showDeleted, pageToken, 500)
104+
resp, err := c.listProjectPerPage(ctx, query, pageToken, 500)
81105
if err != nil {
82106
return nil, err
83107
}
@@ -102,12 +126,12 @@ func (c *client) ListProject(ctx context.Context, showDeleted bool) ([]*v1pb.Pro
102126
}
103127

104128
// listProjectPerPage list the projects.
105-
func (c *client) listProjectPerPage(ctx context.Context, showDeleted bool, pageToken string, pageSize int) (*v1pb.ListProjectsResponse, error) {
129+
func (c *client) listProjectPerPage(ctx context.Context, query, pageToken string, pageSize int) (*v1pb.ListProjectsResponse, error) {
106130
requestURL := fmt.Sprintf(
107-
"%s/%s/projects?showDeleted=%v&page_size=%d&page_token=%s",
131+
"%s/%s/projects?%s&page_size=%d&page_token=%s",
108132
c.url,
109133
c.version,
110-
showDeleted,
134+
query,
111135
pageSize,
112136
url.QueryEscape(pageToken),
113137
)

0 commit comments

Comments
 (0)