diff --git a/.golangci.yaml b/.golangci.yaml index 27ee1e4..25a3c2e 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -77,6 +77,8 @@ linters-settings: disabled: true - name: unchecked-type-assertion disabled: true + - name: redundant-import-alias + disabled: true gocritic: disabled-checks: - ifElseChain diff --git a/VERSION b/VERSION index e6d5cb8..e4c0d46 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.2 \ No newline at end of file +1.0.3 \ No newline at end of file diff --git a/api/client.go b/api/client.go index 32fa5b7..146cec6 100644 --- a/api/client.go +++ b/api/client.go @@ -3,7 +3,8 @@ package api import ( "context" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" + v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) // Client is the API message for Bytebase OpenAPI client. @@ -73,4 +74,16 @@ type Client interface { DeleteProject(ctx context.Context, projectName string) error // UndeleteProject undeletes the project. UndeleteProject(ctx context.Context, projectName string) (*v1pb.Project, error) + + // Setting + // ListSettings lists all settings. + ListSettings(ctx context.Context) (*v1pb.ListSettingsResponse, error) + // GetSetting gets the setting by the name. + GetSetting(ctx context.Context, settingName string) (*v1pb.Setting, error) + // UpsertSetting updates or creates the setting. + UpsertSetting(ctx context.Context, upsert *v1pb.Setting, updateMasks []string) (*v1pb.Setting, error) + + // Cel + // ParseExpression parse the expression string. + ParseExpression(ctx context.Context, expression string) (*v1alpha1.Expr, error) } diff --git a/api/setting.go b/api/setting.go new file mode 100644 index 0000000..09b82cb --- /dev/null +++ b/api/setting.go @@ -0,0 +1,51 @@ +package api + +// SettingName is the Bytebase setting name without settings/ prefix. +type SettingName string + +const ( + // SettingWorkspaceApproval is the setting name for workspace approval config. + SettingWorkspaceApproval SettingName = "bb.workspace.approval" + // SettingWorkspaceExternalApproval is the setting name for workspace external approval config. + SettingWorkspaceExternalApproval SettingName = "bb.workspace.approval.external" +) + +// RiskLevel is the approval risk level. +type RiskLevel string + +const ( + // RiskLevelDefault is the default risk level, the level number should be 0. + RiskLevelDefault RiskLevel = "DEFAULT" + // RiskLevelLow is the low risk level, the level number should be 100. + RiskLevelLow RiskLevel = "LOW" + // RiskLevelModerate is the moderate risk level, the level number should be 200. + RiskLevelModerate RiskLevel = "MODERATE" + // RiskLevelHigh is the high risk level, the level number should be 300. + RiskLevelHigh RiskLevel = "HIGH" +) + +// Int returns the int value for risk. +func (r RiskLevel) Int() int { + switch r { + case RiskLevelLow: + return 100 + case RiskLevelModerate: + return 200 + case RiskLevelHigh: + return 300 + default: + return 0 + } +} + +// ApprovalNodeType is the type for approval node. +type ApprovalNodeType string + +const ( + // ApprovalNodeTypeGroup means the approval node is a group. + ApprovalNodeTypeGroup ApprovalNodeType = "GROUP" + // ApprovalNodeTypeRole means the approval node is a role, the value should be role fullname. + ApprovalNodeTypeRole ApprovalNodeType = "ROLE" + // ApprovalNodeTypeExternalNodeID means the approval node is a external node, the value should be the node id. + ApprovalNodeTypeExternalNodeID ApprovalNodeType = "EXTERNAL_NODE" +) diff --git a/client/auth.go b/client/auth.go index 31b7fed..a7a80e9 100644 --- a/client/auth.go +++ b/client/auth.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" "google.golang.org/protobuf/encoding/protojson" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" ) // Login will login the user and get the response. diff --git a/client/cel.go b/client/cel.go new file mode 100644 index 0000000..fe4d63f --- /dev/null +++ b/client/cel.go @@ -0,0 +1,48 @@ +package client + +import ( + "context" + "fmt" + "net/http" + "strings" + + "github.com/hashicorp/terraform-plugin-log/tflog" + + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" + "github.com/pkg/errors" + v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + "google.golang.org/protobuf/encoding/protojson" +) + +// ParseExpression parse the expression string. +func (c *client) ParseExpression(ctx context.Context, expression string) (*v1alpha1.Expr, error) { + payload, err := protojson.Marshal(&v1pb.BatchParseRequest{ + Expressions: []string{expression}, + }) + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/%s/cel/batchParse", c.url, c.version), strings.NewReader(string(payload))) + if err != nil { + return nil, err + } + + body, err := c.doRequest(req) + if err != nil { + return nil, err + } + + tflog.Debug(ctx, fmt.Sprintf("parse cel response:\n%v", string(body))) + + var res v1pb.BatchParseResponse + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { + return nil, err + } + + if len(res.Expressions) != 1 { + return nil, errors.Errorf("failed to parse the cel: %v", expression) + } + + return res.GetExpressions()[0], nil +} diff --git a/client/client.go b/client/client.go index 208fbeb..8eb04a3 100644 --- a/client/client.go +++ b/client/client.go @@ -9,7 +9,7 @@ import ( "github.com/pkg/errors" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" "github.com/bytebase/terraform-provider-bytebase/api" ) diff --git a/client/database.go b/client/database.go index 6788eb4..05f090c 100644 --- a/client/database.go +++ b/client/database.go @@ -7,7 +7,7 @@ import ( "net/url" "strings" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" "google.golang.org/protobuf/encoding/protojson" ) @@ -24,8 +24,7 @@ func (c *client) GetDatabase(ctx context.Context, databaseName string) (*v1pb.Da } var res v1pb.Database - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } @@ -50,8 +49,7 @@ func (c *client) ListDatabase(ctx context.Context, instanceID, filter string) (* } var res v1pb.ListDatabasesResponse - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } @@ -65,14 +63,6 @@ func (c *client) UpdateDatabase(ctx context.Context, patch *v1pb.Database, updat return nil, err } - // updateMask := []string{} - // if patch.Project != nil { - // updateMask = append(updateMask, "project") - // } - // if patch.Labels != nil { - // updateMask = append(updateMask, "labels") - // } - req, err := http.NewRequestWithContext(ctx, "PATCH", fmt.Sprintf("%s/%s/%s?update_mask=%s", c.url, c.version, patch.Name, strings.Join(updateMasks, ",")), strings.NewReader(string(payload))) if err != nil { return nil, err @@ -84,8 +74,7 @@ func (c *client) UpdateDatabase(ctx context.Context, patch *v1pb.Database, updat } var res v1pb.Database - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } diff --git a/client/environment.go b/client/environment.go index b2478fb..55a13be 100644 --- a/client/environment.go +++ b/client/environment.go @@ -6,7 +6,7 @@ import ( "net/http" "strings" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" "google.golang.org/protobuf/encoding/protojson" ) @@ -28,8 +28,7 @@ func (c *client) CreateEnvironment(ctx context.Context, environmentID string, cr } var env v1pb.Environment - err = ProtojsonUnmarshaler.Unmarshal(body, &env) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &env); err != nil { return nil, err } @@ -49,8 +48,7 @@ func (c *client) GetEnvironment(ctx context.Context, environmentName string) (*v } var env v1pb.Environment - err = ProtojsonUnmarshaler.Unmarshal(body, &env) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &env); err != nil { return nil, err } @@ -70,8 +68,7 @@ func (c *client) ListEnvironment(ctx context.Context, showDeleted bool) (*v1pb.L } var res v1pb.ListEnvironmentsResponse - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } @@ -96,8 +93,7 @@ func (c *client) UpdateEnvironment(ctx context.Context, patch *v1pb.Environment, } var env v1pb.Environment - err = ProtojsonUnmarshaler.Unmarshal(body, &env) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &env); err != nil { return nil, err } @@ -130,8 +126,7 @@ func (c *client) UndeleteEnvironment(ctx context.Context, environmentName string } var res v1pb.Environment - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } diff --git a/client/instance.go b/client/instance.go index 569fbe5..215baf6 100644 --- a/client/instance.go +++ b/client/instance.go @@ -6,7 +6,7 @@ import ( "net/http" "strings" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" "google.golang.org/protobuf/encoding/protojson" ) @@ -23,8 +23,7 @@ func (c *client) ListInstance(ctx context.Context, showDeleted bool) (*v1pb.List } var res v1pb.ListInstancesResponse - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } @@ -44,8 +43,7 @@ func (c *client) GetInstance(ctx context.Context, instanceName string) (*v1pb.In } var res v1pb.Instance - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } @@ -71,8 +69,7 @@ func (c *client) CreateInstance(ctx context.Context, instanceID string, instance } var res v1pb.Instance - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } @@ -98,8 +95,7 @@ func (c *client) UpdateInstance(ctx context.Context, patch *v1pb.Instance, updat } var res v1pb.Instance - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } @@ -132,8 +128,7 @@ func (c *client) UndeleteInstance(ctx context.Context, instanceName string) (*v1 } var res v1pb.Instance - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } diff --git a/client/policy.go b/client/policy.go index 72b6f77..a84a89e 100644 --- a/client/policy.go +++ b/client/policy.go @@ -35,8 +35,7 @@ func (c *client) ListPolicies(ctx context.Context, find *api.PolicyFindMessage) } var res api.ListPolicyMessage - err = json.Unmarshal(body, &res) - if err != nil { + if err := json.Unmarshal(body, &res); err != nil { return nil, err } @@ -56,8 +55,7 @@ func (c *client) GetPolicy(ctx context.Context, policyName string) (*api.PolicyM } var res api.PolicyMessage - err = json.Unmarshal(body, &res) - if err != nil { + if err := json.Unmarshal(body, &res); err != nil { return nil, err } @@ -93,8 +91,7 @@ func (c *client) UpsertPolicy(ctx context.Context, patch *api.PolicyPatchMessage } var res api.PolicyMessage - err = json.Unmarshal(body, &res) - if err != nil { + if err := json.Unmarshal(body, &res); err != nil { return nil, err } diff --git a/client/project.go b/client/project.go index dcb21f8..278d750 100644 --- a/client/project.go +++ b/client/project.go @@ -6,7 +6,7 @@ import ( "net/http" "strings" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" "google.golang.org/protobuf/encoding/protojson" ) @@ -23,8 +23,7 @@ func (c *client) GetProject(ctx context.Context, projectName string) (*v1pb.Proj } var res v1pb.Project - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } @@ -44,8 +43,7 @@ func (c *client) ListProject(ctx context.Context, showDeleted bool) (*v1pb.ListP } var res v1pb.ListProjectsResponse - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } @@ -71,8 +69,7 @@ func (c *client) CreateProject(ctx context.Context, projectID string, project *v } var res v1pb.Project - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } @@ -98,8 +95,7 @@ func (c *client) UpdateProject(ctx context.Context, patch *v1pb.Project, updateM } var res v1pb.Project - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } @@ -132,8 +128,7 @@ func (c *client) UndeleteProject(ctx context.Context, projectName string) (*v1pb } var res v1pb.Project - err = ProtojsonUnmarshaler.Unmarshal(body, &res) - if err != nil { + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { return nil, err } diff --git a/client/setting.go b/client/setting.go new file mode 100644 index 0000000..96fd47e --- /dev/null +++ b/client/setting.go @@ -0,0 +1,77 @@ +package client + +import ( + "context" + "fmt" + "net/http" + "strings" + + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" + "google.golang.org/protobuf/encoding/protojson" +) + +// ListSettings lists all settings. +func (c *client) ListSettings(ctx context.Context) (*v1pb.ListSettingsResponse, error) { + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/%s/settings", c.url, c.version), nil) + if err != nil { + return nil, err + } + + body, err := c.doRequest(req) + if err != nil { + return nil, err + } + + var res v1pb.ListSettingsResponse + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { + return nil, err + } + + return &res, nil +} + +// GetSetting gets the setting by the name. +func (c *client) GetSetting(ctx context.Context, settingName string) (*v1pb.Setting, error) { + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/%s/%s", c.url, c.version, settingName), nil) + if err != nil { + return nil, err + } + + body, err := c.doRequest(req) + if err != nil { + return nil, err + } + + var res v1pb.Setting + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { + return nil, err + } + + return &res, nil +} + +// UpsertSetting updates or creates the setting. +func (c *client) UpsertSetting(ctx context.Context, upsert *v1pb.Setting, updateMasks []string) (*v1pb.Setting, error) { + payload, err := protojson.Marshal(upsert) + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, "PATCH", fmt.Sprintf("%s/%s/%s?update_mask=%s&allow_missing=true", c.url, c.version, upsert.Name, strings.Join(updateMasks, ",")), strings.NewReader(string(payload))) + + if err != nil { + return nil, err + } + + body, err := c.doRequest(req) + if err != nil { + return nil, err + } + + var res v1pb.Setting + if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil { + return nil, err + } + + return &res, nil +} diff --git a/examples/environments/main.tf b/examples/environments/main.tf index f31d158..e966b2f 100644 --- a/examples/environments/main.tf +++ b/examples/environments/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { bytebase = { - version = "0.0.9" + version = "1.0.3" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/instances/main.tf b/examples/instances/main.tf index d90e884..4700e92 100644 --- a/examples/instances/main.tf +++ b/examples/instances/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { bytebase = { - version = "0.0.9" + version = "1.0.3" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/projects/main.tf b/examples/projects/main.tf index 040634d..7c30e4e 100644 --- a/examples/projects/main.tf +++ b/examples/projects/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { bytebase = { - version = "0.0.9" + version = "1.0.3" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } diff --git a/examples/settings/main.tf b/examples/settings/main.tf new file mode 100644 index 0000000..b43021e --- /dev/null +++ b/examples/settings/main.tf @@ -0,0 +1,22 @@ +terraform { + required_providers { + bytebase = { + version = "1.0.3" + # For local development, please use "terraform.local/bytebase/bytebase" instead + source = "terraform.local/bytebase/bytebase" + } + } +} + +provider "bytebase" { + # You need to replace the account and key with your Bytebase service account. + service_account = "ed@bytebase.com" + service_key = "12345678A!" + # The Bytebase service URL. You can use the external URL in production. + # Check the docs about external URL: https://www.bytebase.com/docs/get-started/install/external-url + url = "http://localhost:8080" +} + +data "bytebase_setting" "approval_flow" { + name = "bb.workspace.approval" +} diff --git a/examples/setup/main.tf b/examples/setup/main.tf index 1519eff..8266761 100644 --- a/examples/setup/main.tf +++ b/examples/setup/main.tf @@ -1,7 +1,7 @@ terraform { required_providers { bytebase = { - version = "1.0.0" + version = "1.0.3" # For local development, please use "terraform.local/bytebase/bytebase" instead source = "registry.terraform.io/bytebase/bytebase" } @@ -95,3 +95,37 @@ resource "bytebase_project" "sample_project" { title = "Sample project" key = "SAMM" } + +resource "bytebase_setting" "approval_flow" { + name = "bb.workspace.approval" + approval_flow { + rules { + flow { + title = "DBA -> OWNER" + description = "Need DBA and workspace owner approval" + creator = "users/support@bytebase.com" + + # Approval flow following the step order. + steps { + type = "GROUP" + node = "WORKSPACE_DBA" + } + + steps { + type = "GROUP" + node = "WORKSPACE_OWNER" + } + } + + # Match any condition will trigger this approval flow. + conditions { + source = "DML" + level = "MODERATE" + } + conditions { + source = "DDL" + level = "HIGH" + } + } + } +} diff --git a/go.mod b/go.mod index 3adf98e..449b615 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,14 @@ module github.com/bytebase/terraform-provider-bytebase go 1.23.2 require ( - buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.35.2-20240417031025-3a5c93e22bec.1 + github.com/bytebase/bytebase v0.0.0-20241205093738-38cba35b1547 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-docs v0.13.0 github.com/hashicorp/terraform-plugin-log v0.7.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0 github.com/pkg/errors v0.9.1 + google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 + google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583 google.golang.org/protobuf v1.35.2 ) @@ -20,11 +22,12 @@ require ( github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fatih/color v1.13.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -44,8 +47,8 @@ require ( github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.13 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/cli v1.1.4 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect @@ -55,17 +58,17 @@ require ( github.com/oklog/run v1.0.0 // indirect github.com/posener/complete v1.2.3 // indirect github.com/russross/blackfriday v1.6.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect github.com/vmihailenco/tagparser v0.1.1 // indirect github.com/zclconf/go-cty v1.11.0 // indirect - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/crypto v0.29.0 // indirect + golang.org/x/net v0.31.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/grpc v1.53.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect + google.golang.org/grpc v1.68.0 // indirect ) diff --git a/go.sum b/go.sum index 7f16df1..7e3736f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.35.2-20240417031025-3a5c93e22bec.1 h1:MA91yfslAinPBPzB0ZGXCW97kcmYBVy9QhM4X7M9nKY= -buf.build/gen/go/bytebase/bytebase/protocolbuffers/go v1.35.2-20240417031025-3a5c93e22bec.1/go.mod h1:fFdEhMRFdbiK/xOo8mzc5bmoNJ4cxeEfBMXodq5wRE8= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -31,10 +29,13 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bytebase/bytebase v0.0.0-20241205093738-38cba35b1547 h1:4iW1KxDfqJkTbebeuiVgzxxrN6X/Ask1ic9MDFmIfno= +github.com/bytebase/bytebase v0.0.0-20241205093738-38cba35b1547/go.mod h1:3eFZBpvlGqEmGwGK6x7sgdUth4I6uNCNTw57fRFuXr8= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -58,19 +59,19 @@ github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -137,8 +138,8 @@ github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -149,12 +150,15 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mitchellh/cli v1.1.4 h1:qj8czE26AU4PbiaPXK5uVmMSM+V5BYsFBiM9HhGRLUA= github.com/mitchellh/cli v1.1.4/go.mod h1:vTLESy5mRhKOs9KDp0/RATawxP1UqBmdrpVRMnpcvKQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= @@ -179,13 +183,14 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= @@ -193,8 +198,8 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -207,8 +212,9 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -231,8 +237,8 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -243,8 +249,8 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -264,28 +270,31 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= +google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583 h1:v+j+5gpj0FopU0KKLDGfDo9ZRRpKdi5UBrCP0f76kuY= +google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/provider/data_source_project.go b/provider/data_source_project.go index 2001e3f..7a992b7 100644 --- a/provider/data_source_project.go +++ b/provider/data_source_project.go @@ -10,7 +10,7 @@ import ( "github.com/bytebase/terraform-provider-bytebase/api" "github.com/bytebase/terraform-provider-bytebase/provider/internal" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" ) func dataSourceProject() *schema.Resource { diff --git a/provider/data_source_setting.go b/provider/data_source_setting.go new file mode 100644 index 0000000..6f16844 --- /dev/null +++ b/provider/data_source_setting.go @@ -0,0 +1,287 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/pkg/errors" + + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" + v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + + "github.com/bytebase/terraform-provider-bytebase/api" + "github.com/bytebase/terraform-provider-bytebase/provider/internal" +) + +func dataSourceSetting() *schema.Resource { + return &schema.Resource{ + Description: "The setting data source.", + ReadContext: dataSourceSettingRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(api.SettingWorkspaceApproval), + }, false), + }, + "approval_flow": getWorkspaceApprovalSetting(true), + }, + } +} + +func getWorkspaceApprovalSetting(computed bool) *schema.Schema { + return &schema.Schema{ + Computed: computed, + Optional: true, + Default: nil, + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rules": { + Type: schema.TypeList, + Computed: computed, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "flow": { + Computed: computed, + Optional: true, + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "title": { + Type: schema.TypeString, + Computed: computed, + Optional: true, + }, + "description": { + Type: schema.TypeString, + Computed: computed, + Optional: true, + }, + "creator": { + Type: schema.TypeString, + Computed: computed, + Optional: true, + }, + "steps": { + Type: schema.TypeList, + Computed: computed, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Computed: computed, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(api.ApprovalNodeTypeGroup), + string(api.ApprovalNodeTypeRole), + string(api.ApprovalNodeTypeExternalNodeID), + }, false), + }, + "node": { + Optional: true, + Default: nil, + Computed: computed, + Type: schema.TypeString, + // TODO(ed): consider add validate + }, + }, + }, + }, + }, + }, + }, + "conditions": { + Computed: computed, + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "source": { + Type: schema.TypeString, + Computed: computed, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + v1pb.Risk_DDL.String(), + v1pb.Risk_DML.String(), + v1pb.Risk_CREATE_DATABASE.String(), + v1pb.Risk_DATA_EXPORT.String(), + v1pb.Risk_REQUEST_QUERY.String(), + v1pb.Risk_REQUEST_EXPORT.String(), + }, false), + }, + "level": { + Type: schema.TypeString, + Computed: computed, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(api.RiskLevelDefault), + string(api.RiskLevelLow), + string(api.RiskLevelModerate), + string(api.RiskLevelHigh), + }, false), + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func dataSourceSettingRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(api.Client) + + settingName := fmt.Sprintf("%s%s", internal.SettingNamePrefix, d.Get("name").(string)) + setting, err := c.GetSetting(ctx, settingName) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(setting.Name) + return setSettingMessage(ctx, d, c, setting) +} + +func setSettingMessage(ctx context.Context, d *schema.ResourceData, client api.Client, setting *v1pb.Setting) diag.Diagnostics { + if value := setting.Value.GetWorkspaceApprovalSettingValue(); value != nil { + settingVal, err := flattenWorkspaceApprovalSetting(ctx, client, value) + if err != nil { + return diag.Errorf("failed to parse workspace_approval_setting: %s", err.Error()) + } + if err := d.Set("approval_flow", settingVal); err != nil { + return diag.Errorf("cannot set workspace_approval_setting: %s", err.Error()) + } + } + + return nil +} + +func parseApprovalExpression(callExpr *v1alpha1.Expr_Call) ([]map[string]interface{}, error) { + if callExpr == nil { + return nil, errors.Errorf("failed to parse the expression") + } + + switch callExpr.Function { + case "_&&_": + resp := map[string]interface{}{} + for _, arg := range callExpr.Args { + argExpr := arg.GetCallExpr() + if argExpr == nil { + return nil, errors.Errorf("expect call_expr") + } + if argExpr.Function != "_==_" { + return nil, errors.Errorf("expect == operation but found: %v", argExpr.Function) + } + if len(argExpr.Args) != 2 { + return nil, errors.Errorf("expect 2 args") + } + + if argExpr.Args[0].GetIdentExpr() == nil || argExpr.Args[1].GetConstExpr() == nil { + return nil, errors.Errorf("expect ident expr and const expr") + } + + argName := argExpr.Args[0].GetIdentExpr().Name + switch argName { + case "source": + resp[argName] = argExpr.Args[1].GetConstExpr().GetStringValue() + case "level": + levelNumber := argExpr.Args[1].GetConstExpr().GetInt64Value() + switch int(levelNumber) { + case api.RiskLevelDefault.Int(): + resp[argName] = api.RiskLevelDefault + case api.RiskLevelLow.Int(): + resp[argName] = api.RiskLevelLow + case api.RiskLevelModerate.Int(): + resp[argName] = api.RiskLevelModerate + case api.RiskLevelHigh.Int(): + resp[argName] = api.RiskLevelHigh + default: + return nil, errors.Errorf("unknown risk level: %v", levelNumber) + } + default: + return nil, errors.Errorf("unsupport arg: %v", argName) + } + } + return []map[string]interface{}{resp}, nil + case "_||_": + resp := []map[string]interface{}{} + for _, arg := range callExpr.Args { + expression, err := parseApprovalExpression(arg.GetCallExpr()) + if err != nil { + return nil, err + } + resp = append(resp, expression...) + } + return resp, nil + default: + return nil, errors.Errorf("unsupport expr function: %v", callExpr.Function) + } +} + +func flattenWorkspaceApprovalSetting(ctx context.Context, client api.Client, setting *v1pb.WorkspaceApprovalSetting) ([]interface{}, error) { + ruleList := []interface{}{} + for _, rule := range setting.Rules { + stepList := []interface{}{} + for _, step := range rule.Template.Flow.Steps { + for _, node := range step.Nodes { + rawNode := map[string]interface{}{} + switch payload := node.Payload.(type) { + case *v1pb.ApprovalNode_Role: + rawNode["type"] = string(api.ApprovalNodeTypeRole) + rawNode["node"] = payload.Role + case *v1pb.ApprovalNode_ExternalNodeId: + rawNode["type"] = string(api.ApprovalNodeTypeExternalNodeID) + rawNode["node"] = payload.ExternalNodeId + case *v1pb.ApprovalNode_GroupValue_: + rawNode["type"] = string(api.ApprovalNodeTypeGroup) + rawNode["node"] = payload.GroupValue.String() + } + stepList = append(stepList, rawNode) + } + } + + conditionList := []map[string]interface{}{} + if rule.Condition.Expression != "" { + tflog.Debug(ctx, rule.Condition.Expression) + + parsedExpr, err := client.ParseExpression(ctx, rule.Condition.Expression) + if err != nil { + return nil, err + } + expressions, err := parseApprovalExpression(parsedExpr.GetCallExpr()) + if err != nil { + return nil, err + } + conditionList = expressions + } + + raw := map[string]interface{}{ + "conditions": conditionList, + "flow": []interface{}{ + map[string]interface{}{ + "title": rule.Template.Title, + "description": rule.Template.Description, + "creator": rule.Template.Creator, + "steps": stepList, + }, + }, + } + + ruleList = append(ruleList, raw) + } + + approvalSetting := map[string]interface{}{ + "rules": ruleList, + } + return []interface{}{approvalSetting}, nil +} diff --git a/provider/internal/mock_client.go b/provider/internal/mock_client.go index 36343a7..e31f289 100644 --- a/provider/internal/mock_client.go +++ b/provider/internal/mock_client.go @@ -10,7 +10,8 @@ import ( "github.com/bytebase/terraform-provider-bytebase/api" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" + v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1" ) var environmentMap map[string]*v1pb.Environment @@ -18,6 +19,7 @@ var instanceMap map[string]*v1pb.Instance var policyMap map[string]*api.PolicyMessage var projectMap map[string]*v1pb.Project var databaseMap map[string]*v1pb.Database +var settingMap map[string]*v1pb.Setting func init() { environmentMap = map[string]*v1pb.Environment{} @@ -25,6 +27,7 @@ func init() { policyMap = map[string]*api.PolicyMessage{} projectMap = map[string]*v1pb.Project{} databaseMap = map[string]*v1pb.Database{} + settingMap = map[string]*v1pb.Setting{} } type mockClient struct { @@ -33,6 +36,7 @@ type mockClient struct { policyMap map[string]*api.PolicyMessage projectMap map[string]*v1pb.Project databaseMap map[string]*v1pb.Database + settingMap map[string]*v1pb.Setting } // newMockClient returns the new Bytebase API mock client. @@ -43,6 +47,7 @@ func newMockClient(_, _, _ string) (api.Client, error) { policyMap: policyMap, projectMap: projectMap, databaseMap: databaseMap, + settingMap: settingMap, }, nil } @@ -475,3 +480,41 @@ func (c *mockClient) UndeleteProject(ctx context.Context, projectName string) (* return proj, nil } + +// ListSettings lists all settings. +func (c *mockClient) ListSettings(_ context.Context) (*v1pb.ListSettingsResponse, error) { + settings := make([]*v1pb.Setting, 0) + for _, setting := range c.settingMap { + settings = append(settings, setting) + } + + return &v1pb.ListSettingsResponse{ + Settings: settings, + }, nil +} + +// ListSettings lists all settings. +func (c *mockClient) GetSetting(_ context.Context, settingName string) (*v1pb.Setting, error) { + setting, ok := c.settingMap[settingName] + if !ok { + return nil, errors.Errorf("Cannot found setting %s", settingName) + } + + return setting, nil +} + +// UpsertSetting updates or creates the setting. +func (c *mockClient) UpsertSetting(_ context.Context, upsert *v1pb.Setting, _ []string) (*v1pb.Setting, error) { + setting, ok := c.settingMap[upsert.Name] + if !ok { + c.settingMap[upsert.Name] = upsert + } else { + setting.Value = upsert.Value + c.settingMap[upsert.Name] = setting + } + return c.settingMap[upsert.Name], nil +} + +func (*mockClient) ParseExpression(_ context.Context, _ string) (*v1alpha1.Expr, error) { + return nil, nil +} diff --git a/provider/internal/utils.go b/provider/internal/utils.go index 4fd83a4..a4068cd 100644 --- a/provider/internal/utils.go +++ b/provider/internal/utils.go @@ -25,6 +25,8 @@ const ( DatabaseIDPrefix = "databases/" // PolicyNamePrefix is the prefix for policy unique name. PolicyNamePrefix = "policies/" + // SettingNamePrefix is the prefix for setting unique name. + SettingNamePrefix = "settings/" // ResourceIDPattern is the pattern for resource id. ResourceIDPattern = "[a-z]([a-z0-9-]{0,61}[a-z0-9])?" ) diff --git a/provider/provider.go b/provider/provider.go index f29af92..d53005b 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -57,12 +57,14 @@ func NewProvider() *schema.Provider { "bytebase_policy_list": dataSourcePolicyList(), "bytebase_project": dataSourceProject(), "bytebase_project_list": dataSourceProjectList(), + "bytebase_setting": dataSourceSetting(), }, ResourcesMap: map[string]*schema.Resource{ "bytebase_environment": resourceEnvironment(), "bytebase_instance": resourceInstance(), "bytebase_policy": resourcePolicy(), "bytebase_project": resourceProjct(), + "bytebase_setting": resourceSetting(), }, } } diff --git a/provider/resource_environment.go b/provider/resource_environment.go index 9c8cc7d..1e703a7 100644 --- a/provider/resource_environment.go +++ b/provider/resource_environment.go @@ -10,7 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" "github.com/bytebase/terraform-provider-bytebase/api" "github.com/bytebase/terraform-provider-bytebase/provider/internal" diff --git a/provider/resource_instance.go b/provider/resource_instance.go index 595abfa..948d77a 100644 --- a/provider/resource_instance.go +++ b/provider/resource_instance.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/pkg/errors" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" "github.com/bytebase/terraform-provider-bytebase/api" "github.com/bytebase/terraform-provider-bytebase/provider/internal" diff --git a/provider/resource_project.go b/provider/resource_project.go index 5958487..5fd849b 100644 --- a/provider/resource_project.go +++ b/provider/resource_project.go @@ -12,7 +12,7 @@ import ( "github.com/bytebase/terraform-provider-bytebase/api" "github.com/bytebase/terraform-provider-bytebase/provider/internal" - v1pb "buf.build/gen/go/bytebase/bytebase/protocolbuffers/go/v1" + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" ) // defaultProj is the default project name. diff --git a/provider/resource_setting.go b/provider/resource_setting.go new file mode 100644 index 0000000..8bf890e --- /dev/null +++ b/provider/resource_setting.go @@ -0,0 +1,187 @@ +package provider + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/pkg/errors" + "google.golang.org/genproto/googleapis/type/expr" + + v1pb "github.com/bytebase/bytebase/proto/generated-go/v1" + + "github.com/bytebase/terraform-provider-bytebase/api" + "github.com/bytebase/terraform-provider-bytebase/provider/internal" +) + +func resourceSetting() *schema.Resource { + return &schema.Resource{ + Description: "The setting resource.", + CreateContext: resourceSettingUpsert, + ReadContext: resourceSettingRead, + UpdateContext: resourceSettingUpsert, + DeleteContext: resourceSettingDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(api.SettingWorkspaceApproval), + }, false), + }, + "approval_flow": getWorkspaceApprovalSetting(false), + }, + } +} + +func resourceSettingUpsert(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(api.Client) + var diags diag.Diagnostics + + name := api.SettingName(d.Get("name").(string)) + settingName := fmt.Sprintf("%s%s", internal.SettingNamePrefix, string(name)) + + setting := &v1pb.Setting{ + Name: settingName, + } + + switch name { + case api.SettingWorkspaceApproval: + workspaceApproval, err := convertToV1ApprovalSetting(d) + if err != nil { + return diag.FromErr(err) + } + setting.Value = &v1pb.Value{ + Value: &v1pb.Value_WorkspaceApprovalSettingValue{ + WorkspaceApprovalSettingValue: workspaceApproval, + }, + } + default: + return diag.FromErr(errors.Errorf("Unsupport setting: %v", name)) + } + + updatedSetting, err := c.UpsertSetting(ctx, setting, []string{}) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(updatedSetting.Name) + + diag := resourceSettingRead(ctx, d, m) + if diag != nil { + diags = append(diags, diag...) + } + + return diags +} + +func convertToV1ApprovalSetting(d *schema.ResourceData) (*v1pb.WorkspaceApprovalSetting, error) { + rawList, ok := d.Get("approval_flow").([]interface{}) + if !ok || len(rawList) != 1 { + return nil, errors.Errorf("invalid approval_flow") + } + + raw := rawList[0].(map[string]interface{}) + rules := raw["rules"].([]interface{}) + workspaceApprovalSetting := &v1pb.WorkspaceApprovalSetting{} + for _, rule := range rules { + rawRule := rule.(map[string]interface{}) + + // build condition expression. + conditionList, ok := rawRule["conditions"].([]interface{}) + if !ok { + return nil, errors.Errorf("invalid conditions") + } + buildCondition := []string{} + for _, condition := range conditionList { + rawCondition := condition.(map[string]interface{}) + rawLevel := rawCondition["level"].(string) + buildCondition = append(buildCondition, fmt.Sprintf(`source == "%s" && level == %d`, rawCondition["source"].(string), api.RiskLevel(rawLevel).Int())) + } + expression := strings.Join(buildCondition, " || ") + + flowList, ok := rawRule["flow"].([]interface{}) + if !ok || len(flowList) != 1 { + return nil, errors.Errorf("invalid flow") + } + rawFlow := flowList[0].(map[string]interface{}) + approvalRule := &v1pb.WorkspaceApprovalSetting_Rule{ + Template: &v1pb.ApprovalTemplate{ + Title: rawFlow["title"].(string), + Description: rawFlow["description"].(string), + Creator: rawFlow["creator"].(string), + Flow: &v1pb.ApprovalFlow{}, + }, + Condition: &expr.Expr{ + Expression: expression, + }, + } + + stepList, ok := rawFlow["steps"].([]interface{}) + if !ok { + return nil, errors.Errorf("invalid steps") + } + + for _, step := range stepList { + rawStep := step.(map[string]interface{}) + stepType := api.ApprovalNodeType(rawStep["type"].(string)) + node := rawStep["node"].(string) + + approvalNode := &v1pb.ApprovalNode{ + Type: v1pb.ApprovalNode_ANY_IN_GROUP, + } + switch stepType { + case api.ApprovalNodeTypeRole: + approvalNode.Payload = &v1pb.ApprovalNode_Role{ + Role: node, + } + case api.ApprovalNodeTypeGroup: + group, ok := v1pb.ApprovalNode_GroupValue_value[node] + if !ok { + return nil, errors.Errorf("invalid group: %v", node) + } + approvalNode.Payload = &v1pb.ApprovalNode_GroupValue_{ + GroupValue: v1pb.ApprovalNode_GroupValue(group), + } + case api.ApprovalNodeTypeExternalNodeID: + approvalNode.Payload = &v1pb.ApprovalNode_ExternalNodeId{ + ExternalNodeId: node, + } + } + + approvalStep := &v1pb.ApprovalStep{ + Type: v1pb.ApprovalStep_ANY, + Nodes: []*v1pb.ApprovalNode{approvalNode}, + } + + approvalRule.Template.Flow.Steps = append(approvalRule.Template.Flow.Steps, approvalStep) + } + + workspaceApprovalSetting.Rules = append(workspaceApprovalSetting.Rules, approvalRule) + } + + return workspaceApprovalSetting, nil +} + +func resourceSettingRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(api.Client) + + settingName := d.Id() + setting, err := c.GetSetting(ctx, settingName) + if err != nil { + return diag.FromErr(err) + } + + return setSettingMessage(ctx, d, c, setting) +} + +func resourceSettingDelete(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics { + d.SetId("") + return nil +}