Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.21
1.0.22
52 changes: 48 additions & 4 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,50 @@ import (
v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)

// InstanceFilter is the filter for list instances API.
type InstanceFilter struct {
Query string
Environment string
Project string
State v1pb.State
Engines []v1pb.Engine
Host string
Port string
}

// ProjectFilter is the filter for list projects API.
type ProjectFilter struct {
Query string
ExcludeDefault bool
State v1pb.State
}

// Label is the database label.
type Label struct {
Key string
Value string
}

// DatabaseFilter is the filter for list databases API.
type DatabaseFilter struct {
Query string
Environment string
Project string
Instance string
Engines []v1pb.Engine
Labels []*Label
ExcludeUnassigned bool
}

// UserFilter is the filter for list users API.
type UserFilter struct {
Name string
Email string
Project string
UserTypes []v1pb.UserType
State v1pb.State
}

// Client is the API message for Bytebase OpenAPI client.
type Client interface {
// GetCaller returns the API caller.
Expand All @@ -28,7 +72,7 @@ type Client interface {

// Instance
// ListInstance will return instances.
ListInstance(ctx context.Context, showDeleted bool) (*v1pb.ListInstancesResponse, error)
ListInstance(ctx context.Context, filter *InstanceFilter) ([]*v1pb.Instance, error)
// GetInstance gets the instance by full name.
GetInstance(ctx context.Context, instanceName string) (*v1pb.Instance, error)
// CreateInstance creates the instance.
Expand Down Expand Up @@ -56,7 +100,7 @@ type Client interface {
// GetDatabase gets the database by instance resource id and the database name.
GetDatabase(ctx context.Context, databaseName string) (*v1pb.Database, error)
// ListDatabase list the databases.
ListDatabase(ctx context.Context, instanceID, filter string, listAll bool) ([]*v1pb.Database, error)
ListDatabase(ctx context.Context, instanceID string, filter *DatabaseFilter, listAll bool) ([]*v1pb.Database, error)
// UpdateDatabase patches the database.
UpdateDatabase(ctx context.Context, patch *v1pb.Database, updateMasks []string) (*v1pb.Database, error)
// BatchUpdateDatabases batch updates databases.
Expand All @@ -70,7 +114,7 @@ type Client interface {
// GetProject gets the project by project full name.
GetProject(ctx context.Context, projectName string) (*v1pb.Project, error)
// ListProject list all projects,
ListProject(ctx context.Context, showDeleted bool) ([]*v1pb.Project, error)
ListProject(ctx context.Context, filter *ProjectFilter) ([]*v1pb.Project, error)
// CreateProject creates the project.
CreateProject(ctx context.Context, projectID string, project *v1pb.Project) (*v1pb.Project, error)
// UpdateProject updates the project.
Expand Down Expand Up @@ -98,7 +142,7 @@ type Client interface {

// User
// ListUser list all users.
ListUser(ctx context.Context, showDeleted bool) ([]*v1pb.User, error)
ListUser(ctx context.Context, filter *UserFilter) ([]*v1pb.User, error)
// CreateUser creates the user.
CreateUser(ctx context.Context, user *v1pb.User) (*v1pb.User, error)
// GetUser gets the user by name.
Expand Down
52 changes: 48 additions & 4 deletions client/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
v1pb "github.com/bytebase/bytebase/proto/generated-go/v1"
"github.com/hashicorp/terraform-plugin-log/tflog"
"google.golang.org/protobuf/encoding/protojson"

"github.com/bytebase/terraform-provider-bytebase/api"
)

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

func buildDatabaseQuery(filter *api.DatabaseFilter) string {
params := []string{}

if v := filter.Query; v != "" {
params = append(params, fmt.Sprintf(`name.matches("%s")`, strings.ToLower(v)))
}
if v := filter.Environment; v != "" {
params = append(params, fmt.Sprintf(`environment == "%s"`, v))
}
if v := filter.Project; v != "" {
params = append(params, fmt.Sprintf(`project == "%s"`, v))
}
if v := filter.Instance; v != "" {
params = append(params, fmt.Sprintf(`instance == "%s"`, v))
}
if filter.ExcludeUnassigned {
params = append(params, "exclude_unassigned == true")
}
if v := filter.Engines; len(v) > 0 {
engines := []string{}
for _, e := range v {
engines = append(engines, fmt.Sprintf(`"%s"`, e.String()))
}
params = append(params, fmt.Sprintf(`engine in [%s]`, strings.Join(engines, ", ")))
}
if v := filter.Labels; len(v) > 0 {
labelMap := map[string][]string{}
for _, label := range v {
if _, ok := labelMap[label.Key]; !ok {
labelMap[label.Key] = []string{}
}
labelMap[label.Key] = append(labelMap[label.Key], label.Value)
}
for key, values := range labelMap {
params = append(params, fmt.Sprintf(`label == "%s:%s"`, key, strings.Join(values, ",")))
}
}

return fmt.Sprintf("filter=%s", url.QueryEscape(strings.Join(params, " && ")))
}

// ListDatabase list all databases.
func (c *client) ListDatabase(ctx context.Context, parent, filter string, listAll bool) ([]*v1pb.Database, error) {
func (c *client) ListDatabase(ctx context.Context, parent string, filter *api.DatabaseFilter, listAll bool) ([]*v1pb.Database, error) {
res := []*v1pb.Database{}
pageToken := ""
startTime := time.Now()
query := buildDatabaseQuery(filter)

for {
startTimePerPage := time.Now()
resp, err := c.listDatabasePerPage(ctx, parent, filter, pageToken, 500)
resp, err := c.listDatabasePerPage(ctx, parent, query, pageToken, 500)
if err != nil {
return nil, err
}
Expand All @@ -63,11 +107,11 @@ func (c *client) ListDatabase(ctx context.Context, parent, filter string, listAl
// listDatabasePerPage list the databases.
func (c *client) listDatabasePerPage(ctx context.Context, parent, filter, pageToken string, pageSize int) (*v1pb.ListDatabasesResponse, error) {
requestURL := fmt.Sprintf(
"%s/%s/%s/databases?filter=%s&page_size=%d&page_token=%s",
"%s/%s/%s/databases?%s&page_size=%d&page_token=%s",
c.url,
c.version,
parent,
url.QueryEscape(filter),
filter,
pageSize,
url.QueryEscape(pageToken),
)
Expand Down
88 changes: 86 additions & 2 deletions client/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,99 @@ import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
"time"

v1pb "github.com/bytebase/bytebase/proto/generated-go/v1"
"github.com/hashicorp/terraform-plugin-log/tflog"
"google.golang.org/protobuf/encoding/protojson"

"github.com/bytebase/terraform-provider-bytebase/api"
)

func buildInstanceQuery(filter *api.InstanceFilter) string {
params := []string{}
showDeleted := v1pb.State_DELETED == filter.State

if v := filter.Query; v != "" {
params = append(params, fmt.Sprintf(`(name.matches("%s") || resource_id.matches("%s"))`, strings.ToLower(v), strings.ToLower(v)))
}
if v := filter.Project; v != "" {
params = append(params, fmt.Sprintf(`project == "%s"`, v))
}
if v := filter.Environment; v != "" {
params = append(params, fmt.Sprintf(`environment == "%s"`, v))
}
if v := filter.Host; v != "" {
params = append(params, fmt.Sprintf(`host == "%s"`, v))
}
if v := filter.Port; v != "" {
params = append(params, fmt.Sprintf(`port == "%s"`, v))
}
if v := filter.Engines; len(v) > 0 {
engines := []string{}
for _, e := range v {
engines = append(engines, fmt.Sprintf(`"%s"`, e.String()))
}
params = append(params, fmt.Sprintf(`engine in [%s]`, strings.Join(engines, ", ")))
}
if showDeleted {
params = append(params, fmt.Sprintf(`state == "%s"`, filter.State.String()))
}

if len(params) == 0 {
return fmt.Sprintf("showDeleted=%v", showDeleted)
}

return fmt.Sprintf("filter=%s&showDeleted=%v", url.QueryEscape(strings.Join(params, " && ")), showDeleted)
}

// ListInstance will return instances.
func (c *client) ListInstance(ctx context.Context, showDeleted bool) (*v1pb.ListInstancesResponse, error) {
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/%s/instances?showDeleted=%v", c.url, c.version, showDeleted), nil)
func (c *client) ListInstance(ctx context.Context, filter *api.InstanceFilter) ([]*v1pb.Instance, error) {
res := []*v1pb.Instance{}
pageToken := ""
startTime := time.Now()
query := buildInstanceQuery(filter)

for {
startTimePerPage := time.Now()
resp, err := c.listInstancePerPage(ctx, query, pageToken, 500)
if err != nil {
return nil, err
}
res = append(res, resp.Instances...)
tflog.Debug(ctx, "[list instance per page]", map[string]interface{}{
"count": len(resp.Instances),
"ms": time.Since(startTimePerPage).Milliseconds(),
})

pageToken = resp.NextPageToken
if pageToken == "" {
break
}
}

tflog.Debug(ctx, "[list instance]", map[string]interface{}{
"total": len(res),
"ms": time.Since(startTime).Milliseconds(),
})

return res, nil
}

// listInstancePerPage list the instance.
func (c *client) listInstancePerPage(ctx context.Context, query, pageToken string, pageSize int) (*v1pb.ListInstancesResponse, error) {
requestURL := fmt.Sprintf(
"%s/%s/instances?%s&page_size=%d&page_token=%s",
c.url,
c.version,
query,
pageSize,
url.QueryEscape(pageToken),
)

req, err := http.NewRequestWithContext(ctx, "GET", requestURL, nil)
if err != nil {
return nil, err
}
Expand Down
34 changes: 29 additions & 5 deletions client/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
v1pb "github.com/bytebase/bytebase/proto/generated-go/v1"
"github.com/hashicorp/terraform-plugin-log/tflog"
"google.golang.org/protobuf/encoding/protojson"

"github.com/bytebase/terraform-provider-bytebase/api"
)

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

func buildProjectQuery(filter *api.ProjectFilter) string {
params := []string{}
showDeleted := v1pb.State_DELETED == filter.State

if v := filter.Query; v != "" {
params = append(params, fmt.Sprintf(`(name.matches("%s") || resource_id.matches("%s"))`, strings.ToLower(v), strings.ToLower(v)))
}
if filter.ExcludeDefault {
params = append(params, "exclude_default == true")
}
if showDeleted {
params = append(params, fmt.Sprintf(`state == "%s"`, filter.State.String()))
}

if len(params) == 0 {
return fmt.Sprintf("showDeleted=%v", showDeleted)
}

return fmt.Sprintf("filter=%s&showDeleted=%v", url.QueryEscape(strings.Join(params, " && ")), showDeleted)
}

// ListProject list all projects.
func (c *client) ListProject(ctx context.Context, showDeleted bool) ([]*v1pb.Project, error) {
func (c *client) ListProject(ctx context.Context, filter *api.ProjectFilter) ([]*v1pb.Project, error) {
res := []*v1pb.Project{}
pageToken := ""
startTime := time.Now()
query := buildProjectQuery(filter)

for {
startTimePerPage := time.Now()
resp, err := c.listProjectPerPage(ctx, showDeleted, pageToken, 500)
resp, err := c.listProjectPerPage(ctx, query, pageToken, 500)
if err != nil {
return nil, err
}
Expand All @@ -102,12 +126,12 @@ func (c *client) ListProject(ctx context.Context, showDeleted bool) ([]*v1pb.Pro
}

// listProjectPerPage list the projects.
func (c *client) listProjectPerPage(ctx context.Context, showDeleted bool, pageToken string, pageSize int) (*v1pb.ListProjectsResponse, error) {
func (c *client) listProjectPerPage(ctx context.Context, query, pageToken string, pageSize int) (*v1pb.ListProjectsResponse, error) {
requestURL := fmt.Sprintf(
"%s/%s/projects?showDeleted=%v&page_size=%d&page_token=%s",
"%s/%s/projects?%s&page_size=%d&page_token=%s",
c.url,
c.version,
showDeleted,
query,
pageSize,
url.QueryEscape(pageToken),
)
Expand Down
Loading