diff --git a/api/setting.go b/api/setting.go
index fab775f..abb08fb 100644
--- a/api/setting.go
+++ b/api/setting.go
@@ -10,6 +10,8 @@ const (
SettingWorkspaceProfile SettingName = "bb.workspace.profile"
// SettingWorkspaceExternalApproval is the setting name for workspace external approval config.
SettingWorkspaceExternalApproval SettingName = "bb.workspace.approval.external"
+ // SettingDataClassification is the setting name for data classification.
+ SettingDataClassification SettingName = "bb.workspace.data-classification"
)
// RiskLevel is the approval risk level.
diff --git a/docs/data-sources/setting.md b/docs/data-sources/setting.md
index bcf5780..8c7447a 100644
--- a/docs/data-sources/setting.md
+++ b/docs/data-sources/setting.md
@@ -21,6 +21,7 @@ The setting data source.
### Optional
+- `classification` (Block List, Max: 1) Classification for data masking. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--classification))
- `workspace_profile` (Block List, Max: 1) (see [below for nested schema](#nestedblock--workspace_profile))
### Read-Only
@@ -29,6 +30,39 @@ The setting data source.
- `external_approval_nodes` (Block List) Configure external nodes in the approval flow. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--external_approval_nodes))
- `id` (String) The ID of this resource.
+
+### Nested Schema for `classification`
+
+Optional:
+
+- `classification_from_config` (Boolean) If true, we will only store the classification in the config. Otherwise we will get the classification from table/column comment, and write back to the schema metadata.
+- `classifications` (Block List) (see [below for nested schema](#nestedblock--classification--classifications))
+- `id` (String) The classification unique uuid.
+- `levels` (Block List) (see [below for nested schema](#nestedblock--classification--levels))
+- `title` (String) The classification title. Optional.
+
+
+### Nested Schema for `classification.classifications`
+
+Optional:
+
+- `description` (String) The classification description.
+- `id` (String) The classification unique id, must in {number}-{number} format.
+- `level` (String) The classification level id.
+- `title` (String) The classification title.
+
+
+
+### Nested Schema for `classification.levels`
+
+Optional:
+
+- `description` (String) The classification level description.
+- `id` (String) The classification level unique uuid.
+- `title` (String) The classification level title.
+
+
+
### Nested Schema for `workspace_profile`
diff --git a/docs/resources/setting.md b/docs/resources/setting.md
index 2e1b80e..421ad28 100644
--- a/docs/resources/setting.md
+++ b/docs/resources/setting.md
@@ -22,6 +22,7 @@ The setting resource.
### Optional
- `approval_flow` (Block List) Configure risk level and approval flow for different tasks. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--approval_flow))
+- `classification` (Block List, Max: 1) Classification for data masking. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--classification))
- `external_approval_nodes` (Block List) Configure external nodes in the approval flow. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--external_approval_nodes))
- `workspace_profile` (Block List, Max: 1) (see [below for nested schema](#nestedblock--workspace_profile))
@@ -84,6 +85,39 @@ Optional:
+
+### Nested Schema for `classification`
+
+Optional:
+
+- `classification_from_config` (Boolean) If true, we will only store the classification in the config. Otherwise we will get the classification from table/column comment, and write back to the schema metadata.
+- `classifications` (Block List) (see [below for nested schema](#nestedblock--classification--classifications))
+- `id` (String) The classification unique uuid.
+- `levels` (Block List) (see [below for nested schema](#nestedblock--classification--levels))
+- `title` (String) The classification title. Optional.
+
+
+### Nested Schema for `classification.classifications`
+
+Optional:
+
+- `description` (String) The classification description.
+- `id` (String) The classification unique id, must in {number}-{number} format.
+- `level` (String) The classification level id.
+- `title` (String) The classification title.
+
+
+
+### Nested Schema for `classification.levels`
+
+Optional:
+
+- `description` (String) The classification level description.
+- `id` (String) The classification level unique uuid.
+- `title` (String) The classification level title.
+
+
+
### Nested Schema for `external_approval_nodes`
diff --git a/examples/database/main.tf b/examples/database/main.tf
index e139030..a01c738 100644
--- a/examples/database/main.tf
+++ b/examples/database/main.tf
@@ -2,7 +2,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.6"
+ version = "1.0.7"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
diff --git a/examples/environments/main.tf b/examples/environments/main.tf
index 8929126..fc4fa79 100644
--- a/examples/environments/main.tf
+++ b/examples/environments/main.tf
@@ -2,7 +2,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.6"
+ version = "1.0.7"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
diff --git a/examples/groups/main.tf b/examples/groups/main.tf
index 7d0bc8f..0efb1b7 100644
--- a/examples/groups/main.tf
+++ b/examples/groups/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.6"
+ version = "1.0.7"
# 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 cac84e7..ae342b3 100644
--- a/examples/instances/main.tf
+++ b/examples/instances/main.tf
@@ -2,7 +2,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.6"
+ version = "1.0.7"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
diff --git a/examples/policies/main.tf b/examples/policies/main.tf
index 3b9b7a4..c1ef376 100644
--- a/examples/policies/main.tf
+++ b/examples/policies/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.6"
+ version = "1.0.7"
# 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 5421c88..b898b8a 100644
--- a/examples/projects/main.tf
+++ b/examples/projects/main.tf
@@ -2,7 +2,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.6"
+ version = "1.0.7"
# 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
index 4c9244f..e2974ca 100644
--- a/examples/settings/main.tf
+++ b/examples/settings/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.6"
+ version = "1.0.7"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
@@ -29,6 +29,10 @@ data "bytebase_setting" "workspace_profile" {
name = "bb.workspace.profile"
}
+data "bytebase_setting" "classification" {
+ name = "bb.workspace.data-classification"
+}
+
output "approval_flow" {
value = data.bytebase_setting.approval_flow
}
@@ -40,3 +44,7 @@ output "external_approval" {
output "workspace_profile" {
value = data.bytebase_setting.workspace_profile
}
+
+output "classification" {
+ value = data.bytebase_setting.classification
+}
diff --git a/examples/setup/data_masking.tf b/examples/setup/data_masking.tf
index 74be919..ded651d 100644
--- a/examples/setup/data_masking.tf
+++ b/examples/setup/data_masking.tf
@@ -1,6 +1,49 @@
+resource "bytebase_setting" "classification" {
+ name = "bb.workspace.data-classification"
+
+ classification {
+ id = "unique-id"
+ title = "Classification Example"
+
+ levels {
+ id = "1"
+ title = "Level 1"
+ }
+ levels {
+ id = "2"
+ title = "Level 2"
+ }
+
+ classifications {
+ id = "1"
+ title = "Basic"
+ }
+ classifications {
+ id = "1-1"
+ title = "User basic info"
+ level = "2"
+ }
+ classifications {
+ id = "1-2"
+ title = "User contact info"
+ level = "2"
+ }
+ classifications {
+ id = "2"
+ title = "Relationship"
+ }
+ classifications {
+ id = "2-1"
+ title = "Social info"
+ level = "2"
+ }
+ }
+}
+
resource "bytebase_database_catalog" "employee_catalog" {
depends_on = [
- bytebase_instance.test
+ bytebase_instance.test,
+ bytebase_setting.classification
]
database = "instances/test-sample-instance/databases/employee"
@@ -9,13 +52,13 @@ resource "bytebase_database_catalog" "employee_catalog" {
tables {
name = "salary"
columns {
- name = "amount"
- semantic_type = "default"
- classification = "1-1-1"
+ name = "amount"
+ semantic_type = "default"
}
columns {
- name = "emp_no"
- semantic_type = "default-partial"
+ name = "emp_no"
+ semantic_type = "default-partial"
+ classification = "1-1"
labels = {
tenant = "example"
region = "asia"
diff --git a/examples/setup/main.tf b/examples/setup/main.tf
index d6c3f96..57a4c9f 100644
--- a/examples/setup/main.tf
+++ b/examples/setup/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.6"
+ version = "1.0.7"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
diff --git a/examples/users/main.tf b/examples/users/main.tf
index a571ee7..f262419 100644
--- a/examples/users/main.tf
+++ b/examples/users/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.6"
+ version = "1.0.7"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
diff --git a/examples/vcs/main.tf b/examples/vcs/main.tf
index 5d9c9f7..9afc88b 100644
--- a/examples/vcs/main.tf
+++ b/examples/vcs/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
bytebase = {
- version = "1.0.6"
+ version = "1.0.7"
# For local development, please use "terraform.local/bytebase/bytebase" instead
source = "registry.terraform.io/bytebase/bytebase"
}
diff --git a/provider/data_source_setting.go b/provider/data_source_setting.go
index 239a169..c5240c1 100644
--- a/provider/data_source_setting.go
+++ b/provider/data_source_setting.go
@@ -29,11 +29,109 @@ func dataSourceSetting() *schema.Resource {
string(api.SettingWorkspaceApproval),
string(api.SettingWorkspaceExternalApproval),
string(api.SettingWorkspaceProfile),
+ string(api.SettingDataClassification),
}, false),
},
"approval_flow": getWorkspaceApprovalSetting(true),
"external_approval_nodes": getExternalApprovalSetting(true),
"workspace_profile": getWorkspaceProfileSetting(true),
+ "classification": getClassificationSetting(true),
+ },
+ }
+}
+
+func getClassificationSetting(computed bool) *schema.Schema {
+ return &schema.Schema{
+ Computed: computed,
+ Optional: true,
+ Default: nil,
+ Type: schema.TypeList,
+ MaxItems: 1,
+ MinItems: 1,
+ Description: "Classification for data masking. Require ENTERPRISE subscription.",
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Type: schema.TypeString,
+ Computed: computed,
+ Optional: true,
+ Description: "The classification unique uuid.",
+ },
+ "title": {
+ Type: schema.TypeString,
+ Computed: computed,
+ Optional: true,
+ Description: "The classification title. Optional.",
+ },
+ "classification_from_config": {
+ Type: schema.TypeBool,
+ Computed: computed,
+ Optional: true,
+ Description: "If true, we will only store the classification in the config. Otherwise we will get the classification from table/column comment, and write back to the schema metadata.",
+ },
+ "levels": {
+ Computed: computed,
+ Optional: true,
+ Type: schema.TypeList,
+ MinItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Type: schema.TypeString,
+ Computed: computed,
+ Optional: true,
+ Description: "The classification level unique uuid.",
+ },
+ "title": {
+ Type: schema.TypeString,
+ Computed: computed,
+ Optional: true,
+ Description: "The classification level title.",
+ },
+ "description": {
+ Type: schema.TypeString,
+ Computed: computed,
+ Optional: true,
+ Description: "The classification level description.",
+ },
+ },
+ },
+ },
+ "classifications": {
+ Computed: computed,
+ Optional: true,
+ Type: schema.TypeList,
+ MinItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Type: schema.TypeString,
+ Computed: computed,
+ Optional: true,
+ Description: "The classification unique id, must in {number}-{number} format.",
+ },
+ "title": {
+ Type: schema.TypeString,
+ Computed: computed,
+ Optional: true,
+ Description: "The classification title.",
+ },
+ "description": {
+ Type: schema.TypeString,
+ Computed: computed,
+ Optional: true,
+ Description: "The classification description.",
+ },
+ "level": {
+ Type: schema.TypeString,
+ Computed: computed,
+ Optional: true,
+ Description: "The classification level id.",
+ },
+ },
+ },
+ },
+ },
},
}
}
@@ -271,6 +369,12 @@ func setSettingMessage(ctx context.Context, d *schema.ResourceData, client api.C
return diag.Errorf("cannot set workspace_profile: %s", err.Error())
}
}
+ if value := setting.Value.GetDataClassificationSettingValue(); value != nil {
+ settingVal := flattenClassificationSetting(value)
+ if err := d.Set("classification", settingVal); err != nil {
+ return diag.Errorf("cannot set classification: %s", err.Error())
+ }
+ }
return nil
}
@@ -422,3 +526,37 @@ func flattenWorkspaceProfileSetting(setting *v1pb.WorkspaceProfileSetting) []int
return []interface{}{raw}
}
+
+func flattenClassificationSetting(setting *v1pb.DataClassificationSetting) []interface{} {
+ raw := map[string]interface{}{}
+
+ if len(setting.GetConfigs()) > 0 {
+ config := setting.GetConfigs()[0]
+ raw["id"] = config.Id
+ raw["title"] = config.Title
+ raw["classification_from_config"] = config.ClassificationFromConfig
+
+ rawLevels := []interface{}{}
+ for _, level := range config.Levels {
+ rawLevel := map[string]interface{}{}
+ rawLevel["id"] = level.Id
+ rawLevel["title"] = level.Title
+ rawLevel["description"] = level.Description
+ rawLevels = append(rawLevels, rawLevel)
+ }
+ raw["levels"] = rawLevels
+
+ rawClassifications := []interface{}{}
+ for _, classification := range config.GetClassification() {
+ rawClassification := map[string]interface{}{}
+ rawClassification["id"] = classification.Id
+ rawClassification["title"] = classification.Title
+ rawClassification["description"] = classification.Description
+ rawClassification["level"] = classification.LevelId
+ rawClassifications = append(rawClassifications, rawClassification)
+ }
+ raw["classifications"] = rawClassifications
+ }
+
+ return []interface{}{raw}
+}
diff --git a/provider/resource_setting.go b/provider/resource_setting.go
index 1dfc4d5..4702a8b 100644
--- a/provider/resource_setting.go
+++ b/provider/resource_setting.go
@@ -35,12 +35,13 @@ func resourceSetting() *schema.Resource {
string(api.SettingWorkspaceApproval),
string(api.SettingWorkspaceExternalApproval),
string(api.SettingWorkspaceProfile),
+ string(api.SettingDataClassification),
}, false),
},
"approval_flow": getWorkspaceApprovalSetting(false),
"external_approval_nodes": getExternalApprovalSetting(false),
"workspace_profile": getWorkspaceProfileSetting(false),
- },
+ "classification": getClassificationSetting(false)},
}
}
@@ -88,6 +89,16 @@ func resourceSettingUpsert(ctx context.Context, d *schema.ResourceData, m interf
},
}
updateMasks = updatePathes
+ case api.SettingDataClassification:
+ classificationSetting, err := convertToV1ClassificationSetting(d)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ setting.Value = &v1pb.Value{
+ Value: &v1pb.Value_DataClassificationSettingValue{
+ DataClassificationSettingValue: classificationSetting,
+ },
+ }
default:
return diag.FromErr(errors.Errorf("Unsupport setting: %v", name))
}
@@ -146,6 +157,70 @@ func convertToV1WorkspaceProfileSetting(d *schema.ResourceData) (*v1pb.Workspace
return workspacePrfile, updateMasks, nil
}
+func convertToV1ClassificationSetting(d *schema.ResourceData) (*v1pb.DataClassificationSetting, error) {
+ rawList, ok := d.Get("classification").([]interface{})
+ if !ok || len(rawList) != 1 {
+ return nil, errors.Errorf("invalid classification")
+ }
+
+ raw := rawList[0].(map[string]interface{})
+
+ dataClassificationConfig := &v1pb.DataClassificationSetting_DataClassificationConfig{
+ Id: raw["id"].(string),
+ Title: raw["title"].(string),
+ ClassificationFromConfig: raw["classification_from_config"].(bool),
+ Levels: []*v1pb.DataClassificationSetting_DataClassificationConfig_Level{},
+ Classification: map[string]*v1pb.DataClassificationSetting_DataClassificationConfig_DataClassification{},
+ }
+ if dataClassificationConfig.Id == "" {
+ return nil, errors.Errorf("id is required for classification config")
+ }
+
+ rawLevels := raw["levels"].([]interface{})
+ for _, level := range rawLevels {
+ rawLevel := level.(map[string]interface{})
+ classificationLevel := &v1pb.DataClassificationSetting_DataClassificationConfig_Level{
+ Id: rawLevel["id"].(string),
+ Title: rawLevel["title"].(string),
+ Description: rawLevel["description"].(string),
+ }
+ if classificationLevel.Id == "" {
+ return nil, errors.Errorf("classification level id is required")
+ }
+ if classificationLevel.Title == "" {
+ return nil, errors.Errorf("classification level title is required")
+ }
+ dataClassificationConfig.Levels = append(dataClassificationConfig.Levels, classificationLevel)
+ }
+
+ rawClassificationss := raw["classifications"].([]interface{})
+ for _, classification := range rawClassificationss {
+ rawClassification := classification.(map[string]interface{})
+ classificationData := &v1pb.DataClassificationSetting_DataClassificationConfig_DataClassification{
+ Id: rawClassification["id"].(string),
+ Title: rawClassification["title"].(string),
+ Description: rawClassification["description"].(string),
+ }
+ if classificationData.Id == "" {
+ return nil, errors.Errorf("classification id is required")
+ }
+ if classificationData.Title == "" {
+ return nil, errors.Errorf("classification title is required")
+ }
+ levelID, ok := rawClassification["level"].(string)
+ if ok {
+ classificationData.LevelId = &levelID
+ }
+ dataClassificationConfig.Classification[classificationData.Id] = classificationData
+ }
+
+ return &v1pb.DataClassificationSetting{
+ Configs: []*v1pb.DataClassificationSetting_DataClassificationConfig{
+ dataClassificationConfig,
+ },
+ }, nil
+}
+
func convertToV1ExternalNodesSetting(d *schema.ResourceData) (*v1pb.ExternalApprovalSetting, error) {
rawList, ok := d.Get("external_approval_nodes").([]interface{})
if !ok || len(rawList) != 1 {