From a9e96cf4bbec6ae20abfd0eae9a1bcd45317c4d9 Mon Sep 17 00:00:00 2001 From: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com> Date: Mon, 23 Dec 2024 12:00:09 -0800 Subject: [PATCH] feat(fabricitem): enable multi-format support --- .../unreleased/added-20241223-094847.yaml | 7 + .../unreleased/breaking-20241223-094847.yaml | 7 + docs/data-sources/notebook.md | 5 +- docs/data-sources/report.md | 3 +- docs/data-sources/semantic_model.md | 3 +- docs/data-sources/spark_job_definition.md | 3 +- docs/resources/notebook.md | 6 +- docs/resources/report.md | 4 +- docs/resources/semantic_model.md | 4 +- docs/resources/spark_job_definition.md | 4 +- .../fabric_notebook/data-source.tf | 1 + .../data-sources/fabric_report/data-source.tf | 1 + .../fabric_semantic_model/data-source.tf | 1 + .../data-source.tf | 1 + .../resources/fabric_notebook/resource.tf | 2 + examples/resources/fabric_report/resource.tf | 2 + .../fabric_semantic_model/resource.tf | 2 + .../fabric_spark_job_definition/resource.tf | 2 + .../pkg/fabricitem/data_item_definition.go | 19 ++- internal/pkg/fabricitem/data_schema.go | 26 +++- internal/pkg/fabricitem/definition.go | 83 +++++++++++ .../models_resource_item_definition.go | 16 +- ...els_resource_item_definition_properties.go | 16 +- .../fabricitem/resource_item_definition.go | 9 +- .../resource_item_definition_properties.go | 6 +- internal/pkg/fabricitem/resource_schema.go | 67 +++++---- internal/provider/utils/values.go | 12 +- internal/services/datapipeline/base.go | 9 +- .../datapipeline/resource_data_pipeline.go | 5 +- internal/services/notebook/base.go | 37 +++-- internal/services/notebook/data_notebook.go | 5 +- .../services/notebook/data_notebook_test.go | 52 +++++++ .../services/notebook/resource_notebook.go | 8 +- .../notebook/resource_notebook_test.go | 140 ++++++++++++++++-- internal/services/report/base.go | 12 +- internal/services/report/data_report.go | 3 +- internal/services/report/data_report_test.go | 1 + internal/services/report/resource_report.go | 5 +- .../services/report/resource_report_test.go | 10 ++ internal/services/semanticmodel/base.go | 12 +- .../semanticmodel/data_semantic_model.go | 3 +- .../semanticmodel/data_semantic_model_test.go | 1 + .../semanticmodel/resource_semantic_model.go | 5 +- .../resource_semantic_model_test.go | 10 ++ internal/services/sparkjobdefinition/base.go | 12 +- .../data_spark_job_definition.go | 3 +- .../data_spark_job_definition_test.go | 1 + .../resource_spark_job_definition.go | 6 +- .../resource_spark_job_definition_test.go | 10 ++ .../workspace/resource_workspace_git.go | 2 +- .../fixtures/notebook/notebook.py.tmpl | 22 +++ 51 files changed, 545 insertions(+), 141 deletions(-) create mode 100644 .changes/unreleased/added-20241223-094847.yaml create mode 100644 .changes/unreleased/breaking-20241223-094847.yaml create mode 100644 internal/pkg/fabricitem/definition.go create mode 100644 internal/testhelp/fixtures/notebook/notebook.py.tmpl diff --git a/.changes/unreleased/added-20241223-094847.yaml b/.changes/unreleased/added-20241223-094847.yaml new file mode 100644 index 0000000..3e7c6a5 --- /dev/null +++ b/.changes/unreleased/added-20241223-094847.yaml @@ -0,0 +1,7 @@ +kind: added +body: | + Added support for multi-format Notebook Resource/Data-Source. + By using `format` attribute, you can now define the format of the Notebook Resource/Data-Source. Accepted values are `jpynb`, and `py`. +time: 2024-12-23T09:48:47.1324573-08:00 +custom: + Issue: "168" diff --git a/.changes/unreleased/breaking-20241223-094847.yaml b/.changes/unreleased/breaking-20241223-094847.yaml new file mode 100644 index 0000000..050da5f --- /dev/null +++ b/.changes/unreleased/breaking-20241223-094847.yaml @@ -0,0 +1,7 @@ +kind: breaking +body: | + The `format` attribute is now REQUIRED for Resources/Data-Sources with definition support. + Currently applicable to the following Resources/Data-Sources: Report, Notebook, Semantic Model, and Spark Job Definition. +time: 2024-12-23T09:48:47.1324573-08:00 +custom: + Issue: "111" diff --git a/docs/data-sources/notebook.md b/docs/data-sources/notebook.md index bcbc635..5d17e2d 100644 --- a/docs/data-sources/notebook.md +++ b/docs/data-sources/notebook.md @@ -36,6 +36,7 @@ data "fabric_notebook" "example_by_id" { data "fabric_notebook" "example_definition" { id = "11111111-1111-1111-1111-111111111111" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "ipynb" output_definition = true } @@ -68,6 +69,7 @@ output "example_definition_content_object" { ### Optional - `display_name` (String) The Notebook display name. +- `format` (String) The Notebook format. Possible values: `ipynb`, `py`. Default: `ipynb` - `id` (String) The Notebook ID. - `output_definition` (Boolean) Output definition parts as gzip base64 content? Default: `false` @@ -77,9 +79,8 @@ output "example_definition_content_object" { ### Read-Only -- `definition` (Attributes Map) Definition parts. Possible path keys: `notebook-content.ipynb`. (see [below for nested schema](#nestedatt--definition)) +- `definition` (Attributes Map) Definition parts. Possible path keys: `notebook-content.ipynb`, `notebook-content.py`. (see [below for nested schema](#nestedatt--definition)) - `description` (String) The Notebook description. -- `format` (String) The Notebook format. Possible values: `ipynb`. diff --git a/docs/data-sources/report.md b/docs/data-sources/report.md index d1a463b..38fd00b 100644 --- a/docs/data-sources/report.md +++ b/docs/data-sources/report.md @@ -29,6 +29,7 @@ data "fabric_report" "example" { data "fabric_report" "example_definition" { id = "11111111-1111-1111-1111-111111111111" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "PBIR-Legacy" output_definition = true } @@ -56,6 +57,7 @@ output "example_definition_report_object" { ### Optional +- `format` (String) The Report format. Possible values: `PBIR-Legacy`. Default: `PBIR-Legacy` - `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. @@ -67,7 +69,6 @@ output "example_definition_report_object" { - `definition` (Attributes Map) Definition parts. Possible path keys: `report.json`, `definition.pbir`, `StaticResources/RegisteredResources/*`, `StaticResources/SharedResources/*`. (see [below for nested schema](#nestedatt--definition)) - `description` (String) The Report description. - `display_name` (String) The Report display name. -- `format` (String) The Report format. Possible values: `PBIR-Legacy`. diff --git a/docs/data-sources/semantic_model.md b/docs/data-sources/semantic_model.md index ddd44af..caa01a2 100644 --- a/docs/data-sources/semantic_model.md +++ b/docs/data-sources/semantic_model.md @@ -29,6 +29,7 @@ data "fabric_semantic_model" "example" { data "fabric_semantic_model" "example_definition" { id = "11111111-1111-1111-1111-111111111111" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "TMSL" output_definition = true } @@ -53,6 +54,7 @@ output "example_definition_bim_object" { ### Optional +- `format` (String) The Semantic Model format. Possible values: `TMSL`. Default: `TMSL` - `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. @@ -64,7 +66,6 @@ output "example_definition_bim_object" { - `definition` (Attributes Map) Definition parts. Possible path keys: `model.bim`, `definition.pbism`, `diagramLayout.json`. (see [below for nested schema](#nestedatt--definition)) - `description` (String) The Semantic Model description. - `display_name` (String) The Semantic Model display name. -- `format` (String) The Semantic Model format. Possible values: `TMSL`. diff --git a/docs/data-sources/spark_job_definition.md b/docs/data-sources/spark_job_definition.md index 935ea75..ec5524b 100644 --- a/docs/data-sources/spark_job_definition.md +++ b/docs/data-sources/spark_job_definition.md @@ -36,6 +36,7 @@ data "fabric_spark_job_definition" "example_by_name" { data "fabric_spark_job_definition" "example_definition" { id = "11111111-1111-1111-1111-111111111111" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "SparkJobDefinitionV1" output_definition = true } @@ -68,6 +69,7 @@ output "example_definition_content_object" { ### Optional - `display_name` (String) The Spark Job Definition display name. +- `format` (String) The Spark Job Definition format. Possible values: `SparkJobDefinitionV1`. Default: `SparkJobDefinitionV1` - `id` (String) The Spark Job Definition ID. - `output_definition` (Boolean) Output definition parts as gzip base64 content? Default: `false` @@ -79,7 +81,6 @@ output "example_definition_content_object" { - `definition` (Attributes Map) Definition parts. Possible path keys: `SparkJobDefinitionV1.json`. (see [below for nested schema](#nestedatt--definition)) - `description` (String) The Spark Job Definition description. -- `format` (String) The Spark Job Definition format. Possible values: `SparkJobDefinitionV1`. - `properties` (Attributes) The Spark Job Definition properties. (see [below for nested schema](#nestedatt--properties)) diff --git a/docs/resources/notebook.md b/docs/resources/notebook.md index ae60746..b831cd3 100644 --- a/docs/resources/notebook.md +++ b/docs/resources/notebook.md @@ -31,6 +31,7 @@ resource "fabric_notebook" "example_definition_bootstrap" { description = "example with definition bootstrapping" workspace_id = "00000000-0000-0000-0000-000000000000" definition_update_enabled = false + format = "ipynb" definition = { "notebook-content.ipynb" = { source = "${local.path}/notebook.ipynb.tmpl" @@ -43,6 +44,7 @@ resource "fabric_notebook" "example_definition_update" { display_name = "example" description = "example with definition update when source or tokens changed" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "ipynb" definition = { "notebook-content.ipynb" = { source = "${local.path}/notebook.ipynb.tmpl" @@ -65,14 +67,14 @@ resource "fabric_notebook" "example_definition_update" { ### Optional -- `definition` (Attributes Map) Definition parts. Accepted path keys: `notebook-content.ipynb`. 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. 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_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` - `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) ### Read-Only -- `format` (String) The Notebook format. Possible values: `ipynb`. - `id` (String) The Notebook ID. diff --git a/docs/resources/report.md b/docs/resources/report.md index 8412332..b3aa1ea 100644 --- a/docs/resources/report.md +++ b/docs/resources/report.md @@ -24,6 +24,7 @@ resource "fabric_report" "example_bootstrap" { display_name = "example" workspace_id = "00000000-0000-0000-0000-000000000000" definition_update_enabled = false + format = "PBIR-Legacy" definition = { "report.json" = { source = "${local.path}/report.json" @@ -44,6 +45,7 @@ resource "fabric_report" "example_bootstrap" { resource "fabric_report" "example_update" { display_name = "example with update" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "PBIR-Legacy" definition = { "report.json" = { source = "${local.path}/report.json" @@ -68,6 +70,7 @@ resource "fabric_report" "example_update" { - `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)) - `display_name` (String) The Report display name. +- `format` (String) The Report format. Possible values: `PBIR-Legacy` - `workspace_id` (String) The Workspace ID. ### Optional @@ -78,7 +81,6 @@ resource "fabric_report" "example_update" { ### Read-Only -- `format` (String) The Report format. Possible values: `PBIR-Legacy`. - `id` (String) The Report ID. diff --git a/docs/resources/semantic_model.md b/docs/resources/semantic_model.md index f3aa4e5..6394f19 100644 --- a/docs/resources/semantic_model.md +++ b/docs/resources/semantic_model.md @@ -24,6 +24,7 @@ resource "fabric_semantic_model" "example_bootstrap" { display_name = "example" workspace_id = "00000000-0000-0000-0000-000000000000" definition_update_enabled = false + format = "TMSL" definition = { "model.bim" = { source = "${local.path}/model.bim.tmpl" @@ -38,6 +39,7 @@ resource "fabric_semantic_model" "example_bootstrap" { resource "fabric_semantic_model" "example_update" { display_name = "example with update" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "TMSL" definition = { "model.bim" = { source = "${local.path}/model.bim.tmpl" @@ -59,6 +61,7 @@ resource "fabric_semantic_model" "example_update" { - `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)) - `display_name` (String) The Semantic Model display name. +- `format` (String) The Semantic Model format. Possible values: `TMSL` - `workspace_id` (String) The Workspace ID. ### Optional @@ -69,7 +72,6 @@ resource "fabric_semantic_model" "example_update" { ### Read-Only -- `format` (String) The Semantic Model format. Possible values: `TMSL`. - `id` (String) The Semantic Model ID. diff --git a/docs/resources/spark_job_definition.md b/docs/resources/spark_job_definition.md index c08ab09..b38ac41 100644 --- a/docs/resources/spark_job_definition.md +++ b/docs/resources/spark_job_definition.md @@ -31,6 +31,7 @@ resource "fabric_spark_job_definition" "example_definition_bootstrap" { description = "example with definition bootstrapping" workspace_id = "00000000-0000-0000-0000-000000000000" definition_update_enabled = false + format = "SparkJobDefinitionV1" definition = { "SparkJobDefinitionV1.json" = { source = "${local.path}/SparkJobDefinitionV1.json.tmpl" @@ -43,6 +44,7 @@ resource "fabric_spark_job_definition" "example_definition_update" { display_name = "example3" description = "example with definition update when source or tokens changed" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "SparkJobDefinitionV1" definition = { "SparkJobDefinitionV1.json" = { source = "${local.path}/SparkJobDefinitionV1.json.tmpl" @@ -68,11 +70,11 @@ resource "fabric_spark_job_definition" "example_definition_update" { - `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_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` - `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) ### Read-Only -- `format` (String) The Spark Job Definition format. Possible values: `SparkJobDefinitionV1`. - `id` (String) The Spark Job Definition ID. - `properties` (Attributes) The Spark Job Definition properties. (see [below for nested schema](#nestedatt--properties)) diff --git a/examples/data-sources/fabric_notebook/data-source.tf b/examples/data-sources/fabric_notebook/data-source.tf index f3faf46..e721011 100644 --- a/examples/data-sources/fabric_notebook/data-source.tf +++ b/examples/data-sources/fabric_notebook/data-source.tf @@ -15,6 +15,7 @@ data "fabric_notebook" "example_by_id" { data "fabric_notebook" "example_definition" { id = "11111111-1111-1111-1111-111111111111" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "ipynb" output_definition = true } diff --git a/examples/data-sources/fabric_report/data-source.tf b/examples/data-sources/fabric_report/data-source.tf index 677ba50..6fa4b5e 100644 --- a/examples/data-sources/fabric_report/data-source.tf +++ b/examples/data-sources/fabric_report/data-source.tf @@ -8,6 +8,7 @@ data "fabric_report" "example" { data "fabric_report" "example_definition" { id = "11111111-1111-1111-1111-111111111111" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "PBIR-Legacy" output_definition = true } diff --git a/examples/data-sources/fabric_semantic_model/data-source.tf b/examples/data-sources/fabric_semantic_model/data-source.tf index bc1d2b3..a965fee 100644 --- a/examples/data-sources/fabric_semantic_model/data-source.tf +++ b/examples/data-sources/fabric_semantic_model/data-source.tf @@ -8,6 +8,7 @@ data "fabric_semantic_model" "example" { data "fabric_semantic_model" "example_definition" { id = "11111111-1111-1111-1111-111111111111" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "TMSL" output_definition = true } diff --git a/examples/data-sources/fabric_spark_job_definition/data-source.tf b/examples/data-sources/fabric_spark_job_definition/data-source.tf index 0540e7a..a3aaa5f 100644 --- a/examples/data-sources/fabric_spark_job_definition/data-source.tf +++ b/examples/data-sources/fabric_spark_job_definition/data-source.tf @@ -15,6 +15,7 @@ data "fabric_spark_job_definition" "example_by_name" { data "fabric_spark_job_definition" "example_definition" { id = "11111111-1111-1111-1111-111111111111" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "SparkJobDefinitionV1" output_definition = true } diff --git a/examples/resources/fabric_notebook/resource.tf b/examples/resources/fabric_notebook/resource.tf index 2f6e58d..80f6fc2 100644 --- a/examples/resources/fabric_notebook/resource.tf +++ b/examples/resources/fabric_notebook/resource.tf @@ -10,6 +10,7 @@ resource "fabric_notebook" "example_definition_bootstrap" { description = "example with definition bootstrapping" workspace_id = "00000000-0000-0000-0000-000000000000" definition_update_enabled = false + format = "ipynb" definition = { "notebook-content.ipynb" = { source = "${local.path}/notebook.ipynb.tmpl" @@ -22,6 +23,7 @@ resource "fabric_notebook" "example_definition_update" { display_name = "example" description = "example with definition update when source or tokens changed" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "ipynb" definition = { "notebook-content.ipynb" = { source = "${local.path}/notebook.ipynb.tmpl" diff --git a/examples/resources/fabric_report/resource.tf b/examples/resources/fabric_report/resource.tf index 846b03f..4faf413 100644 --- a/examples/resources/fabric_report/resource.tf +++ b/examples/resources/fabric_report/resource.tf @@ -3,6 +3,7 @@ resource "fabric_report" "example_bootstrap" { display_name = "example" workspace_id = "00000000-0000-0000-0000-000000000000" definition_update_enabled = false + format = "PBIR-Legacy" definition = { "report.json" = { source = "${local.path}/report.json" @@ -23,6 +24,7 @@ resource "fabric_report" "example_bootstrap" { resource "fabric_report" "example_update" { display_name = "example with update" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "PBIR-Legacy" definition = { "report.json" = { source = "${local.path}/report.json" diff --git a/examples/resources/fabric_semantic_model/resource.tf b/examples/resources/fabric_semantic_model/resource.tf index 8d536ef..abe5e01 100644 --- a/examples/resources/fabric_semantic_model/resource.tf +++ b/examples/resources/fabric_semantic_model/resource.tf @@ -3,6 +3,7 @@ resource "fabric_semantic_model" "example_bootstrap" { display_name = "example" workspace_id = "00000000-0000-0000-0000-000000000000" definition_update_enabled = false + format = "TMSL" definition = { "model.bim" = { source = "${local.path}/model.bim.tmpl" @@ -17,6 +18,7 @@ resource "fabric_semantic_model" "example_bootstrap" { resource "fabric_semantic_model" "example_update" { display_name = "example with update" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "TMSL" definition = { "model.bim" = { source = "${local.path}/model.bim.tmpl" diff --git a/examples/resources/fabric_spark_job_definition/resource.tf b/examples/resources/fabric_spark_job_definition/resource.tf index 619d28b..eace356 100644 --- a/examples/resources/fabric_spark_job_definition/resource.tf +++ b/examples/resources/fabric_spark_job_definition/resource.tf @@ -10,6 +10,7 @@ resource "fabric_spark_job_definition" "example_definition_bootstrap" { description = "example with definition bootstrapping" workspace_id = "00000000-0000-0000-0000-000000000000" definition_update_enabled = false + format = "SparkJobDefinitionV1" definition = { "SparkJobDefinitionV1.json" = { source = "${local.path}/SparkJobDefinitionV1.json.tmpl" @@ -22,6 +23,7 @@ resource "fabric_spark_job_definition" "example_definition_update" { display_name = "example3" description = "example with definition update when source or tokens changed" workspace_id = "00000000-0000-0000-0000-000000000000" + format = "SparkJobDefinitionV1" definition = { "SparkJobDefinitionV1.json" = { source = "${local.path}/SparkJobDefinitionV1.json.tmpl" diff --git a/internal/pkg/fabricitem/data_item_definition.go b/internal/pkg/fabricitem/data_item_definition.go index ceb63cc..44221c3 100644 --- a/internal/pkg/fabricitem/data_item_definition.go +++ b/internal/pkg/fabricitem/data_item_definition.go @@ -36,8 +36,7 @@ type DataSourceFabricItemDefinition struct { MarkdownDescription string IsDisplayNameUnique bool FormatTypeDefault string - FormatTypes []string - DefinitionPathKeys []string + DefinitionFormats []DefinitionFormat } func NewDataSourceFabricItemDefinition(config DataSourceFabricItemDefinition) datasource.DataSource { @@ -120,10 +119,12 @@ func (d *DataSourceFabricItemDefinition) Read(ctx context.Context, req datasourc return } - data.Format = types.StringNull() - - if d.FormatTypeDefault != "" { - data.Format = types.StringValue(d.FormatTypeDefault) + if data.Format.IsNull() || data.Format.IsUnknown() { + if d.FormatTypeDefault != "" { + data.Format = types.StringValue(d.FormatTypeDefault) + } else { + data.Format = types.StringNull() + } } resp.Diagnostics.Append(resp.State.Set(ctx, data)...) @@ -203,7 +204,11 @@ func (d *DataSourceFabricItemDefinition) getDefinition(ctx context.Context, mode respGetOpts := &fabcore.ItemsClientBeginGetItemDefinitionOptions{} if !model.Format.IsNull() { - respGetOpts.Format = model.Format.ValueStringPointer() + apiFormat := GetDefinitionFormatAPI(d.DefinitionFormats, model.Format.ValueString()) + + if apiFormat != "" { + respGetOpts.Format = azto.Ptr(apiFormat) + } } respGet, err := d.client.GetItemDefinition(ctx, model.WorkspaceID.ValueString(), model.ID.ValueString(), respGetOpts) diff --git a/internal/pkg/fabricitem/data_schema.go b/internal/pkg/fabricitem/data_schema.go index 7cdbb7b..8e30a92 100644 --- a/internal/pkg/fabricitem/data_schema.go +++ b/internal/pkg/fabricitem/data_schema.go @@ -8,8 +8,14 @@ import ( "fmt" supertypes "github.com/FrangipaneTeam/terraform-plugin-framework-supertypes" + superstringvalidator "github.com/FrangipaneTeam/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" "github.com/microsoft/terraform-provider-fabric/internal/pkg/utils" @@ -27,7 +33,7 @@ func GetDataSourceFabricItemSchema(ctx context.Context, d DataSourceFabricItem) func GetDataSourceFabricItemDefinitionSchema(ctx context.Context, d DataSourceFabricItemDefinition) schema.Schema { attributes := getDataSourceFabricItemBaseAttributes(ctx, d.Name, d.IsDisplayNameUnique) - for key, value := range getDataSourceFabricItemDefinitionAttributes(ctx, d.Name, d.FormatTypes, d.DefinitionPathKeys) { + for key, value := range getDataSourceFabricItemDefinitionAttributes(ctx, d.Name, d.FormatTypeDefault, d.DefinitionFormats) { attributes[key] = value } @@ -51,7 +57,7 @@ func GetDataSourceFabricItemDefinitionPropertiesSchema(ctx context.Context, d Da attributes := getDataSourceFabricItemBaseAttributes(ctx, d.Name, d.IsDisplayNameUnique) attributes["properties"] = properties - for key, value := range getDataSourceFabricItemDefinitionAttributes(ctx, d.Name, d.FormatTypes, d.DefinitionPathKeys) { + for key, value := range getDataSourceFabricItemDefinitionAttributes(ctx, d.Name, d.FormatTypeDefault, d.DefinitionFormats) { attributes[key] = value } @@ -65,7 +71,7 @@ func GetDataSourceFabricItemDefinitionPropertiesSchema1[Ttfprop, Titemprop any]( attributes := getDataSourceFabricItemBaseAttributes(ctx, d.Name, d.IsDisplayNameUnique) attributes["properties"] = d.PropertiesSchema - for key, value := range getDataSourceFabricItemDefinitionAttributes(ctx, d.Name, d.FormatTypes, d.DefinitionPathKeys) { + for key, value := range getDataSourceFabricItemDefinitionAttributes(ctx, d.Name, d.FormatTypeDefault, d.DefinitionFormats) { attributes[key] = value } @@ -118,13 +124,21 @@ func getDataSourceFabricItemBaseAttributes(ctx context.Context, itemName string, } // Helper function to get Fabric Item data-source definition attributes. -func getDataSourceFabricItemDefinitionAttributes(ctx context.Context, name string, formatTypes, definitionPathKeys []string) map[string]schema.Attribute { +func getDataSourceFabricItemDefinitionAttributes(ctx context.Context, name, formatTypeDefault string, definitionFormatTypes []DefinitionFormat) map[string]schema.Attribute { attributes := make(map[string]schema.Attribute) - if len(formatTypes) > 0 { + formatTypes := GetDefinitionFormats(definitionFormatTypes) + definitionPathKeys := GetDefinitionFormatsPaths(definitionFormatTypes) + + if len(formatTypes) > 1 || (len(formatTypes) == 1 && formatTypes[0] != "") { attributes["format"] = schema.StringAttribute{ - MarkdownDescription: fmt.Sprintf("The %s format. Possible values: %s.", name, utils.ConvertStringSlicesToString(formatTypes, true, false)), + MarkdownDescription: fmt.Sprintf("The %s format. Possible values: %s. Default: `%s`", name, utils.ConvertStringSlicesToString(formatTypes, true, false), formatTypeDefault), + Optional: true, Computed: true, + Validators: []validator.String{ + stringvalidator.OneOf(formatTypes...), + superstringvalidator.RequireIfAttributeIsOneOf(path.MatchRoot("output_definition"), []attr.Value{types.BoolValue(true)}), + }, } } else { attributes["format"] = schema.StringAttribute{ diff --git a/internal/pkg/fabricitem/definition.go b/internal/pkg/fabricitem/definition.go new file mode 100644 index 0000000..b16a1bc --- /dev/null +++ b/internal/pkg/fabricitem/definition.go @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package fabricitem + +import ( + superstringvalidator "github.com/FrangipaneTeam/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/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type DefinitionFormat struct { + Type string + API string + Paths []string +} + +func GetDefinitionFormats(values []DefinitionFormat) []string { + results := make([]string, len(values)) + + for i, value := range values { + results[i] = value.Type + } + + return results +} + +func GetDefinitionFormatsPaths(values []DefinitionFormat) []string { + var results []string + + for _, value := range values { + results = append(results, value.Paths...) + } + + return results +} + +func GetDefinitionFormatPaths(values []DefinitionFormat, format string) []string { + for _, value := range values { + if value.Type == format { + return value.Paths + } + } + + return nil +} + +func GetDefinitionFormatAPI(values []DefinitionFormat, format string) string { + for _, value := range values { + if value.Type == format { + return value.API + } + } + + return "" +} + +func DefinitionPathKeysValidator(values []DefinitionFormat) []validator.String { + results := make([]validator.String, 0, len(values)) + + for _, value := range values { + paths := []superstringvalidator.OneOfWithDescriptionIfAttributeIsOneOfValues{} + + for _, p := range value.Paths { + paths = append(paths, superstringvalidator.OneOfWithDescriptionIfAttributeIsOneOfValues{ + Value: p, + Description: p, + }) + } + + stringValidator := superstringvalidator.OneOfWithDescriptionIfAttributeIsOneOf( + path.MatchRoot("format"), + []attr.Value{types.StringValue(value.Type)}, + paths..., + ) + + results = append(results, stringValidator) + } + + return results +} diff --git a/internal/pkg/fabricitem/models_resource_item_definition.go b/internal/pkg/fabricitem/models_resource_item_definition.go index 2cfc742..f72a744 100644 --- a/internal/pkg/fabricitem/models_resource_item_definition.go +++ b/internal/pkg/fabricitem/models_resource_item_definition.go @@ -35,9 +35,13 @@ type fabricItemDefinition struct { fabcore.ItemDefinition } -func (to *fabricItemDefinition) set(ctx context.Context, from resourceFabricItemDefinitionModel, update bool, definitionEmpty string, definitionPaths []string) diag.Diagnostics { //revive:disable-line:flag-parameter +func (to *fabricItemDefinition) set(ctx context.Context, from resourceFabricItemDefinitionModel, update bool, definitionEmpty string, definitionPaths []string, definitionFormats []DefinitionFormat) diag.Diagnostics { //revive:disable-line:flag-parameter if from.Format.ValueString() != DefinitionFormatNotApplicable { - to.Format = from.Format.ValueStringPointer() + apiFormat := GetDefinitionFormatAPI(definitionFormats, from.Format.ValueString()) + + if apiFormat != "" { + to.Format = azto.Ptr(apiFormat) + } } to.Parts = []fabcore.ItemDefinitionPart{} @@ -90,7 +94,7 @@ type requestCreateFabricItemDefinition struct { fabcore.CreateItemRequest } -func (to *requestCreateFabricItemDefinition) set(ctx context.Context, from resourceFabricItemDefinitionModel, itemType fabcore.ItemType) diag.Diagnostics { +func (to *requestCreateFabricItemDefinition) set(ctx context.Context, from resourceFabricItemDefinitionModel, itemType fabcore.ItemType, definitionFormats []DefinitionFormat) diag.Diagnostics { to.DisplayName = from.DisplayName.ValueStringPointer() to.Description = from.Description.ValueStringPointer() to.Type = azto.Ptr(itemType) @@ -98,7 +102,7 @@ func (to *requestCreateFabricItemDefinition) set(ctx context.Context, from resou if !from.Definition.IsNull() && !from.Definition.IsUnknown() { var def fabricItemDefinition - if diags := def.set(ctx, from, false, "", []string{}); diags.HasError() { + if diags := def.set(ctx, from, false, "", []string{}, definitionFormats); diags.HasError() { return diags } @@ -121,10 +125,10 @@ type requestUpdateFabricItemDefinitionDefinition struct { fabcore.UpdateItemDefinitionRequest } -func (to *requestUpdateFabricItemDefinitionDefinition) set(ctx context.Context, from resourceFabricItemDefinitionModel, definitionEmpty string, definitionPaths []string) diag.Diagnostics { +func (to *requestUpdateFabricItemDefinitionDefinition) set(ctx context.Context, from resourceFabricItemDefinitionModel, definitionEmpty string, definitionPaths []string, definitionFormats []DefinitionFormat) diag.Diagnostics { var def fabricItemDefinition - if diags := def.set(ctx, from, true, definitionEmpty, definitionPaths); diags.HasError() { + if diags := def.set(ctx, from, true, definitionEmpty, definitionPaths, definitionFormats); diags.HasError() { return diags } diff --git a/internal/pkg/fabricitem/models_resource_item_definition_properties.go b/internal/pkg/fabricitem/models_resource_item_definition_properties.go index 37abc20..622a520 100644 --- a/internal/pkg/fabricitem/models_resource_item_definition_properties.go +++ b/internal/pkg/fabricitem/models_resource_item_definition_properties.go @@ -39,9 +39,13 @@ type FabricItemDefinitionProperties[Ttfprop, Titemprop any] struct { //revive:di fabcore.ItemDefinition } -func (to *FabricItemDefinitionProperties[Ttfprop, Titemprop]) set(ctx context.Context, from ResourceFabricItemDefinitionPropertiesModel[Ttfprop, Titemprop], update bool, definitionEmpty string, definitionPaths []string) diag.Diagnostics { //revive:disable-line:flag-parameter,confusing-naming +func (to *FabricItemDefinitionProperties[Ttfprop, Titemprop]) set(ctx context.Context, from ResourceFabricItemDefinitionPropertiesModel[Ttfprop, Titemprop], update bool, definitionEmpty string, definitionPaths []string, definitionFormats []DefinitionFormat) diag.Diagnostics { //revive:disable-line:flag-parameter,confusing-naming if from.Format.ValueString() != DefinitionFormatNotApplicable { - to.Format = from.Format.ValueStringPointer() + apiFormat := GetDefinitionFormatAPI(definitionFormats, from.Format.ValueString()) + + if apiFormat != "" { + to.Format = azto.Ptr(apiFormat) + } } to.Parts = []fabcore.ItemDefinitionPart{} @@ -94,7 +98,7 @@ type requestCreateFabricItemDefinitionProperties[Ttfprop, Titemprop any] struct fabcore.CreateItemRequest } -func (to *requestCreateFabricItemDefinitionProperties[Ttfprop, Titemprop]) set(ctx context.Context, from ResourceFabricItemDefinitionPropertiesModel[Ttfprop, Titemprop], itemType fabcore.ItemType) diag.Diagnostics { //revive:disable-line:confusing-naming +func (to *requestCreateFabricItemDefinitionProperties[Ttfprop, Titemprop]) set(ctx context.Context, from ResourceFabricItemDefinitionPropertiesModel[Ttfprop, Titemprop], itemType fabcore.ItemType, definitionFormats []DefinitionFormat) diag.Diagnostics { //revive:disable-line:confusing-naming to.DisplayName = from.DisplayName.ValueStringPointer() to.Description = from.Description.ValueStringPointer() to.Type = azto.Ptr(itemType) @@ -102,7 +106,7 @@ func (to *requestCreateFabricItemDefinitionProperties[Ttfprop, Titemprop]) set(c if !from.Definition.IsNull() && !from.Definition.IsUnknown() { var def FabricItemDefinitionProperties[Ttfprop, Titemprop] - if diags := def.set(ctx, from, false, "", []string{}); diags.HasError() { + if diags := def.set(ctx, from, false, "", []string{}, definitionFormats); diags.HasError() { return diags } @@ -125,10 +129,10 @@ type requestUpdateFabricItemDefinitionPropertiesDefinition[Ttfprop, Titemprop an fabcore.UpdateItemDefinitionRequest } -func (to *requestUpdateFabricItemDefinitionPropertiesDefinition[Ttfprop, Titemprop]) set(ctx context.Context, from ResourceFabricItemDefinitionPropertiesModel[Ttfprop, Titemprop], definitionEmpty string, definitionPaths []string) diag.Diagnostics { //revive:disable-line:confusing-naming +func (to *requestUpdateFabricItemDefinitionPropertiesDefinition[Ttfprop, Titemprop]) set(ctx context.Context, from ResourceFabricItemDefinitionPropertiesModel[Ttfprop, Titemprop], definitionEmpty string, definitionPaths []string, definitionFormats []DefinitionFormat) diag.Diagnostics { //revive:disable-line:confusing-naming var def FabricItemDefinitionProperties[Ttfprop, Titemprop] - if diags := def.set(ctx, from, true, definitionEmpty, definitionPaths); diags.HasError() { + if diags := def.set(ctx, from, true, definitionEmpty, definitionPaths, definitionFormats); diags.HasError() { return diags } diff --git a/internal/pkg/fabricitem/resource_item_definition.go b/internal/pkg/fabricitem/resource_item_definition.go index 97c4672..f867e23 100644 --- a/internal/pkg/fabricitem/resource_item_definition.go +++ b/internal/pkg/fabricitem/resource_item_definition.go @@ -43,12 +43,11 @@ type ResourceFabricItemDefinition struct { DisplayNameMaxLength int DescriptionMaxLength int FormatTypeDefault string - FormatTypes []string DefinitionPathDocsURL string - DefinitionPathKeys []string DefinitionPathKeysValidator []validator.Map DefinitionRequired bool DefinitionEmpty string + DefinitionFormats []DefinitionFormat } func NewResourceFabricItemDefinition(config ResourceFabricItemDefinition) resource.Resource { @@ -147,7 +146,7 @@ func (r *ResourceFabricItemDefinition) Create(ctx context.Context, req resource. var reqCreate requestCreateFabricItemDefinition - if resp.Diagnostics.Append(reqCreate.set(ctx, plan, r.Type)...); resp.Diagnostics.HasError() { + if resp.Diagnostics.Append(reqCreate.set(ctx, plan, r.Type, r.DefinitionFormats)...); resp.Diagnostics.HasError() { return } @@ -411,7 +410,9 @@ func (r *ResourceFabricItemDefinition) checkUpdateItem(plan, state resourceFabri func (r *ResourceFabricItemDefinition) checkUpdateDefinition(ctx context.Context, plan, state resourceFabricItemDefinitionModel, reqUpdate *requestUpdateFabricItemDefinitionDefinition) (bool, diag.Diagnostics) { if !plan.Definition.Equal(state.Definition) && plan.DefinitionUpdateEnabled.ValueBool() { - if diags := reqUpdate.set(ctx, plan, r.DefinitionEmpty, r.DefinitionPathKeys); diags.HasError() { + definitionPathKeys := GetDefinitionFormatsPaths(r.DefinitionFormats) + + if diags := reqUpdate.set(ctx, plan, r.DefinitionEmpty, definitionPathKeys, r.DefinitionFormats); diags.HasError() { return false, diags } diff --git a/internal/pkg/fabricitem/resource_item_definition_properties.go b/internal/pkg/fabricitem/resource_item_definition_properties.go index affc75b..a97a5d1 100644 --- a/internal/pkg/fabricitem/resource_item_definition_properties.go +++ b/internal/pkg/fabricitem/resource_item_definition_properties.go @@ -136,7 +136,7 @@ func (r *ResourceFabricItemDefinitionProperties[Ttfprop, Titemprop]) Create(ctx var reqCreate requestCreateFabricItemDefinitionProperties[Ttfprop, Titemprop] - if resp.Diagnostics.Append(reqCreate.set(ctx, plan, r.Type)...); resp.Diagnostics.HasError() { + if resp.Diagnostics.Append(reqCreate.set(ctx, plan, r.Type, r.DefinitionFormats)...); resp.Diagnostics.HasError() { return } @@ -414,7 +414,9 @@ func (r *ResourceFabricItemDefinitionProperties[Ttfprop, Titemprop]) checkUpdate func (r *ResourceFabricItemDefinitionProperties[Ttfprop, Titemprop]) checkUpdateDefinition(ctx context.Context, plan, state ResourceFabricItemDefinitionPropertiesModel[Ttfprop, Titemprop], reqUpdate *requestUpdateFabricItemDefinitionPropertiesDefinition[Ttfprop, Titemprop]) (bool, diag.Diagnostics) { if !plan.Definition.Equal(state.Definition) && plan.DefinitionUpdateEnabled.ValueBool() { - if diags := reqUpdate.set(ctx, plan, r.DefinitionEmpty, r.DefinitionPathKeys); diags.HasError() { + definitionPathKeys := GetDefinitionFormatsPaths(r.DefinitionFormats) + + if diags := reqUpdate.set(ctx, plan, r.DefinitionEmpty, definitionPathKeys, r.DefinitionFormats); diags.HasError() { return false, diags } diff --git a/internal/pkg/fabricitem/resource_schema.go b/internal/pkg/fabricitem/resource_schema.go index 8d86724..9b8a953 100644 --- a/internal/pkg/fabricitem/resource_schema.go +++ b/internal/pkg/fabricitem/resource_schema.go @@ -9,6 +9,7 @@ import ( "regexp" supertypes "github.com/FrangipaneTeam/terraform-plugin-framework-supertypes" + superstringvalidator "github.com/FrangipaneTeam/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -37,7 +38,7 @@ func GetResourceFabricItemSchema(ctx context.Context, r ResourceFabricItem) sche func GetResourceFabricItemDefinitionSchema(ctx context.Context, r ResourceFabricItemDefinition) schema.Schema { attributes := getResourceFabricItemBaseAttributes(ctx, r.Name, r.DisplayNameMaxLength, r.DescriptionMaxLength, r.NameRenameAllowed) - for key, value := range getResourceFabricItemDefinitionAttributes(ctx, r.Name, r.FormatTypeDefault, r.FormatTypes, r.DefinitionPathDocsURL, r.DefinitionPathKeys, r.DefinitionPathKeysValidator, r.DefinitionRequired) { + for key, value := range getResourceFabricItemDefinitionAttributes(ctx, r.Name, r.DefinitionPathDocsURL, r.DefinitionFormats, r.DefinitionPathKeysValidator, r.DefinitionRequired) { attributes[key] = value } @@ -61,7 +62,7 @@ func GetResourceFabricItemDefinitionPropertiesSchema(ctx context.Context, r Reso attributes := getResourceFabricItemBaseAttributes(ctx, r.Name, r.DisplayNameMaxLength, r.DescriptionMaxLength, r.NameRenameAllowed) attributes["properties"] = properties - for key, value := range getResourceFabricItemDefinitionAttributes(ctx, r.Name, r.FormatTypeDefault, r.FormatTypes, r.DefinitionPathDocsURL, r.DefinitionPathKeys, r.DefinitionPathKeysValidator, r.DefinitionRequired) { + for key, value := range getResourceFabricItemDefinitionAttributes(ctx, r.Name, r.DefinitionPathDocsURL, r.DefinitionFormats, r.DefinitionPathKeysValidator, r.DefinitionRequired) { attributes[key] = value } @@ -75,7 +76,7 @@ func GetResourceFabricItemDefinitionPropertiesSchema1[Ttfprop, Titemprop any](ct attributes := getResourceFabricItemBaseAttributes(ctx, r.Name, r.DisplayNameMaxLength, r.DescriptionMaxLength, r.NameRenameAllowed) attributes["properties"] = r.PropertiesSchema - for key, value := range getResourceFabricItemDefinitionAttributes(ctx, r.Name, r.FormatTypeDefault, r.FormatTypes, r.DefinitionPathDocsURL, r.DefinitionPathKeys, r.DefinitionPathKeysValidator, r.DefinitionRequired) { + for key, value := range getResourceFabricItemDefinitionAttributes(ctx, r.Name, r.DefinitionPathDocsURL, r.DefinitionFormats, r.DefinitionPathKeysValidator, r.DefinitionRequired) { attributes[key] = value } @@ -147,7 +148,7 @@ func getResourceFabricItemBaseAttributes(ctx context.Context, name string, displ } // Helper function to get Fabric Item definition attributes. -func getResourceFabricItemDefinitionAttributes(ctx context.Context, name, formatTypeDefault string, formatTypes []string, definitionPathDocsURL string, definitionPathKeys []string, definitionPathKeysValidator []validator.Map, definitionRequired bool) map[string]schema.Attribute { //revive:disable-line:flag-parameter +func getResourceFabricItemDefinitionAttributes(ctx context.Context, name, definitionPathDocsURL string, definitionFormatTypes []DefinitionFormat, definitionPathKeysValidator []validator.Map, definitionRequired bool) map[string]schema.Attribute { //revive:disable-line:flag-parameter attributes := make(map[string]schema.Attribute) attributes["definition_update_enabled"] = schema.BoolAttribute{ @@ -157,38 +158,48 @@ func getResourceFabricItemDefinitionAttributes(ctx context.Context, name, format Default: booldefault.StaticBool(true), } - if len(formatTypes) > 0 { - attributes["format"] = schema.StringAttribute{ - MarkdownDescription: fmt.Sprintf("The %s format. Possible values: %s.", name, utils.ConvertStringSlicesToString(formatTypes, true, false)), - Computed: true, - Default: stringdefault.StaticString(formatTypeDefault), + formatTypes := GetDefinitionFormats(definitionFormatTypes) + definitionPathKeys := GetDefinitionFormatsPaths(definitionFormatTypes) + + // 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.Validators = []validator.String{ + stringvalidator.OneOf(utils.ConvertEnumsToStringSlices(formatTypes, true)...), + superstringvalidator.RequireIfAttributeIsSet(path.MatchRoot("definition")), } - } else { - attributes["format"] = schema.StringAttribute{ - MarkdownDescription: fmt.Sprintf("The %s format. Possible values: `%s`", name, DefinitionFormatNotApplicable), - Computed: true, - Default: stringdefault.StaticString(DefinitionFormatNotApplicable), + + if definitionRequired { + attrFormat.Required = true + } else { + attrFormat.Optional = true } + } else { + attrFormat.MarkdownDescription = fmt.Sprintf("The %s format. Possible values: `%s`", name, DefinitionFormatNotApplicable) + attrFormat.Computed = true + attrFormat.Default = stringdefault.StaticString(DefinitionFormatNotApplicable) } + attributes["format"] = attrFormat + + // definition attribute + 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.CustomType = supertypes.NewMapNestedObjectTypeOf[ResourceFabricItemDefinitionPartModel](ctx) + attrDefinition.Validators = definitionPathKeysValidator + attrDefinition.NestedObject = getResourceFabricItemDefinitionPartSchema(ctx) + if definitionRequired { - attributes["definition"] = schema.MapNestedAttribute{ - MarkdownDescription: fmt.Sprintf("Definition parts. Accepted path keys: %s. Read more about [%s definition part paths](%s).", utils.ConvertStringSlicesToString(definitionPathKeys, true, false), name, definitionPathDocsURL), - Required: true, - CustomType: supertypes.NewMapNestedObjectTypeOf[ResourceFabricItemDefinitionPartModel](ctx), - Validators: definitionPathKeysValidator, - NestedObject: getResourceFabricItemDefinitionPartSchema(ctx), - } + attrDefinition.Required = true } else { - attributes["definition"] = schema.MapNestedAttribute{ - MarkdownDescription: fmt.Sprintf("Definition parts. Accepted path keys: %s. Read more about [%s definition part paths](%s).", utils.ConvertStringSlicesToString(definitionPathKeys, true, false), name, definitionPathDocsURL), - Optional: true, - CustomType: supertypes.NewMapNestedObjectTypeOf[ResourceFabricItemDefinitionPartModel](ctx), - Validators: definitionPathKeysValidator, - NestedObject: getResourceFabricItemDefinitionPartSchema(ctx), - } + attrDefinition.Optional = true } + attributes["definition"] = attrDefinition + return attributes } diff --git a/internal/provider/utils/values.go b/internal/provider/utils/values.go index cf6877d..5fe05ca 100644 --- a/internal/provider/utils/values.go +++ b/internal/provider/utils/values.go @@ -16,10 +16,10 @@ import ( func GetValueOrFileValue(attValue, attFile string, value, file types.String) (string, error) { valueResult := value.ValueString() - if path := file.ValueString(); path != "" { - fileRaw, err := os.ReadFile(path) + if p := file.ValueString(); p != "" { + fileRaw, err := os.ReadFile(p) if err != nil { - return "", fmt.Errorf("reading '%s' from file %q: %w", attFile, path, err) + return "", fmt.Errorf("reading '%s' from file %q: %w", attFile, p, err) } fileResult := strings.TrimSpace(string(fileRaw)) @@ -36,10 +36,10 @@ func GetValueOrFileValue(attValue, attFile string, value, file types.String) (st func GetCertOrFileCert(attValue, attFile string, value, file types.String) (string, error) { valueResult := strings.TrimSpace(value.ValueString()) - if path := file.ValueString(); path != "" { - b64, err := auth.ConvertFileToBase64(path) + if p := file.ValueString(); p != "" { + b64, err := auth.ConvertFileToBase64(p) if err != nil { - return "", fmt.Errorf("reading '%s' from file %q: %w", attFile, path, err) + return "", fmt.Errorf("reading '%s' from file %q: %w", attFile, p, err) } fileResult := strings.TrimSpace(b64) diff --git a/internal/services/datapipeline/base.go b/internal/services/datapipeline/base.go index 89ec580..a372645 100644 --- a/internal/services/datapipeline/base.go +++ b/internal/services/datapipeline/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 ( @@ -21,4 +22,10 @@ const ( ItemDefinitionPathDocsURL = "https://learn.microsoft.com/fabric/data-factory/pipeline-rest-api" ) -var ItemDefinitionPaths = []string{"pipeline-content.json"} //nolint:gochecknoglobals +var itemDefinitionFormats = []fabricitem.DefinitionFormat{ + { + Type: "", + 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 2730140..ff3d6b7 100644 --- a/internal/services/datapipeline/resource_data_pipeline.go +++ b/internal/services/datapipeline/resource_data_pipeline.go @@ -24,15 +24,14 @@ func NewResourceDataPipeline() resource.Resource { DisplayNameMaxLength: 123, DescriptionMaxLength: 256, FormatTypeDefault: "", - FormatTypes: []string{}, DefinitionPathDocsURL: ItemDefinitionPathDocsURL, - DefinitionPathKeys: ItemDefinitionPaths, DefinitionPathKeysValidator: []validator.Map{ mapvalidator.SizeAtMost(1), - mapvalidator.KeysAre(stringvalidator.OneOf(ItemDefinitionPaths...)), + mapvalidator.KeysAre(stringvalidator.OneOf(fabricitem.GetDefinitionFormatsPaths(itemDefinitionFormats)...)), }, DefinitionRequired: false, DefinitionEmpty: ItemDefinitionEmpty, + DefinitionFormats: itemDefinitionFormats, } return fabricitem.NewResourceFabricItemDefinition(config) diff --git a/internal/services/notebook/base.go b/internal/services/notebook/base.go index 881e382..7ad0e38 100644 --- a/internal/services/notebook/base.go +++ b/internal/services/notebook/base.go @@ -7,22 +7,31 @@ 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 ( - ItemName = "Notebook" - ItemTFName = "notebook" - ItemsName = "Notebooks" - ItemsTFName = "notebooks" - ItemType = fabcore.ItemTypeNotebook - ItemDocsSPNSupport = common.DocsSPNSupported - ItemDocsURL = "https://learn.microsoft.com/fabric/data-engineering/how-to-use-notebook" - ItemFormatTypeDefault = "ipynb" - ItemDefinitionEmptyIPYNB = `{"cells":[{"cell_type":"code","metadata":{},"source":["# Welcome to your notebook"]}],"metadata":{"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":5}` - ItemDefinitionPathDocsURL = "https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/notebook-definition" + ItemName = "Notebook" + ItemTFName = "notebook" + ItemsName = "Notebooks" + ItemsTFName = "notebooks" + ItemType = fabcore.ItemTypeNotebook + ItemDocsSPNSupport = common.DocsSPNSupported + ItemDocsURL = "https://learn.microsoft.com/fabric/data-engineering/how-to-use-notebook" + ItemDefinitionFormatTypeDefault = "ipynb" + ItemDefinitionEmptyIPYNB = `{"cells":[{"cell_type":"code","metadata":{},"source":["# Welcome to your notebook"]}],"metadata":{"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":5}` + ItemDefinitionPathDocsURL = "https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/notebook-definition" ) -var ( - ItemFormatTypes = []string{"ipynb"} //nolint:gochecknoglobals - ItemDefinitionPathsIPYNB = []string{"notebook-content.ipynb"} //nolint:gochecknoglobals -) +var itemDefinitionFormats = []fabricitem.DefinitionFormat{ + { + Type: "ipynb", + API: "ipynb", + Paths: []string{"notebook-content.ipynb"}, + }, + { + Type: "py", + API: "", + Paths: []string{"notebook-content.py"}, + }, +} diff --git a/internal/services/notebook/data_notebook.go b/internal/services/notebook/data_notebook.go index d3c9e29..50907fb 100644 --- a/internal/services/notebook/data_notebook.go +++ b/internal/services/notebook/data_notebook.go @@ -18,9 +18,8 @@ func NewDataSourceNotebook() datasource.DataSource { "Use this data source to fetch a [" + ItemName + "](" + ItemDocsURL + ").\n\n" + ItemDocsSPNSupport, IsDisplayNameUnique: true, - FormatTypeDefault: ItemFormatTypeDefault, - FormatTypes: ItemFormatTypes, - DefinitionPathKeys: ItemDefinitionPathsIPYNB, + FormatTypeDefault: ItemDefinitionFormatTypeDefault, + DefinitionFormats: itemDefinitionFormats, } return fabricitem.NewDataSourceFabricItemDefinition(config) diff --git a/internal/services/notebook/data_notebook_test.go b/internal/services/notebook/data_notebook_test.go index a8df755..fbb326e 100644 --- a/internal/services/notebook/data_notebook_test.go +++ b/internal/services/notebook/data_notebook_test.go @@ -173,6 +173,7 @@ func TestAcc_NotebookDataSource(t *testing.T) { resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityID), resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityDisplayName), resource.TestCheckResourceAttr(testDataSourceItemFQN, "description", entityDescription), + resource.TestCheckNoResourceAttr(testDataSourceItemFQN, "definition"), ), }, // read by id - not found @@ -200,6 +201,7 @@ func TestAcc_NotebookDataSource(t *testing.T) { resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityID), resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityDisplayName), resource.TestCheckResourceAttr(testDataSourceItemFQN, "description", entityDescription), + resource.TestCheckNoResourceAttr(testDataSourceItemFQN, "definition"), ), }, // read by name - not found @@ -213,5 +215,55 @@ func TestAcc_NotebookDataSource(t *testing.T) { ), ExpectError: regexp.MustCompile(common.ErrorReadHeader), }, + // read by id with definition - default + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "workspace_id": workspaceID, + "id": entityID, + "output_definition": true, + }, + ), + ExpectError: regexp.MustCompile("Invalid configuration for attribute format"), + }, + // read by id with definition - py + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "workspace_id": workspaceID, + "id": entityID, + "output_definition": true, + "format": "py", + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testDataSourceItemFQN, "workspace_id", workspaceID), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityID), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityDisplayName), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "description", entityDescription), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "definition.notebook-content.py.content"), + ), + }, + // read by id with definition - ipynb + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "workspace_id": workspaceID, + "id": entityID, + "output_definition": true, + "format": "ipynb", + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testDataSourceItemFQN, "workspace_id", workspaceID), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityID), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityDisplayName), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "description", entityDescription), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "definition.notebook-content.ipynb.content"), + ), + }, })) } diff --git a/internal/services/notebook/resource_notebook.go b/internal/services/notebook/resource_notebook.go index 48e7252..98fbc5b 100644 --- a/internal/services/notebook/resource_notebook.go +++ b/internal/services/notebook/resource_notebook.go @@ -5,7 +5,6 @@ package notebook import ( "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -23,16 +22,15 @@ func NewResourceNotebook() resource.Resource { ItemDocsSPNSupport, DisplayNameMaxLength: 123, DescriptionMaxLength: 256, - FormatTypeDefault: ItemFormatTypeDefault, - FormatTypes: ItemFormatTypes, + FormatTypeDefault: ItemDefinitionFormatTypeDefault, DefinitionPathDocsURL: ItemDefinitionPathDocsURL, - DefinitionPathKeys: ItemDefinitionPathsIPYNB, DefinitionPathKeysValidator: []validator.Map{ mapvalidator.SizeAtMost(1), - mapvalidator.KeysAre(stringvalidator.OneOf(ItemDefinitionPathsIPYNB...)), + mapvalidator.KeysAre(fabricitem.DefinitionPathKeysValidator(itemDefinitionFormats)...), }, DefinitionRequired: false, DefinitionEmpty: ItemDefinitionEmptyIPYNB, + DefinitionFormats: itemDefinitionFormats, } return fabricitem.NewResourceFabricItemDefinition(config) diff --git a/internal/services/notebook/resource_notebook_test.go b/internal/services/notebook/resource_notebook_test.go index 4964133..88aaa3f 100644 --- a/internal/services/notebook/resource_notebook_test.go +++ b/internal/services/notebook/resource_notebook_test.go @@ -28,12 +28,18 @@ var testHelperLocals = at.CompileLocalsConfig(map[string]any{ "path": testhelp.GetFixturesDirPath("notebook"), }) -var testHelperDefinition = map[string]any{ +var testHelperDefinitionIPYNB = map[string]any{ `"notebook-content.ipynb"`: map[string]any{ "source": "${local.path}/notebook.ipynb.tmpl", }, } +var testHelperDefinitionPY = map[string]any{ + `"notebook-content.py"`: map[string]any{ + "source": "${local.path}/notebook.py.tmpl", + }, +} + func TestUnit_NotebookResource_Attributes(t *testing.T) { resource.ParallelTest(t, testhelp.NewTestUnitCase(t, &testResourceItemFQN, fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ // error - no attributes @@ -58,7 +64,8 @@ func TestUnit_NotebookResource_Attributes(t *testing.T) { map[string]any{ "workspace_id": "invalid uuid", "display_name": "test", - "definition": testHelperDefinition, + "format": "ipynb", + "definition": testHelperDefinitionIPYNB, }, )), ExpectError: regexp.MustCompile(customtypes.UUIDTypeErrorInvalidStringHeader), @@ -74,7 +81,8 @@ func TestUnit_NotebookResource_Attributes(t *testing.T) { "workspace_id": "00000000-0000-0000-0000-000000000000", "display_name": "test", "unexpected_attr": "test", - "definition": testHelperDefinition, + "format": "ipynb", + "definition": testHelperDefinitionIPYNB, }, )), ExpectError: regexp.MustCompile(`An argument named "unexpected_attr" is not expected here`), @@ -88,7 +96,8 @@ func TestUnit_NotebookResource_Attributes(t *testing.T) { testResourceItemHeader, map[string]any{ "display_name": "test", - "definition": testHelperDefinition, + "format": "ipynb", + "definition": testHelperDefinitionIPYNB, }, )), ExpectError: regexp.MustCompile(`The argument "workspace_id" is required, but no definition was found.`), @@ -102,7 +111,8 @@ func TestUnit_NotebookResource_Attributes(t *testing.T) { testResourceItemHeader, map[string]any{ "workspace_id": "00000000-0000-0000-0000-000000000000", - "definition": testHelperDefinition, + "format": "ipynb", + "definition": testHelperDefinitionIPYNB, }, )), ExpectError: regexp.MustCompile(`The argument "display_name" is required, but no definition was found.`), @@ -125,7 +135,8 @@ func TestUnit_NotebookResource_ImportState(t *testing.T) { map[string]any{ "workspace_id": *entity.WorkspaceID, "display_name": *entity.DisplayName, - "definition": testHelperDefinition, + "format": "ipynb", + "definition": testHelperDefinitionIPYNB, }, )) @@ -202,7 +213,8 @@ func TestUnit_NotebookResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": *entityExist.WorkspaceID, "display_name": *entityExist.DisplayName, - "definition": testHelperDefinition, + "format": "ipynb", + "definition": testHelperDefinitionIPYNB, }, )), ExpectError: regexp.MustCompile(common.ErrorCreateHeader), @@ -217,7 +229,8 @@ func TestUnit_NotebookResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": *entityBefore.WorkspaceID, "display_name": *entityBefore.DisplayName, - "definition": testHelperDefinition, + "format": "ipynb", + "definition": testHelperDefinitionIPYNB, }, )), Check: resource.ComposeAggregateTestCheckFunc( @@ -236,7 +249,8 @@ func TestUnit_NotebookResource_CRUD(t *testing.T) { "workspace_id": *entityBefore.WorkspaceID, "display_name": *entityAfter.DisplayName, "description": *entityAfter.Description, - "definition": testHelperDefinition, + "format": "ipynb", + "definition": testHelperDefinitionIPYNB, }, )), Check: resource.ComposeAggregateTestCheckFunc( @@ -268,7 +282,110 @@ func TestAcc_NotebookResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": workspaceID, "display_name": entityCreateDisplayName, - "definition": testHelperDefinition, + }, + )), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityCreateDisplayName), + resource.TestCheckResourceAttr(testResourceItemFQN, "description", ""), + ), + }, + // Update and Read + { + ResourceName: testResourceItemFQN, + Config: at.JoinConfigs( + testHelperLocals, + at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "workspace_id": workspaceID, + "display_name": entityUpdateDisplayName, + "description": entityUpdateDescription, + }, + )), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), + resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), + ), + }, + }, + )) +} + +func TestAcc_NotebookDefinitionIPYNBResource_CRUD(t *testing.T) { + workspace := testhelp.WellKnown()["Workspace"].(map[string]any) + workspaceID := workspace["id"].(string) + + entityCreateDisplayName := testhelp.RandomName() + entityUpdateDisplayName := testhelp.RandomName() + entityUpdateDescription := testhelp.RandomName() + + resource.Test(t, testhelp.NewTestAccCase(t, &testResourceItemFQN, nil, []resource.TestStep{ + // Create and Read + { + ResourceName: testResourceItemFQN, + Config: at.JoinConfigs( + testHelperLocals, + at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "workspace_id": workspaceID, + "display_name": entityCreateDisplayName, + "format": "ipynb", + "definition": testHelperDefinitionIPYNB, + }, + )), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityCreateDisplayName), + resource.TestCheckResourceAttr(testResourceItemFQN, "description", ""), + resource.TestCheckResourceAttr(testResourceItemFQN, "definition_update_enabled", "true"), + ), + }, + // Update and Read + { + ResourceName: testResourceItemFQN, + Config: at.JoinConfigs( + testHelperLocals, + at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "workspace_id": workspaceID, + "display_name": entityUpdateDisplayName, + "description": entityUpdateDescription, + "format": "ipynb", + "definition": testHelperDefinitionIPYNB, + }, + )), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), + resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), + resource.TestCheckResourceAttr(testResourceItemFQN, "definition_update_enabled", "true"), + ), + }, + }, + )) +} + +func TestAcc_NotebookDefinitionPYResource_CRUD(t *testing.T) { + workspace := testhelp.WellKnown()["Workspace"].(map[string]any) + workspaceID := workspace["id"].(string) + + entityCreateDisplayName := testhelp.RandomName() + entityUpdateDisplayName := testhelp.RandomName() + entityUpdateDescription := testhelp.RandomName() + + resource.Test(t, testhelp.NewTestAccCase(t, &testResourceItemFQN, nil, []resource.TestStep{ + // Create and Read + { + ResourceName: testResourceItemFQN, + Config: at.JoinConfigs( + testHelperLocals, + at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "workspace_id": workspaceID, + "display_name": entityCreateDisplayName, + "format": "py", + "definition": testHelperDefinitionPY, }, )), Check: resource.ComposeAggregateTestCheckFunc( @@ -288,7 +405,8 @@ func TestAcc_NotebookResource_CRUD(t *testing.T) { "workspace_id": workspaceID, "display_name": entityUpdateDisplayName, "description": entityUpdateDescription, - "definition": testHelperDefinition, + "format": "py", + "definition": testHelperDefinitionPY, }, )), Check: resource.ComposeAggregateTestCheckFunc( diff --git a/internal/services/report/base.go b/internal/services/report/base.go index 84e3c70..8adb9c1 100644 --- a/internal/services/report/base.go +++ b/internal/services/report/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 ( @@ -21,7 +22,10 @@ const ( ItemDefinitionPathDocsURL = "https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/report-definition" ) -var ( - ItemFormatTypes = []string{"PBIR-Legacy"} //nolint:gochecknoglobals - ItemDefinitionPathsPBIRLegacy = []string{"report.json", "definition.pbir", "StaticResources/RegisteredResources/*", "StaticResources/SharedResources/*"} //nolint:gochecknoglobals -) +var itemDefinitionFormats = []fabricitem.DefinitionFormat{ + { + Type: "PBIR-Legacy", + API: "PBIR-Legacy", + Paths: []string{"report.json", "definition.pbir", "StaticResources/RegisteredResources/*", "StaticResources/SharedResources/*"}, + }, +} diff --git a/internal/services/report/data_report.go b/internal/services/report/data_report.go index fd6e400..cf359c7 100644 --- a/internal/services/report/data_report.go +++ b/internal/services/report/data_report.go @@ -19,8 +19,7 @@ func NewDataSourceReport() datasource.DataSource { ItemDocsSPNSupport, IsDisplayNameUnique: false, FormatTypeDefault: ItemFormatTypeDefault, - FormatTypes: ItemFormatTypes, - DefinitionPathKeys: ItemDefinitionPathsPBIRLegacy, + DefinitionFormats: itemDefinitionFormats, } return fabricitem.NewDataSourceFabricItemDefinition(config) diff --git a/internal/services/report/data_report_test.go b/internal/services/report/data_report_test.go index ae21f46..a2aa045 100644 --- a/internal/services/report/data_report_test.go +++ b/internal/services/report/data_report_test.go @@ -156,6 +156,7 @@ func TestAcc_ReportDataSource(t *testing.T) { map[string]any{ "workspace_id": workspaceID, "id": entityID, + "format": "PBIR-Legacy", "output_definition": true, }, ), diff --git a/internal/services/report/resource_report.go b/internal/services/report/resource_report.go index 37935f3..e2bda41 100644 --- a/internal/services/report/resource_report.go +++ b/internal/services/report/resource_report.go @@ -27,20 +27,19 @@ func NewResourceReport() resource.Resource { DisplayNameMaxLength: 123, DescriptionMaxLength: 256, FormatTypeDefault: ItemFormatTypeDefault, - FormatTypes: ItemFormatTypes, DefinitionPathDocsURL: ItemDefinitionPathDocsURL, - DefinitionPathKeys: ItemDefinitionPathsPBIRLegacy, DefinitionPathKeysValidator: []validator.Map{ mapvalidator.SizeAtLeast(3), mapvalidator.KeysAre( stringvalidator.RegexMatches( regexp.MustCompile(`^(report\.json|definition\.pbir|StaticResources/RegisteredResources/.*|StaticResources/SharedResources/.*)$`), - "Definition path must match one of the following: "+utils.ConvertStringSlicesToString(ItemDefinitionPathsPBIRLegacy, true, false), + "Definition path must match one of the following: "+utils.ConvertStringSlicesToString(fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, "PBIR-Legacy"), true, false), ), ), }, DefinitionRequired: true, DefinitionEmpty: "", + DefinitionFormats: itemDefinitionFormats, } return fabricitem.NewResourceFabricItemDefinition(config) diff --git a/internal/services/report/resource_report_test.go b/internal/services/report/resource_report_test.go index 57964a5..853efac 100644 --- a/internal/services/report/resource_report_test.go +++ b/internal/services/report/resource_report_test.go @@ -68,6 +68,7 @@ func TestUnit_ReportResource_Attributes(t *testing.T) { map[string]any{ "workspace_id": "invalid uuid", "display_name": "test", + "format": "PBIR-Legacy", "definition": testHelperDefinition, }, )), @@ -84,6 +85,7 @@ func TestUnit_ReportResource_Attributes(t *testing.T) { "workspace_id": "00000000-0000-0000-0000-000000000000", "display_name": "test", "unexpected_attr": "test", + "format": "PBIR-Legacy", "definition": testHelperDefinition, }, )), @@ -98,6 +100,7 @@ func TestUnit_ReportResource_Attributes(t *testing.T) { testResourceItemHeader, map[string]any{ "display_name": "test", + "format": "PBIR-Legacy", "definition": testHelperDefinition, }, )), @@ -112,6 +115,7 @@ func TestUnit_ReportResource_Attributes(t *testing.T) { testResourceItemHeader, map[string]any{ "workspace_id": "00000000-0000-0000-0000-000000000000", + "format": "PBIR-Legacy", "definition": testHelperDefinition, }, )), @@ -149,6 +153,7 @@ func TestUnit_ReportResource_ImportState(t *testing.T) { map[string]any{ "workspace_id": *entity.WorkspaceID, "display_name": *entity.DisplayName, + "format": "PBIR-Legacy", "definition": testHelperDefinition, }, )) @@ -231,6 +236,7 @@ func TestUnit_ReportResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": *entityExist.WorkspaceID, "display_name": *entityExist.DisplayName, + "format": "PBIR-Legacy", "definition": testHelperDefinition, }, )), @@ -246,6 +252,7 @@ func TestUnit_ReportResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": *entityBefore.WorkspaceID, "display_name": *entityBefore.DisplayName, + "format": "PBIR-Legacy", "definition": testHelperDefinition, }, )), @@ -265,6 +272,7 @@ func TestUnit_ReportResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": *entityBefore.WorkspaceID, "display_name": *entityAfter.DisplayName, + "format": "PBIR-Legacy", "definition": testHelperDefinition, }, )), @@ -300,6 +308,7 @@ func TestAcc_ReportResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": workspaceID, "display_name": entityCreateDisplayName, + "format": "PBIR-Legacy", "definition": testHelperDefinition, }, )), @@ -319,6 +328,7 @@ func TestAcc_ReportResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": workspaceID, "display_name": entityUpdateDisplayName, + "format": "PBIR-Legacy", "definition": testHelperDefinition, }, )), diff --git a/internal/services/semanticmodel/base.go b/internal/services/semanticmodel/base.go index b28d72d..ed355ee 100644 --- a/internal/services/semanticmodel/base.go +++ b/internal/services/semanticmodel/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 ( @@ -21,7 +22,10 @@ const ( ItemDefinitionPathDocsURL = "https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/semantic-model-definition" ) -var ( - ItemFormatTypes = []string{"TMSL"} //nolint:gochecknoglobals - ItemDefinitionPathsTMSL = []string{"model.bim", "definition.pbism", "diagramLayout.json"} //nolint:gochecknoglobals -) +var itemDefinitionFormats = []fabricitem.DefinitionFormat{ + { + Type: "TMSL", + API: "TMSL", + Paths: []string{"model.bim", "definition.pbism", "diagramLayout.json"}, + }, +} diff --git a/internal/services/semanticmodel/data_semantic_model.go b/internal/services/semanticmodel/data_semantic_model.go index b0ed23b..fcd7bbd 100644 --- a/internal/services/semanticmodel/data_semantic_model.go +++ b/internal/services/semanticmodel/data_semantic_model.go @@ -19,8 +19,7 @@ func NewDataSourceSemanticModel() datasource.DataSource { ItemDocsSPNSupport, IsDisplayNameUnique: false, FormatTypeDefault: ItemFormatTypeDefault, - FormatTypes: ItemFormatTypes, - DefinitionPathKeys: ItemDefinitionPathsTMSL, + DefinitionFormats: itemDefinitionFormats, } return fabricitem.NewDataSourceFabricItemDefinition(config) diff --git a/internal/services/semanticmodel/data_semantic_model_test.go b/internal/services/semanticmodel/data_semantic_model_test.go index 2a27b96..adb73f5 100644 --- a/internal/services/semanticmodel/data_semantic_model_test.go +++ b/internal/services/semanticmodel/data_semantic_model_test.go @@ -156,6 +156,7 @@ func TestAcc_SemanticModelDataSource(t *testing.T) { map[string]any{ "workspace_id": workspaceID, "id": entityID, + "format": "TMSL", "output_definition": true, }, ), diff --git a/internal/services/semanticmodel/resource_semantic_model.go b/internal/services/semanticmodel/resource_semantic_model.go index 8cfcfaf..27203b3 100644 --- a/internal/services/semanticmodel/resource_semantic_model.go +++ b/internal/services/semanticmodel/resource_semantic_model.go @@ -24,15 +24,14 @@ func NewResourceSemanticModel() resource.Resource { DisplayNameMaxLength: 123, DescriptionMaxLength: 256, FormatTypeDefault: ItemFormatTypeDefault, - FormatTypes: ItemFormatTypes, DefinitionPathDocsURL: ItemDefinitionPathDocsURL, - DefinitionPathKeys: ItemDefinitionPathsTMSL, DefinitionPathKeysValidator: []validator.Map{ mapvalidator.SizeAtLeast(2), - mapvalidator.KeysAre(stringvalidator.OneOf(ItemDefinitionPathsTMSL...)), + mapvalidator.KeysAre(stringvalidator.OneOf(fabricitem.GetDefinitionFormatPaths(itemDefinitionFormats, "TMSL")...)), }, DefinitionRequired: true, DefinitionEmpty: "", + DefinitionFormats: itemDefinitionFormats, } return fabricitem.NewResourceFabricItemDefinition(config) diff --git a/internal/services/semanticmodel/resource_semantic_model_test.go b/internal/services/semanticmodel/resource_semantic_model_test.go index b430f6e..cd7be7a 100644 --- a/internal/services/semanticmodel/resource_semantic_model_test.go +++ b/internal/services/semanticmodel/resource_semantic_model_test.go @@ -64,6 +64,7 @@ func TestUnit_SemanticModelResource_Attributes(t *testing.T) { map[string]any{ "workspace_id": "invalid uuid", "display_name": "test", + "format": "TMSL", "definition": testHelperDefinition, }, )), @@ -80,6 +81,7 @@ func TestUnit_SemanticModelResource_Attributes(t *testing.T) { "workspace_id": "00000000-0000-0000-0000-000000000000", "display_name": "test", "unexpected_attr": "test", + "format": "TMSL", "definition": testHelperDefinition, }, )), @@ -94,6 +96,7 @@ func TestUnit_SemanticModelResource_Attributes(t *testing.T) { testResourceItemHeader, map[string]any{ "display_name": "test", + "format": "TMSL", "definition": testHelperDefinition, }, )), @@ -108,6 +111,7 @@ func TestUnit_SemanticModelResource_Attributes(t *testing.T) { testResourceItemHeader, map[string]any{ "workspace_id": "00000000-0000-0000-0000-000000000000", + "format": "TMSL", "definition": testHelperDefinition, }, )), @@ -145,6 +149,7 @@ func TestUnit_SemanticModelResource_ImportState(t *testing.T) { map[string]any{ "workspace_id": *entity.WorkspaceID, "display_name": *entity.DisplayName, + "format": "TMSL", "definition": testHelperDefinition, }, )) @@ -222,6 +227,7 @@ func TestUnit_SemanticModelResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": *entityExist.WorkspaceID, "display_name": *entityExist.DisplayName, + "format": "TMSL", "definition": testHelperDefinition, }, )), @@ -237,6 +243,7 @@ func TestUnit_SemanticModelResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": *entityBefore.WorkspaceID, "display_name": *entityBefore.DisplayName, + "format": "TMSL", "definition": testHelperDefinition, }, )), @@ -256,6 +263,7 @@ func TestUnit_SemanticModelResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": *entityBefore.WorkspaceID, "display_name": *entityAfter.DisplayName, + "format": "TMSL", "definition": testHelperDefinition, }, )), @@ -286,6 +294,7 @@ func TestAcc_SemanticModelResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": workspaceID, "display_name": entityCreateDisplayName, + "format": "TMSL", "definition": testHelperDefinition, }, )), @@ -305,6 +314,7 @@ func TestAcc_SemanticModelResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": workspaceID, "display_name": entityUpdateDisplayName, + "format": "TMSL", "definition": testHelperDefinition, }, )), diff --git a/internal/services/sparkjobdefinition/base.go b/internal/services/sparkjobdefinition/base.go index 5a9ed42..f2c7fc2 100644 --- a/internal/services/sparkjobdefinition/base.go +++ b/internal/services/sparkjobdefinition/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 ( @@ -22,7 +23,10 @@ const ( ItemDefinitionPathDocsURL = "https://learn.microsoft.com/rest/api/fabric/articles/item-management/definitions/spark-job-definition" ) -var ( - ItemFormatTypes = []string{"SparkJobDefinitionV1"} //nolint:gochecknoglobals - ItemDefinitionPaths = []string{"SparkJobDefinitionV1.json"} //nolint:gochecknoglobals -) +var itemDefinitionFormats = []fabricitem.DefinitionFormat{ + { + Type: "SparkJobDefinitionV1", + API: "SparkJobDefinitionV1", + Paths: []string{"SparkJobDefinitionV1.json"}, + }, +} diff --git a/internal/services/sparkjobdefinition/data_spark_job_definition.go b/internal/services/sparkjobdefinition/data_spark_job_definition.go index 7fd54d1..c9f7e8c 100644 --- a/internal/services/sparkjobdefinition/data_spark_job_definition.go +++ b/internal/services/sparkjobdefinition/data_spark_job_definition.go @@ -93,8 +93,7 @@ func NewDataSourceSparkJobDefinition(ctx context.Context) datasource.DataSource ItemDocsSPNSupport, IsDisplayNameUnique: true, FormatTypeDefault: ItemFormatTypeDefault, - FormatTypes: ItemFormatTypes, - DefinitionPathKeys: ItemDefinitionPaths, + DefinitionFormats: itemDefinitionFormats, }, PropertiesSchema: propertiesSchema, PropertiesSetter: propertiesSetter, diff --git a/internal/services/sparkjobdefinition/data_spark_job_definition_test.go b/internal/services/sparkjobdefinition/data_spark_job_definition_test.go index b1b01f8..0ed0d0b 100644 --- a/internal/services/sparkjobdefinition/data_spark_job_definition_test.go +++ b/internal/services/sparkjobdefinition/data_spark_job_definition_test.go @@ -222,6 +222,7 @@ func TestAcc_SparkJobDefinitionDataSource(t *testing.T) { map[string]any{ "workspace_id": workspaceID, "id": entityID, + "format": "SparkJobDefinitionV1", "output_definition": true, }, ), diff --git a/internal/services/sparkjobdefinition/resource_spark_job_definition.go b/internal/services/sparkjobdefinition/resource_spark_job_definition.go index e09f01b..490b2e5 100644 --- a/internal/services/sparkjobdefinition/resource_spark_job_definition.go +++ b/internal/services/sparkjobdefinition/resource_spark_job_definition.go @@ -8,7 +8,6 @@ import ( supertypes "github.com/FrangipaneTeam/terraform-plugin-framework-supertypes" "github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -75,15 +74,14 @@ func NewResourceSparkJobDefinition(ctx context.Context) resource.Resource { DisplayNameMaxLength: 123, DescriptionMaxLength: 256, FormatTypeDefault: ItemFormatTypeDefault, - FormatTypes: ItemFormatTypes, DefinitionPathDocsURL: ItemDefinitionPathDocsURL, - DefinitionPathKeys: ItemDefinitionPaths, DefinitionPathKeysValidator: []validator.Map{ mapvalidator.SizeAtMost(1), - mapvalidator.KeysAre(stringvalidator.OneOf(ItemDefinitionPaths...)), + mapvalidator.KeysAre(fabricitem.DefinitionPathKeysValidator(itemDefinitionFormats)...), }, DefinitionRequired: false, DefinitionEmpty: ItemDefinitionEmpty, + DefinitionFormats: itemDefinitionFormats, }, PropertiesSchema: propertiesSchema, PropertiesSetter: propertiesSetter, diff --git a/internal/services/sparkjobdefinition/resource_spark_job_definition_test.go b/internal/services/sparkjobdefinition/resource_spark_job_definition_test.go index eba7fd2..b8ae38c 100644 --- a/internal/services/sparkjobdefinition/resource_spark_job_definition_test.go +++ b/internal/services/sparkjobdefinition/resource_spark_job_definition_test.go @@ -58,6 +58,7 @@ func TestUnit_SparkJobDefinitionResource_Attributes(t *testing.T) { map[string]any{ "workspace_id": "invalid uuid", "display_name": "test", + "format": "SparkJobDefinitionV1", "definition": testHelperDefinition, }, )), @@ -74,6 +75,7 @@ func TestUnit_SparkJobDefinitionResource_Attributes(t *testing.T) { "workspace_id": "00000000-0000-0000-0000-000000000000", "display_name": "test", "unexpected_attr": "test", + "format": "SparkJobDefinitionV1", "definition": testHelperDefinition, }, )), @@ -88,6 +90,7 @@ func TestUnit_SparkJobDefinitionResource_Attributes(t *testing.T) { testResourceItemHeader, map[string]any{ "display_name": "test", + "format": "SparkJobDefinitionV1", "definition": testHelperDefinition, }, )), @@ -102,6 +105,7 @@ func TestUnit_SparkJobDefinitionResource_Attributes(t *testing.T) { testResourceItemHeader, map[string]any{ "workspace_id": "00000000-0000-0000-0000-000000000000", + "format": "SparkJobDefinitionV1", "definition": testHelperDefinition, }, )), @@ -125,6 +129,7 @@ func TestUnit_SparkJobDefinitionResource_ImportState(t *testing.T) { map[string]any{ "workspace_id": *entity.WorkspaceID, "display_name": *entity.DisplayName, + "format": "SparkJobDefinitionV1", "definition": testHelperDefinition, }, )) @@ -202,6 +207,7 @@ func TestUnit_SparkJobDefinitionResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": *entityExist.WorkspaceID, "display_name": *entityExist.DisplayName, + "format": "SparkJobDefinitionV1", "definition": testHelperDefinition, }, )), @@ -217,6 +223,7 @@ func TestUnit_SparkJobDefinitionResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": *entityBefore.WorkspaceID, "display_name": *entityBefore.DisplayName, + "format": "SparkJobDefinitionV1", "definition": testHelperDefinition, }, )), @@ -236,6 +243,7 @@ func TestUnit_SparkJobDefinitionResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": *entityBefore.WorkspaceID, "display_name": *entityAfter.DisplayName, + "format": "SparkJobDefinitionV1", "definition": testHelperDefinition, }, )), @@ -266,6 +274,7 @@ func TestAcc_SparkJobDefinitionResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": workspaceID, "display_name": entityCreateDisplayName, + "format": "SparkJobDefinitionV1", "definition": testHelperDefinition, }, )), @@ -285,6 +294,7 @@ func TestAcc_SparkJobDefinitionResource_CRUD(t *testing.T) { map[string]any{ "workspace_id": workspaceID, "display_name": entityUpdateDisplayName, + "format": "SparkJobDefinitionV1", "definition": testHelperDefinition, }, )), diff --git a/internal/services/workspace/resource_workspace_git.go b/internal/services/workspace/resource_workspace_git.go index 07348d7..adb69cf 100644 --- a/internal/services/workspace/resource_workspace_git.go +++ b/internal/services/workspace/resource_workspace_git.go @@ -315,7 +315,7 @@ func (r *resourceWorkspaceGit) Read(ctx context.Context, req resource.ReadReques return } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) tflog.Debug(ctx, "READ", map[string]any{ "action": "end", diff --git a/internal/testhelp/fixtures/notebook/notebook.py.tmpl b/internal/testhelp/fixtures/notebook/notebook.py.tmpl new file mode 100644 index 0000000..6e270f5 --- /dev/null +++ b/internal/testhelp/fixtures/notebook/notebook.py.tmpl @@ -0,0 +1,22 @@ +# Fabric notebook source + +# METADATA ******************** + +# META { +# META "kernel_info": { +# META "name": "synapse_pyspark" +# META }, +# META "dependencies": {} +# META } + +# CELL ******************** + +# Welcome to your new notebook +# Type here in the cell editor to add code! + +# METADATA ******************** + +# META { +# META "language": "python", +# META "language_group": "synapse_pyspark" +# META }