diff --git a/docs/data-sources/eventhouse.md b/docs/data-sources/eventhouse.md index 2e06025..1444594 100644 --- a/docs/data-sources/eventhouse.md +++ b/docs/data-sources/eventhouse.md @@ -66,6 +66,7 @@ output "example_definition_content_object" { ### Optional - `display_name` (String) The Eventhouse display name. +- `format` (String) The Eventhouse format. Possible values: `Default` - `id` (String) The Eventhouse ID. - `output_definition` (Boolean) Output definition parts as gzip base64 content? Default: `false` @@ -75,9 +76,8 @@ output "example_definition_content_object" { ### Read-Only -- `definition` (Attributes Map) Definition parts. Possible path keys: `EventhouseProperties.json`. (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Possible path keys: **Default** format: `EventhouseProperties.json` (see [below for nested schema](#nestedatt--definition)) - `description` (String) The Eventhouse description. -- `format` (String) The Eventhouse format. Possible values: `NotApplicable` - `properties` (Attributes) The Eventhouse properties. (see [below for nested schema](#nestedatt--properties)) diff --git a/docs/data-sources/notebook.md b/docs/data-sources/notebook.md index ccb6657..a3b3552 100644 --- a/docs/data-sources/notebook.md +++ b/docs/data-sources/notebook.md @@ -79,7 +79,7 @@ output "example_definition_content_object" { ### Read-Only -- `definition` (Attributes Map) Definition parts. Possible path keys: `notebook-content.ipynb`, `notebook-content.py`. (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Possible path keys: **ipynb** format: `notebook-content.ipynb` **py** format: `notebook-content.py` (see [below for nested schema](#nestedatt--definition)) - `description` (String) The Notebook description. diff --git a/docs/data-sources/report.md b/docs/data-sources/report.md index 5dd3ece..20c3643 100644 --- a/docs/data-sources/report.md +++ b/docs/data-sources/report.md @@ -57,7 +57,7 @@ output "example_definition_report_object" { ### Optional -- `format` (String) The Report format. Possible values: `PBIR-Legacy` +- `format` (String) The Report format. Possible values: `PBIR-Legacy`, `PBIR` - `output_definition` (Boolean) Output definition parts as gzip base64 content? Default: `false` !> Your terraform state file may grow a lot if you output definition content. Only use it when you must use data from the definition. @@ -66,7 +66,7 @@ output "example_definition_report_object" { ### Read-Only -- `definition` (Attributes Map) Definition parts. Possible path keys: `report.json`, `definition.pbir`, `StaticResources/RegisteredResources/*`, `StaticResources/SharedResources/*`. (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Possible path keys: **PBIR-Legacy** format: `StaticResources/RegisteredResources/*`, `StaticResources/SharedResources/*`, `definition.pbir`, `report.json` **PBIR** format: `StaticResources/RegisteredResources/*`, `StaticResources/SharedResources/*`, `definition.pbir`, `definition/pages/*.json`, `definition/report.json`, `definition/version.json` (see [below for nested schema](#nestedatt--definition)) - `description` (String) The Report description. - `display_name` (String) The Report display name. diff --git a/docs/data-sources/semantic_model.md b/docs/data-sources/semantic_model.md index 00d5e71..7e72574 100644 --- a/docs/data-sources/semantic_model.md +++ b/docs/data-sources/semantic_model.md @@ -54,7 +54,7 @@ output "example_definition_bim_object" { ### Optional -- `format` (String) The Semantic Model format. Possible values: `TMSL` +- `format` (String) The Semantic Model format. Possible values: `TMSL`, `TMDL` - `output_definition` (Boolean) Output definition parts as gzip base64 content? Default: `false` !> Your terraform state file may grow a lot if you output definition content. Only use it when you must use data from the definition. @@ -63,7 +63,7 @@ output "example_definition_bim_object" { ### Read-Only -- `definition` (Attributes Map) Definition parts. Possible path keys: `model.bim`, `definition.pbism`, `diagramLayout.json`. (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Possible path keys: **TMSL** format: `definition.pbism`, `diagramLayp.json`, `model.bim` **TMDL** format: `definition.pbism`, `definition/database.tmdl`, `definition/model.tmdl`, `definition/tables/*.tmdl`, `diagramLayp.json` (see [below for nested schema](#nestedatt--definition)) - `description` (String) The Semantic Model description. - `display_name` (String) The Semantic Model display name. diff --git a/docs/data-sources/spark_job_definition.md b/docs/data-sources/spark_job_definition.md index 59c901d..3d6cc75 100644 --- a/docs/data-sources/spark_job_definition.md +++ b/docs/data-sources/spark_job_definition.md @@ -79,7 +79,7 @@ output "example_definition_content_object" { ### Read-Only -- `definition` (Attributes Map) Definition parts. Possible path keys: `SparkJobDefinitionV1.json`. (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Possible path keys: **SparkJobDefinitionV1** format: `SparkJobDefinitionV1.json` (see [below for nested schema](#nestedatt--definition)) - `description` (String) The Spark Job Definition description. - `properties` (Attributes) The Spark Job Definition properties. (see [below for nested schema](#nestedatt--properties)) diff --git a/docs/resources/data_pipeline.md b/docs/resources/data_pipeline.md index 4e13f1e..fd73d0c 100644 --- a/docs/resources/data_pipeline.md +++ b/docs/resources/data_pipeline.md @@ -67,14 +67,14 @@ resource "fabric_data_pipeline" "example_definition_update" { ### Optional -- `definition` (Attributes Map) Definition parts. Accepted path keys: `pipeline-content.json`. Read more about [Data Pipeline definition part paths](https://learn.microsoft.com/fabric/data-factory/pipeline-rest-api). (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Read more about [Data Pipeline definition part paths](https://learn.microsoft.com/fabric/data-factory/pipeline-rest-api). Accepted path keys: **Default** format: `pipeline-content.json` (see [below for nested schema](#nestedatt--definition)) - `definition_update_enabled` (Boolean) Update definition on change of source content. Default: `true`. - `description` (String) The Data Pipeline description. +- `format` (String) The Data Pipeline format. Possible values: `Default` - `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) ### Read-Only -- `format` (String) The Data Pipeline format. Possible values: `NotApplicable` - `id` (String) The Data Pipeline ID. diff --git a/docs/resources/eventhouse.md b/docs/resources/eventhouse.md index 4b2dae9..74938e9 100644 --- a/docs/resources/eventhouse.md +++ b/docs/resources/eventhouse.md @@ -64,14 +64,14 @@ resource "fabric_eventhouse" "example_definition_update" { ### Optional -- `definition` (Attributes Map) Definition parts. Accepted path keys: `EventhouseProperties.json`. Read more about [Eventhouse definition part paths](https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/eventhouse-definition). (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Read more about [Eventhouse definition part paths](https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/eventhouse-definition). Accepted path keys: **Default** format: `EventhouseProperties.json` (see [below for nested schema](#nestedatt--definition)) - `definition_update_enabled` (Boolean) Update definition on change of source content. Default: `true`. - `description` (String) The Eventhouse description. +- `format` (String) The Eventhouse format. Possible values: `Default` - `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) ### Read-Only -- `format` (String) The Eventhouse format. Possible values: `NotApplicable` - `id` (String) The Eventhouse ID. - `properties` (Attributes) The Eventhouse properties. (see [below for nested schema](#nestedatt--properties)) diff --git a/docs/resources/notebook.md b/docs/resources/notebook.md index b831cd3..1cb89c8 100644 --- a/docs/resources/notebook.md +++ b/docs/resources/notebook.md @@ -67,7 +67,7 @@ resource "fabric_notebook" "example_definition_update" { ### Optional -- `definition` (Attributes Map) Definition parts. Accepted path keys: `notebook-content.ipynb`, `notebook-content.py`. Read more about [Notebook definition part paths](https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/notebook-definition). (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Read more about [Notebook definition part paths](https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/notebook-definition). Accepted path keys: **ipynb** format: `notebook-content.ipynb` **py** format: `notebook-content.py` (see [below for nested schema](#nestedatt--definition)) - `definition_update_enabled` (Boolean) Update definition on change of source content. Default: `true`. - `description` (String) The Notebook description. - `format` (String) The Notebook format. Possible values: `ipynb`, `py` diff --git a/docs/resources/report.md b/docs/resources/report.md index b3aa1ea..9723853 100644 --- a/docs/resources/report.md +++ b/docs/resources/report.md @@ -68,9 +68,9 @@ resource "fabric_report" "example_update" { ### Required -- `definition` (Attributes Map) Definition parts. Accepted path keys: `report.json`, `definition.pbir`, `StaticResources/RegisteredResources/*`, `StaticResources/SharedResources/*`. Read more about [Report definition part paths](https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/report-definition). (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Read more about [Report definition part paths](https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/report-definition). Accepted path keys: **PBIR** format: `StaticResources/RegisteredResources/*`, `StaticResources/SharedResources/*`, `definition.pbir`, `definition/pages/*.json`, `definition/report.json`, `definition/version.json` **PBIR-Legacy** format: `StaticResources/RegisteredResources/*`, `StaticResources/SharedResources/*`, `definition.pbir`, `report.json` (see [below for nested schema](#nestedatt--definition)) - `display_name` (String) The Report display name. -- `format` (String) The Report format. Possible values: `PBIR-Legacy` +- `format` (String) The Report format. Possible values: `PBIR-Legacy`, `PBIR` - `workspace_id` (String) The Workspace ID. ### Optional diff --git a/docs/resources/semantic_model.md b/docs/resources/semantic_model.md index 6394f19..87d9b6d 100644 --- a/docs/resources/semantic_model.md +++ b/docs/resources/semantic_model.md @@ -59,9 +59,9 @@ resource "fabric_semantic_model" "example_update" { ### Required -- `definition` (Attributes Map) Definition parts. Accepted path keys: `model.bim`, `definition.pbism`, `diagramLayout.json`. Read more about [Semantic Model definition part paths](https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/semantic-model-definition). (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Read more about [Semantic Model definition part paths](https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/semantic-model-definition). Accepted path keys: **TMSL** format: `definition.pbism`, `diagramLayp.json`, `model.bim` **TMDL** format: `definition.pbism`, `definition/database.tmdl`, `definition/model.tmdl`, `definition/tables/*.tmdl`, `diagramLayp.json` (see [below for nested schema](#nestedatt--definition)) - `display_name` (String) The Semantic Model display name. -- `format` (String) The Semantic Model format. Possible values: `TMSL` +- `format` (String) The Semantic Model format. Possible values: `TMSL`, `TMDL` - `workspace_id` (String) The Workspace ID. ### Optional diff --git a/docs/resources/spark_job_definition.md b/docs/resources/spark_job_definition.md index 7456f93..50a5301 100644 --- a/docs/resources/spark_job_definition.md +++ b/docs/resources/spark_job_definition.md @@ -67,7 +67,7 @@ resource "fabric_spark_job_definition" "example_definition_update" { ### Optional -- `definition` (Attributes Map) Definition parts. Accepted path keys: `SparkJobDefinitionV1.json`. Read more about [Spark Job Definition definition part paths](https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/spark-job-definition). (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Read more about [Spark Job Definition definition part paths](https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/spark-job-definition). Accepted path keys: **SparkJobDefinitionV1** format: `SparkJobDefinitionV1.json` (see [below for nested schema](#nestedatt--definition)) - `definition_update_enabled` (Boolean) Update definition on change of source content. Default: `true`. - `description` (String) The Spark Job Definition description. - `format` (String) The Spark Job Definition format. Possible values: `SparkJobDefinitionV1` diff --git a/internal/framework/validators/regexp_if_attribute_is_one_of.go b/internal/framework/validators/regexp_if_attribute_is_one_of.go new file mode 100644 index 0000000..e3c0a75 --- /dev/null +++ b/internal/framework/validators/regexp_if_attribute_is_one_of.go @@ -0,0 +1,141 @@ +package validators + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +var _ validator.String = RegexpIfAttributeIsOneOfValidator{} + +type RegexpIfAttributeIsOneOfValidator struct { + pathExpression path.Expression + exceptedValues []attr.Value + patterns []string + message string +} + +func RegexpIfAttributeIsOneOf(p path.Expression, exceptedValue []attr.Value, patterns []string, message string) RegexpIfAttributeIsOneOfValidator { + return RegexpIfAttributeIsOneOfValidator{ + pathExpression: p, + exceptedValues: exceptedValue, + patterns: patterns, + message: message, + } +} + +func (v RegexpIfAttributeIsOneOfValidator) Description(_ context.Context) string { + if v.message != "" { + return v.message + } + + return fmt.Sprintf("value must match pattern expression '%s'", strings.Join(v.patterns, ", ")) +} + +func (v RegexpIfAttributeIsOneOfValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v RegexpIfAttributeIsOneOfValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + paths, diags := req.Config.PathMatches(ctx, req.PathExpression.Merge(v.pathExpression)) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + + return + } + + if len(paths) == 0 { + resp.Diagnostics.AddError( + fmt.Sprintf("Invalid configuration for attribute %s", req.Path), + "Path must be set", + ) + + return + } + + p := paths[0] + + // mpVal is the value of the attribute in the path + var mpVal attr.Value + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, p, &mpVal)...) + + if resp.Diagnostics.HasError() { + resp.Diagnostics.AddError( + fmt.Sprintf("Invalid configuration for attribute %s", req.Path), + fmt.Sprintf("Unable to retrieve attribute path: %q", p), + ) + + return + } + + // If the target attribute configuration is unknown or null, there is nothing else to validate + if mpVal.IsNull() || mpVal.IsUnknown() { + return + } + + for _, expectedValue := range v.exceptedValues { + // If the value of the target attribute is equal to one of the expected values, we need to validate the value of the current attribute + if mpVal.Equal(expectedValue) || mpVal.String() == expectedValue.String() { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + resp.Diagnostics.AddAttributeError( + p, + fmt.Sprintf("Invalid configuration for attribute %s", req.Path), + "Value is empty. "+v.Description(ctx), + ) + + return + } + + re, err := v.convertPatternsToRegexp(v.patterns) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Invalid configuration for attribute %s", req.Path), + fmt.Sprintf("Unable to compile regular expression: %q", err), + ) + + return + } + + value := req.ConfigValue.ValueString() + + if !re.MatchString(value) { + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueMatchDiagnostic( + req.Path, + v.Description(ctx), + value, + )) + } + } + } +} + +func (v RegexpIfAttributeIsOneOfValidator) convertPatternsToRegexp(patterns []string) (*regexp.Regexp, error) { + p := make([]string, 0) + + p = append(p, "^(") + + for _, pattern := range patterns { + p = append(p, regexp.QuoteMeta(pattern)) + if pattern != patterns[len(patterns)-1] { + p = append(p, "|") + } + } + + p = append(p, ")$") + + out := strings.Join(p, "") + out = strings.ReplaceAll(out, `\*`, ".+") + + re, err := regexp.Compile(out) + if err != nil { + return nil, err + } + + return re, nil +} diff --git a/internal/pkg/fabricitem/base.go b/internal/pkg/fabricitem/base.go index 45c8c19..20f4db0 100644 --- a/internal/pkg/fabricitem/base.go +++ b/internal/pkg/fabricitem/base.go @@ -3,4 +3,6 @@ package fabricitem -const DefinitionFormatNotApplicable = "NotApplicable" +const ( + DefinitionFormatDefault = "Default" +) diff --git a/internal/pkg/fabricitem/data_item_definition.go b/internal/pkg/fabricitem/data_item_definition.go index 3ad4fe0..5ac8b7a 100644 --- a/internal/pkg/fabricitem/data_item_definition.go +++ b/internal/pkg/fabricitem/data_item_definition.go @@ -204,7 +204,7 @@ func (d *DataSourceFabricItemDefinition) getDefinition(ctx context.Context, mode respGetOpts := &fabcore.ItemsClientBeginGetItemDefinitionOptions{} if !model.Format.IsNull() { - apiFormat := GetDefinitionFormatAPI(d.DefinitionFormats, model.Format.ValueString()) + apiFormat := getDefinitionFormatAPI(d.DefinitionFormats, model.Format.ValueString()) if apiFormat != "" { respGetOpts.Format = azto.Ptr(apiFormat) diff --git a/internal/pkg/fabricitem/data_schema.go b/internal/pkg/fabricitem/data_schema.go index 6156c19..c6bf447 100644 --- a/internal/pkg/fabricitem/data_schema.go +++ b/internal/pkg/fabricitem/data_schema.go @@ -114,22 +114,17 @@ func getDataSourceFabricItemBaseAttributes(ctx context.Context, itemName string, func getDataSourceFabricItemDefinitionAttributes(ctx context.Context, name string, definitionFormats []DefinitionFormat) map[string]schema.Attribute { attributes := make(map[string]schema.Attribute) - formatTypes := GetDefinitionFormats(definitionFormats) - definitionPathKeys := GetDefinitionFormatsPaths(definitionFormats) + formatTypes := getDefinitionFormats(definitionFormats) + definitionFormatsDocs := getDefinitionFormatsPathsDocs(definitionFormats) // format attribute attrFormat := schema.StringAttribute{} - if len(formatTypes) > 1 || (len(formatTypes) == 1 && formatTypes[0] != "") { - attrFormat.MarkdownDescription = fmt.Sprintf("The %s format. Possible values: %s", name, utils.ConvertStringSlicesToString(formatTypes, true, false)) - attrFormat.Optional = true - attrFormat.Validators = []validator.String{ - stringvalidator.OneOf(formatTypes...), - superstringvalidator.RequireIfAttributeIsOneOf(path.MatchRoot("output_definition"), []attr.Value{types.BoolValue(true)}), - } - } else { - attrFormat.MarkdownDescription = fmt.Sprintf("The %s format. Possible values: `%s`", name, DefinitionFormatNotApplicable) - attrFormat.Computed = true + attrFormat.MarkdownDescription = fmt.Sprintf("The %s format. Possible values: %s", name, utils.ConvertStringSlicesToString(formatTypes, true, false)) + attrFormat.Optional = true + attrFormat.Validators = []validator.String{ + stringvalidator.OneOf(formatTypes...), + superstringvalidator.RequireIfAttributeIsOneOf(path.MatchRoot("output_definition"), []attr.Value{types.BoolValue(true)}), } attributes["format"] = attrFormat @@ -156,11 +151,7 @@ func getDataSourceFabricItemDefinitionAttributes(ctx context.Context, name strin }, } - if len(definitionPathKeys) > 0 { - attrDefinition.MarkdownDescription = "Definition parts. Possible path keys: " + utils.ConvertStringSlicesToString(definitionPathKeys, true, false) + "." - } else { - attrDefinition.MarkdownDescription = "Definition parts." - } + attrDefinition.MarkdownDescription = "Definition parts. Possible path keys: " + definitionFormatsDocs attributes["definition"] = attrDefinition diff --git a/internal/pkg/fabricitem/definition.go b/internal/pkg/fabricitem/definition.go index 1b0d6ee..672d96d 100644 --- a/internal/pkg/fabricitem/definition.go +++ b/internal/pkg/fabricitem/definition.go @@ -9,6 +9,8 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" superstringvalidator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/stringvalidator" + + "github.com/microsoft/terraform-provider-fabric/internal/pkg/utils" ) type DefinitionFormat struct { @@ -17,7 +19,7 @@ type DefinitionFormat struct { Paths []string } -func GetDefinitionFormats(values []DefinitionFormat) []string { +func getDefinitionFormats(values []DefinitionFormat) []string { results := make([]string, len(values)) for i, value := range values { @@ -27,11 +29,32 @@ func GetDefinitionFormats(values []DefinitionFormat) []string { return results } -func GetDefinitionFormatsPaths(values []DefinitionFormat) []string { - var results []string +func getDefinitionFormatsPaths(values []DefinitionFormat) map[string][]string { + results := make(map[string][]string) - for _, value := range values { - results = append(results, value.Paths...) + for _, v := range values { + results[v.Type] = v.Paths + } + + return results +} + +func getDefinitionFormatsPathsDocs(values []DefinitionFormat) string { + elements := getDefinitionFormatsPaths(values) + + var results string + + i := 0 + + for k, v := range elements { + results += "**" + k + "** format: " + results += utils.ConvertStringSlicesToString(v, true, true) + + if i != len(elements)-1 { + results += " " + } + + i++ } return results @@ -47,7 +70,7 @@ func GetDefinitionFormatPaths(values []DefinitionFormat, format string) []string return nil } -func GetDefinitionFormatAPI(values []DefinitionFormat, format string) string { +func getDefinitionFormatAPI(values []DefinitionFormat, format string) string { for _, value := range values { if value.Type == format { return value.API diff --git a/internal/pkg/fabricitem/models_resource_item_definition.go b/internal/pkg/fabricitem/models_resource_item_definition.go index 8872e42..1c622fb 100644 --- a/internal/pkg/fabricitem/models_resource_item_definition.go +++ b/internal/pkg/fabricitem/models_resource_item_definition.go @@ -36,11 +36,11 @@ type fabricItemDefinition struct { } func (to *fabricItemDefinition) setFormat(v types.String, definitionFormats []DefinitionFormat) { - if v.ValueString() != DefinitionFormatNotApplicable && v.ValueString() != "" { - apiFormat := GetDefinitionFormatAPI(definitionFormats, v.ValueString()) + if v.ValueString() != DefinitionFormatDefault && v.ValueString() != "" { + apiFormat := getDefinitionFormatAPI(definitionFormats, v.ValueString()) if apiFormat != "" { - to.Format = azto.Ptr(apiFormat) + to.Format = &apiFormat } } } @@ -66,7 +66,7 @@ func (to *fabricItemDefinition) setParts(ctx context.Context, definition superty } to.Parts = append(to.Parts, fabcore.ItemDefinitionPart{ - Path: azto.Ptr(definitionPaths[0]), + Path: &definitionPaths[0], Payload: &content, PayloadType: azto.Ptr(fabcore.PayloadTypeInlineBase64), }) @@ -82,7 +82,7 @@ func (to *fabricItemDefinition) setParts(ctx context.Context, definition superty } to.Parts = append(to.Parts, fabcore.ItemDefinitionPart{ - Path: azto.Ptr(defPartKey), + Path: &defPartKey, Payload: payloadB64, PayloadType: azto.Ptr(fabcore.PayloadTypeInlineBase64), }) @@ -101,7 +101,7 @@ func (to *requestUpdateFabricItemDefinition) setDefinition(ctx context.Context, def.setFormat(format, definitionFormats) - definitionPathKeys := GetDefinitionFormatsPaths(definitionFormats) + definitionPathKeys := GetDefinitionFormatPaths(definitionFormats, format.ValueString()) if diags := def.setParts(ctx, definition, definitionEmpty, definitionPathKeys, definitionUpdateEnabled, true); diags.HasError() { return diags diff --git a/internal/pkg/fabricitem/resource_schema.go b/internal/pkg/fabricitem/resource_schema.go index a688a1a..94efa66 100644 --- a/internal/pkg/fabricitem/resource_schema.go +++ b/internal/pkg/fabricitem/resource_schema.go @@ -207,29 +207,23 @@ func getResourceFabricItemDefinitionAttributes(ctx context.Context, name, format attributes["definition_update_enabled"] = attrDefinitionUpdateEnabled - formatTypes := GetDefinitionFormats(definitionFormats) - definitionPathKeys := GetDefinitionFormatsPaths(definitionFormats) + formatTypes := getDefinitionFormats(definitionFormats) + definitionFormatsDocs := getDefinitionFormatsPathsDocs(definitionFormats) attrFormat := schema.StringAttribute{} - if len(formatTypes) > 1 || (len(formatTypes) == 1 && formatTypes[0] != "") { - attrFormat.MarkdownDescription = fmt.Sprintf("The %s format. Possible values: %s", name, utils.ConvertStringSlicesToString(formatTypes, true, false)) - attrFormat.Validators = []validator.String{ - stringvalidator.OneOf(utils.ConvertEnumsToStringSlices(formatTypes, true)...), - superstringvalidator.RequireIfAttributeIsSet(path.MatchRoot("definition")), - } + attrFormat.MarkdownDescription = fmt.Sprintf("The %s format. Possible values: %s", name, utils.ConvertStringSlicesToString(formatTypes, true, false)) + attrFormat.Validators = []validator.String{ + stringvalidator.OneOf(utils.ConvertEnumsToStringSlices(formatTypes, true)...), + superstringvalidator.RequireIfAttributeIsSet(path.MatchRoot("definition")), + } - if definitionRequired { - attrFormat.Required = true - } else { - attrFormat.Optional = true - attrFormat.Computed = true - attrFormat.Default = stringdefault.StaticString(formatTypeDefault) - } + if definitionRequired { + attrFormat.Required = true } else { - attrFormat.MarkdownDescription = fmt.Sprintf("The %s format. Possible values: `%s`", name, DefinitionFormatNotApplicable) + attrFormat.Optional = true attrFormat.Computed = true - attrFormat.Default = stringdefault.StaticString(DefinitionFormatNotApplicable) + attrFormat.Default = stringdefault.StaticString(formatTypeDefault) } if alongConfiguration { @@ -239,7 +233,7 @@ func getResourceFabricItemDefinitionAttributes(ctx context.Context, name, format attributes["format"] = attrFormat attrDefinition := schema.MapNestedAttribute{} - attrDefinition.MarkdownDescription = fmt.Sprintf("Definition parts. Accepted path keys: %s. Read more about [%s definition part paths](%s).", utils.ConvertStringSlicesToString(definitionPathKeys, true, false), name, definitionPathDocsURL) + attrDefinition.MarkdownDescription = fmt.Sprintf("Definition parts. Read more about [%s definition part paths](%s). Accepted path keys: %s", name, definitionPathDocsURL, definitionFormatsDocs) attrDefinition.CustomType = supertypes.NewMapNestedObjectTypeOf[resourceFabricItemDefinitionPartModel](ctx) attrDefinition.Validators = definitionPathKeysValidator attrDefinition.NestedObject = getResourceFabricItemDefinitionPartSchema(ctx) diff --git a/internal/services/datapipeline/base.go b/internal/services/datapipeline/base.go index 817c298..61a906c 100644 --- a/internal/services/datapipeline/base.go +++ b/internal/services/datapipeline/base.go @@ -24,7 +24,7 @@ const ( var itemDefinitionFormats = []fabricitem.DefinitionFormat{ //nolint:gochecknoglobals { - Type: "", + Type: fabricitem.DefinitionFormatDefault, API: "", Paths: []string{"pipeline-content.json"}, }, diff --git a/internal/services/datapipeline/resource_data_pipeline.go b/internal/services/datapipeline/resource_data_pipeline.go index ff3d6b7..78b4eff 100644 --- a/internal/services/datapipeline/resource_data_pipeline.go +++ b/internal/services/datapipeline/resource_data_pipeline.go @@ -23,11 +23,11 @@ func NewResourceDataPipeline() resource.Resource { ItemDocsSPNSupport, DisplayNameMaxLength: 123, DescriptionMaxLength: 256, - FormatTypeDefault: "", + FormatTypeDefault: fabricitem.DefinitionFormatDefault, DefinitionPathDocsURL: ItemDefinitionPathDocsURL, DefinitionPathKeysValidator: []validator.Map{ mapvalidator.SizeAtMost(1), - mapvalidator.KeysAre(stringvalidator.OneOf(fabricitem.GetDefinitionFormatsPaths(itemDefinitionFormats)...)), + mapvalidator.KeysAre(stringvalidator.OneOf(fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, fabricitem.DefinitionFormatDefault)...)), }, DefinitionRequired: false, DefinitionEmpty: ItemDefinitionEmpty, diff --git a/internal/services/eventhouse/base.go b/internal/services/eventhouse/base.go index 3132ee2..6934e32 100644 --- a/internal/services/eventhouse/base.go +++ b/internal/services/eventhouse/base.go @@ -18,14 +18,14 @@ const ( ItemType = fabcore.ItemTypeEventhouse ItemDocsSPNSupport = common.DocsSPNSupported ItemDocsURL = "https://learn.microsoft.com/fabric/real-time-intelligence/eventhouse" - ItemFormatTypeDefault = "" + ItemFormatTypeDefault = fabricitem.DefinitionFormatDefault ItemDefinitionEmpty = `{}` ItemDefinitionPathDocsURL = "https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/eventhouse-definition" ) var itemDefinitionFormats = []fabricitem.DefinitionFormat{ //nolint:gochecknoglobals { - Type: "", + Type: fabricitem.DefinitionFormatDefault, API: "", Paths: []string{"EventhouseProperties.json"}, }, diff --git a/internal/services/kqldatabase/base.go b/internal/services/kqldatabase/base.go index e54e5de..c82c7a7 100644 --- a/internal/services/kqldatabase/base.go +++ b/internal/services/kqldatabase/base.go @@ -7,6 +7,7 @@ import ( fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" "github.com/microsoft/terraform-provider-fabric/internal/common" + "github.com/microsoft/terraform-provider-fabric/internal/pkg/fabricitem" ) const ( @@ -17,11 +18,14 @@ const ( ItemType = fabcore.ItemTypeKQLDatabase ItemDocsSPNSupport = common.DocsSPNSupported ItemDocsURL = "https://learn.microsoft.com/fabric/real-time-intelligence/create-database" - ItemFormatTypeDefault = "" + ItemFormatTypeDefault = fabricitem.DefinitionFormatDefault ItemDefinitionPathDocsURL = "https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/kql-database-definition" ) -var ( - ItemFormatTypes = []string{""} //nolint:gochecknoglobals - ItemDefinitionPaths = []string{"DatabaseProperties.json", "DatabaseSchema.kql"} //nolint:gochecknoglobals -) +// var itemDefinitionFormats = []fabricitem.DefinitionFormat{ //nolint:gochecknoglobals +// { +// Type: fabricitem.DefinitionFormatDefault, +// API: "", +// Paths: []string{"DatabaseProperties.json", "DatabaseSchema.kql"}, +// }, +// } diff --git a/internal/services/report/base.go b/internal/services/report/base.go index 593775a..bebbe73 100644 --- a/internal/services/report/base.go +++ b/internal/services/report/base.go @@ -28,4 +28,9 @@ var itemDefinitionFormats = []fabricitem.DefinitionFormat{ //nolint:gochecknoglo API: "PBIR-Legacy", Paths: []string{"report.json", "definition.pbir", "StaticResources/RegisteredResources/*", "StaticResources/SharedResources/*"}, }, + { + Type: "PBIR", + API: "PBIR", + Paths: []string{"definition/report.json", "definition/version.json", "definition.pbir", "definition/pages/*.json", "StaticResources/RegisteredResources/*", "StaticResources/SharedResources/*"}, + }, } diff --git a/internal/services/report/resource_report.go b/internal/services/report/resource_report.go index e2bda41..e154867 100644 --- a/internal/services/report/resource_report.go +++ b/internal/services/report/resource_report.go @@ -4,13 +4,14 @@ package report import ( - "regexp" - "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwvalidators "github.com/microsoft/terraform-provider-fabric/internal/framework/validators" "github.com/microsoft/terraform-provider-fabric/internal/pkg/fabricitem" "github.com/microsoft/terraform-provider-fabric/internal/pkg/utils" ) @@ -31,10 +32,18 @@ func NewResourceReport() resource.Resource { DefinitionPathKeysValidator: []validator.Map{ mapvalidator.SizeAtLeast(3), mapvalidator.KeysAre( - stringvalidator.RegexMatches( - regexp.MustCompile(`^(report\.json|definition\.pbir|StaticResources/RegisteredResources/.*|StaticResources/SharedResources/.*)$`), + fwvalidators.RegexpIfAttributeIsOneOf( + path.MatchRoot("format"), + []attr.Value{types.StringValue("PBIR-Legacy")}, + fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, "PBIR-Legacy"), "Definition path must match one of the following: "+utils.ConvertStringSlicesToString(fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, "PBIR-Legacy"), true, false), ), + fwvalidators.RegexpIfAttributeIsOneOf( + path.MatchRoot("format"), + []attr.Value{types.StringValue("PBIR")}, + fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, "PBIR"), + "Definition path must match one of the following: "+utils.ConvertStringSlicesToString(fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, "PBIR"), true, false), + ), ), }, DefinitionRequired: true, diff --git a/internal/services/semanticmodel/base.go b/internal/services/semanticmodel/base.go index a2fc308..cecd314 100644 --- a/internal/services/semanticmodel/base.go +++ b/internal/services/semanticmodel/base.go @@ -26,6 +26,11 @@ var itemDefinitionFormats = []fabricitem.DefinitionFormat{ //nolint:gochecknoglo { Type: "TMSL", API: "TMSL", - Paths: []string{"model.bim", "definition.pbism", "diagramLayout.json"}, + Paths: []string{"model.bim", "definition.pbism", "diagramLayp.json"}, + }, + { + Type: "TMDL", + API: "TMDL", + Paths: []string{"definition/database.tmdl", "definition/model.tmdl", "definition.pbism", "diagramLayp.json", "definition/tables/*.tmdl"}, }, } diff --git a/internal/services/semanticmodel/resource_semantic_model.go b/internal/services/semanticmodel/resource_semantic_model.go index 27203b3..022608c 100644 --- a/internal/services/semanticmodel/resource_semantic_model.go +++ b/internal/services/semanticmodel/resource_semantic_model.go @@ -5,11 +5,15 @@ package semanticmodel import ( "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + fwvalidators "github.com/microsoft/terraform-provider-fabric/internal/framework/validators" "github.com/microsoft/terraform-provider-fabric/internal/pkg/fabricitem" + "github.com/microsoft/terraform-provider-fabric/internal/pkg/utils" ) func NewResourceSemanticModel() resource.Resource { @@ -27,7 +31,20 @@ func NewResourceSemanticModel() resource.Resource { DefinitionPathDocsURL: ItemDefinitionPathDocsURL, DefinitionPathKeysValidator: []validator.Map{ mapvalidator.SizeAtLeast(2), - mapvalidator.KeysAre(stringvalidator.OneOf(fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, "TMSL")...)), + mapvalidator.KeysAre( + fwvalidators.RegexpIfAttributeIsOneOf( + path.MatchRoot("format"), + []attr.Value{types.StringValue("TMSL")}, + fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, "TMSL"), + "Definition path must match one of the following: "+utils.ConvertStringSlicesToString(fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, "TMSL"), true, false), + ), + fwvalidators.RegexpIfAttributeIsOneOf( + path.MatchRoot("format"), + []attr.Value{types.StringValue("TMDL")}, + fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, "TMDL"), + "Definition path must match one of the following: "+utils.ConvertStringSlicesToString(fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, "TMDL"), true, false), + ), + ), }, DefinitionRequired: true, DefinitionEmpty: "",