From 7b3e885420fe151b308151e90d69c09aadf5e720 Mon Sep 17 00:00:00 2001 From: Dariusz Porowski <3431813+DariuszPorowski@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:59:16 -0800 Subject: [PATCH] feat: refactor warehouse --- docs/data-sources/warehouse.md | 6 +- docs/data-sources/warehouses.md | 11 + docs/resources/warehouse.md | 14 +- .../pkg/fabricitem/data_item_properties.go | 170 ++++++++ internal/pkg/fabricitem/data_schema.go | 10 + .../models_data_item_properties.go} | 6 +- .../models_resource_item_properties.go | 34 ++ .../fabricitem/resource_item_properties.go | 321 ++++++++++++++ internal/pkg/fabricitem/resource_schema.go | 11 + internal/provider/provider.go | 6 +- .../data_spark_job_definition.go | 3 +- .../data_spark_job_definitions.go | 3 +- .../resource_spark_job_definition_test.go | 3 + internal/services/warehouse/data_warehouse.go | 229 +++------- .../services/warehouse/data_warehouse_test.go | 12 + .../services/warehouse/data_warehouses.go | 95 ++++- .../warehouse/data_warehouses_test.go | 6 +- .../warehouse/models_resource_warehouse.go | 32 -- .../services/warehouse/resource_warehouse.go | 394 +++--------------- .../warehouse/resource_warehouse_test.go | 9 + .../fakes/fabric_sparkjobdefinition.go | 2 +- 21 files changed, 802 insertions(+), 575 deletions(-) create mode 100644 internal/pkg/fabricitem/data_item_properties.go rename internal/{services/warehouse/models_data_warehouse.go => pkg/fabricitem/models_data_item_properties.go} (60%) create mode 100644 internal/pkg/fabricitem/models_resource_item_properties.go create mode 100644 internal/pkg/fabricitem/resource_item_properties.go delete mode 100644 internal/services/warehouse/models_resource_warehouse.go diff --git a/docs/data-sources/warehouse.md b/docs/data-sources/warehouse.md index 3f709c3..d270948 100644 --- a/docs/data-sources/warehouse.md +++ b/docs/data-sources/warehouse.md @@ -70,6 +70,6 @@ Optional: Read-Only: -- `connection_string` (String) Connection String -- `created_date` (String) Created Date -- `last_updated_time` (String) Last Updated Time +- `connection_string` (String) The SQL connection string connected to the workspace containing this warehouse. +- `created_date` (String) The date and time the warehouse was created. +- `last_updated_time` (String) The date and time the warehouse was last updated. diff --git a/docs/data-sources/warehouses.md b/docs/data-sources/warehouses.md index 7d3cf28..6a72d53 100644 --- a/docs/data-sources/warehouses.md +++ b/docs/data-sources/warehouses.md @@ -56,4 +56,15 @@ Read-Only: - `description` (String) The Warehouse description. - `display_name` (String) The Warehouse display name. - `id` (String) The Warehouse ID. +- `properties` (Attributes) The Warehouse properties. (see [below for nested schema](#nestedatt--values--properties)) - `workspace_id` (String) The Workspace ID. + + + +### Nested Schema for `values.properties` + +Read-Only: + +- `connection_string` (String) The SQL connection string connected to the workspace containing this warehouse. +- `created_date` (String) The date and time the warehouse was created. +- `last_updated_time` (String) The date and time the warehouse was last updated. diff --git a/docs/resources/warehouse.md b/docs/resources/warehouse.md index c783a16..90a7953 100644 --- a/docs/resources/warehouse.md +++ b/docs/resources/warehouse.md @@ -3,16 +3,16 @@ page_title: "fabric_warehouse Resource - terraform-provider-fabric" subcategory: "" description: |- - This resource manages a Fabric Warehouse. - See Warehouse https://learn.microsoft.com/fabric/data-warehouse/data-warehousing for more information. + Manage a Fabric Warehouse. + Use this resource to manage a Warehouse https://learn.microsoft.com/fabric/data-warehouse/data-warehousing. -> This item does not support Service Principal. Please use a User context authentication. --- # fabric_warehouse (Resource) -This resource manages a Fabric Warehouse. +Manage a Fabric Warehouse. -See [Warehouse](https://learn.microsoft.com/fabric/data-warehouse/data-warehousing) for more information. +Use this resource to manage a [Warehouse](https://learn.microsoft.com/fabric/data-warehouse/data-warehousing). -> This item does not support Service Principal. Please use a User context authentication. @@ -60,9 +60,9 @@ Optional: Read-Only: -- `connection_string` (String) Connection String -- `created_date` (String) Created Date -- `last_updated_time` (String) Last Updated Time +- `connection_string` (String) The SQL connection string connected to the workspace containing this warehouse. +- `created_date` (String) The date and time the warehouse was created. +- `last_updated_time` (String) The date and time the warehouse was last updated. ## Import diff --git a/internal/pkg/fabricitem/data_item_properties.go b/internal/pkg/fabricitem/data_item_properties.go new file mode 100644 index 0000000..5554658 --- /dev/null +++ b/internal/pkg/fabricitem/data_item_properties.go @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package fabricitem + +import ( + "context" + "fmt" + "net/http" + + "github.com/hashicorp/terraform-plugin-framework-validators/datasourcevalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/microsoft/fabric-sdk-go/fabric" + 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/utils" + pconfig "github.com/microsoft/terraform-provider-fabric/internal/provider/config" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSourceWithConfigValidators = (*DataSourceFabricItemProperties[struct{}, struct{}])(nil) + _ datasource.DataSourceWithConfigure = (*DataSourceFabricItemProperties[struct{}, struct{}])(nil) +) + +type DataSourceFabricItemProperties[Ttfprop, Titemprop any] struct { + DataSourceFabricItem + PropertiesSchema schema.SingleNestedAttribute + PropertiesSetter func(ctx context.Context, from *Titemprop, to *DataSourceFabricItemPropertiesModel[Ttfprop, Titemprop]) diag.Diagnostics + ItemGetter func(ctx context.Context, fabricClient fabric.Client, model DataSourceFabricItemPropertiesModel[Ttfprop, Titemprop], fabricItem *FabricItemProperties[Titemprop]) error + ItemListGetter func(ctx context.Context, fabricClient fabric.Client, model DataSourceFabricItemPropertiesModel[Ttfprop, Titemprop], errNotFound fabcore.ResponseError, fabricItem *FabricItemProperties[Titemprop]) error +} + +func NewDataSourceFabricItemProperties[Ttfprop, Titemprop any](config DataSourceFabricItemProperties[Ttfprop, Titemprop]) datasource.DataSource { + return &config +} + +func (d *DataSourceFabricItemProperties[Ttfprop, Titemprop]) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { //revive:disable-line:confusing-naming + resp.TypeName = req.ProviderTypeName + "_" + d.TFName +} + +func (d *DataSourceFabricItemProperties[Ttfprop, Titemprop]) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { //revive:disable-line:confusing-naming + resp.Schema = GetDataSourceFabricItemPropertiesSchema1(ctx, *d) +} + +func (d *DataSourceFabricItemProperties[Ttfprop, Titemprop]) ConfigValidators(_ context.Context) []datasource.ConfigValidator { + if d.IsDisplayNameUnique { + return []datasource.ConfigValidator{ + datasourcevalidator.Conflicting( + path.MatchRoot("id"), + path.MatchRoot("display_name"), + ), + datasourcevalidator.ExactlyOneOf( + path.MatchRoot("id"), + path.MatchRoot("display_name"), + ), + } + } + + return []datasource.ConfigValidator{} +} + +func (d *DataSourceFabricItemProperties[Ttfprop, Titemprop]) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { //revive:disable-line:confusing-naming + if req.ProviderData == nil { + return + } + + pConfigData, ok := req.ProviderData.(*pconfig.ProviderData) + if !ok { + resp.Diagnostics.AddError( + common.ErrorDataSourceConfigType, + fmt.Sprintf(common.ErrorFabricClientType, req.ProviderData), + ) + + return + } + + d.pConfigData = pConfigData + d.client = fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewItemsClient() +} + +func (d *DataSourceFabricItemProperties[Ttfprop, Titemprop]) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { //revive:disable-line:confusing-naming + tflog.Debug(ctx, "READ", map[string]any{ + "action": "start", + }) + tflog.Trace(ctx, "READ", map[string]any{ + "config": req.Config, + }) + + var data DataSourceFabricItemPropertiesModel[Ttfprop, Titemprop] + + if resp.Diagnostics.Append(req.Config.Get(ctx, &data)...); resp.Diagnostics.HasError() { + return + } + + timeout, diags := data.Timeouts.Read(ctx, d.pConfigData.Timeout) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + if data.ID.ValueString() != "" { + diags = d.getByID(ctx, &data) + } else { + diags = d.getByDisplayName(ctx, &data) + } + + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) + + tflog.Debug(ctx, "READ", map[string]any{ + "action": "end", + }) + + if resp.Diagnostics.HasError() { + return + } +} + +func (d *DataSourceFabricItemProperties[Ttfprop, Titemprop]) getByID(ctx context.Context, model *DataSourceFabricItemPropertiesModel[Ttfprop, Titemprop]) diag.Diagnostics { + tflog.Trace(ctx, fmt.Sprintf("getting %s by ID: %s", d.Name, model.ID.ValueString())) + + var fabricItem FabricItemProperties[Titemprop] + + err := d.ItemGetter(ctx, *d.pConfigData.FabricClient, *model, &fabricItem) + if diags := utils.GetDiagsFromError(ctx, err, utils.OperationRead, nil); diags.HasError() { + return diags + } + + model.set(fabricItem) + + return d.PropertiesSetter(ctx, fabricItem.Properties, model) +} + +func (d *DataSourceFabricItemProperties[Ttfprop, Titemprop]) getByDisplayName(ctx context.Context, model *DataSourceFabricItemPropertiesModel[Ttfprop, Titemprop]) diag.Diagnostics { + tflog.Trace(ctx, fmt.Sprintf("getting %s by Display Name: %s", d.Name, model.DisplayName.ValueString())) + + errNotFoundCode := fabcore.ErrCommon.EntityNotFound.Error() + errNotFoundMsg := fmt.Sprintf("Unable to find %s with 'display_name': %s in the Workspace ID: %s", d.Name, model.DisplayName.ValueString(), model.WorkspaceID.ValueString()) + + errNotFound := fabcore.ResponseError{ + ErrorCode: errNotFoundCode, + StatusCode: http.StatusNotFound, + ErrorResponse: &fabcore.ErrorResponse{ + ErrorCode: &errNotFoundCode, + Message: &errNotFoundMsg, + }, + } + + var fabricItem FabricItemProperties[Titemprop] + + err := d.ItemListGetter(ctx, *d.pConfigData.FabricClient, *model, errNotFound, &fabricItem) + if diags := utils.GetDiagsFromError(ctx, err, utils.OperationRead, nil); diags.HasError() { + return diags + } + + model.set(fabricItem) + + return d.PropertiesSetter(ctx, fabricItem.Properties, model) +} diff --git a/internal/pkg/fabricitem/data_schema.go b/internal/pkg/fabricitem/data_schema.go index 7cdbb7b..384d20d 100644 --- a/internal/pkg/fabricitem/data_schema.go +++ b/internal/pkg/fabricitem/data_schema.go @@ -47,6 +47,16 @@ func GetDataSourceFabricItemPropertiesSchema(ctx context.Context, d DataSourceFa } } +func GetDataSourceFabricItemPropertiesSchema1[Ttfprop, Titemprop any](ctx context.Context, d DataSourceFabricItemProperties[Ttfprop, Titemprop]) schema.Schema { + attributes := getDataSourceFabricItemBaseAttributes(ctx, d.Name, d.IsDisplayNameUnique) + attributes["properties"] = d.PropertiesSchema + + return schema.Schema{ + MarkdownDescription: d.MarkdownDescription, + Attributes: attributes, + } +} + func GetDataSourceFabricItemDefinitionPropertiesSchema(ctx context.Context, d DataSourceFabricItemDefinition, properties schema.SingleNestedAttribute) schema.Schema { attributes := getDataSourceFabricItemBaseAttributes(ctx, d.Name, d.IsDisplayNameUnique) attributes["properties"] = properties diff --git a/internal/services/warehouse/models_data_warehouse.go b/internal/pkg/fabricitem/models_data_item_properties.go similarity index 60% rename from internal/services/warehouse/models_data_warehouse.go rename to internal/pkg/fabricitem/models_data_item_properties.go index 346e6a3..93ddb51 100644 --- a/internal/services/warehouse/models_data_warehouse.go +++ b/internal/pkg/fabricitem/models_data_item_properties.go @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MPL-2.0 -package warehouse +package fabricitem import ( "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" ) -type dataSourceWarehouseModel struct { - baseWarehouseModel +type DataSourceFabricItemPropertiesModel[Ttfprop, Titemprop any] struct { + FabricItemPropertiesModel[Ttfprop, Titemprop] Timeouts timeouts.Value `tfsdk:"timeouts"` } diff --git a/internal/pkg/fabricitem/models_resource_item_properties.go b/internal/pkg/fabricitem/models_resource_item_properties.go new file mode 100644 index 0000000..ceb2ba6 --- /dev/null +++ b/internal/pkg/fabricitem/models_resource_item_properties.go @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package fabricitem + +import ( + azto "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" +) + +type ResourceFabricItemPropertiesModel[Ttfprop, Titemprop any] struct { + FabricItemPropertiesModel[Ttfprop, Titemprop] + Timeouts timeouts.Value `tfsdk:"timeouts"` +} + +type requestCreateFabricItemProperties[Ttfprop, Titemprop any] struct { + fabcore.CreateItemRequest +} + +func (to *requestCreateFabricItemProperties[Ttfprop, Titemprop]) set(from ResourceFabricItemPropertiesModel[Ttfprop, Titemprop], itemType fabcore.ItemType) { //revive:disable-line:confusing-naming + to.DisplayName = from.DisplayName.ValueStringPointer() + to.Description = from.Description.ValueStringPointer() + to.Type = azto.Ptr(itemType) +} + +type requestUpdateFabricItemProperties[Ttfprop, Titemprop any] struct { + fabcore.UpdateItemRequest +} + +func (to *requestUpdateFabricItemProperties[Ttfprop, Titemprop]) set(from ResourceFabricItemPropertiesModel[Ttfprop, Titemprop]) { //revive:disable-line:confusing-naming + to.DisplayName = from.DisplayName.ValueStringPointer() + to.Description = from.Description.ValueStringPointer() +} diff --git a/internal/pkg/fabricitem/resource_item_properties.go b/internal/pkg/fabricitem/resource_item_properties.go new file mode 100644 index 0000000..6f4b65b --- /dev/null +++ b/internal/pkg/fabricitem/resource_item_properties.go @@ -0,0 +1,321 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package fabricitem + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/microsoft/fabric-sdk-go/fabric" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + + "github.com/microsoft/terraform-provider-fabric/internal/common" + "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" + "github.com/microsoft/terraform-provider-fabric/internal/pkg/utils" + pconfig "github.com/microsoft/terraform-provider-fabric/internal/provider/config" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.ResourceWithConfigure = (*ResourceFabricItemProperties[struct{}, struct{}])(nil) + _ resource.ResourceWithImportState = (*ResourceFabricItemProperties[struct{}, struct{}])(nil) +) + +type ResourceFabricItemProperties[Ttfprop, Titemprop any] struct { + ResourceFabricItem + PropertiesSchema schema.SingleNestedAttribute + PropertiesSetter func(ctx context.Context, from *Titemprop, to *ResourceFabricItemPropertiesModel[Ttfprop, Titemprop]) diag.Diagnostics + ItemGetter func(ctx context.Context, fabricClient fabric.Client, model ResourceFabricItemPropertiesModel[Ttfprop, Titemprop], fabricItem *FabricItemProperties[Titemprop]) error +} + +func NewResourceFabricItemProperties[Ttfprop, Titemprop any](config ResourceFabricItemProperties[Ttfprop, Titemprop]) resource.Resource { + return &config +} + +func (r *ResourceFabricItemProperties[Ttfprop, Titemprop]) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { //revive:disable-line:confusing-naming + resp.TypeName = req.ProviderTypeName + "_" + r.TFName +} + +func (r *ResourceFabricItemProperties[Ttfprop, Titemprop]) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { //revive:disable-line:confusing-naming + resp.Schema = GetResourceFabricItemPropertiesSchema1(ctx, *r) +} + +func (r *ResourceFabricItemProperties[Ttfprop, Titemprop]) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { //revive:disable-line:confusing-naming + if req.ProviderData == nil { + return + } + + pConfigData, ok := req.ProviderData.(*pconfig.ProviderData) + if !ok { + resp.Diagnostics.AddError( + common.ErrorResourceConfigType, + fmt.Sprintf(common.ErrorFabricClientType, req.ProviderData), + ) + + return + } + + r.pConfigData = pConfigData + r.client = fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewItemsClient() +} + +func (r *ResourceFabricItemProperties[Ttfprop, Titemprop]) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + tflog.Debug(ctx, "CREATE", map[string]any{ + "action": "start", + }) + tflog.Trace(ctx, "CREATE", map[string]any{ + "config": req.Config, + "plan": req.Plan, + }) + + var plan ResourceFabricItemPropertiesModel[Ttfprop, Titemprop] + + if resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...); resp.Diagnostics.HasError() { + return + } + + timeout, diags := plan.Timeouts.Create(ctx, r.pConfigData.Timeout) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + var reqCreate requestCreateFabricItemProperties[Ttfprop, Titemprop] + + reqCreate.set(plan, r.Type) + + respCreate, err := r.client.CreateItem(ctx, plan.WorkspaceID.ValueString(), reqCreate.CreateItemRequest, nil) + if resp.Diagnostics.Append(utils.GetDiagsFromError(ctx, err, utils.OperationCreate, nil)...); resp.Diagnostics.HasError() { + return + } + + plan.ID = customtypes.NewUUIDValue(*respCreate.ID) + plan.WorkspaceID = customtypes.NewUUIDValue(*respCreate.WorkspaceID) + + if resp.Diagnostics.Append(r.get(ctx, &plan)...); resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + + tflog.Debug(ctx, "CREATE", map[string]any{ + "action": "end", + }) + + if resp.Diagnostics.HasError() { + return + } +} + +func (r *ResourceFabricItemProperties[Ttfprop, Titemprop]) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { //revive:disable-line:confusing-naming + tflog.Debug(ctx, "READ", map[string]any{ + "action": "start", + }) + tflog.Trace(ctx, "READ", map[string]any{ + "state": req.State, + }) + + var state ResourceFabricItemPropertiesModel[Ttfprop, Titemprop] + + if resp.Diagnostics.Append(req.State.Get(ctx, &state)...); resp.Diagnostics.HasError() { + return + } + + timeout, diags := state.Timeouts.Read(ctx, r.pConfigData.Timeout) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + diags = r.get(ctx, &state) + if utils.IsErrNotFound(state.ID.ValueString(), &diags, fabcore.ErrCommon.EntityNotFound) { + resp.State.RemoveResource(ctx) + + resp.Diagnostics.Append(diags...) + + return + } + + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + + tflog.Debug(ctx, "READ", map[string]any{ + "action": "end", + }) + + if resp.Diagnostics.HasError() { + return + } +} + +func (r *ResourceFabricItemProperties[Ttfprop, Titemprop]) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + tflog.Debug(ctx, "UPDATE", map[string]any{ + "action": "start", + }) + tflog.Trace(ctx, "UPDATE", map[string]any{ + "config": req.Config, + "plan": req.Plan, + "state": req.State, + }) + + var plan, state ResourceFabricItemPropertiesModel[Ttfprop, Titemprop] + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + + if resp.Diagnostics.HasError() { + return + } + + timeout, diags := plan.Timeouts.Update(ctx, r.pConfigData.Timeout) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + var reqUpdate requestUpdateFabricItemProperties[Ttfprop, Titemprop] + + reqUpdate.set(plan) + + _, err := r.client.UpdateItem(ctx, plan.WorkspaceID.ValueString(), plan.ID.ValueString(), reqUpdate.UpdateItemRequest, nil) + if resp.Diagnostics.Append(utils.GetDiagsFromError(ctx, err, utils.OperationUpdate, nil)...); resp.Diagnostics.HasError() { + return + } + + if resp.Diagnostics.Append(r.get(ctx, &plan)...); resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + + tflog.Debug(ctx, "UPDATE", map[string]any{ + "action": "end", + }) + + if resp.Diagnostics.HasError() { + return + } +} + +func (r *ResourceFabricItemProperties[Ttfprop, Titemprop]) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + tflog.Debug(ctx, "DELETE", map[string]any{ + "action": "start", + }) + tflog.Trace(ctx, "DELETE", map[string]any{ + "state": req.State, + }) + + var state ResourceFabricItemPropertiesModel[Ttfprop, Titemprop] + + if resp.Diagnostics.Append(req.State.Get(ctx, &state)...); resp.Diagnostics.HasError() { + return + } + + timeout, diags := state.Timeouts.Delete(ctx, r.pConfigData.Timeout) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + _, err := r.client.DeleteItem(ctx, state.WorkspaceID.ValueString(), state.ID.ValueString(), nil) + if resp.Diagnostics.Append(utils.GetDiagsFromError(ctx, err, utils.OperationDelete, nil)...); resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "DELETE", map[string]any{ + "action": "end", + }) +} + +func (r *ResourceFabricItemProperties[Ttfprop, Titemprop]) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + tflog.Debug(ctx, "IMPORT", map[string]any{ + "action": "start", + }) + tflog.Trace(ctx, "IMPORT", map[string]any{ + "id": req.ID, + }) + + workspaceID, fabricItemID, found := strings.Cut(req.ID, "/") + if !found { + resp.Diagnostics.AddError( + common.ErrorImportIdentifierHeader, + fmt.Sprintf( + common.ErrorImportIdentifierDetails, + fmt.Sprintf("WorkspaceID/%sID", string(r.Type)), + ), + ) + + return + } + + uuidWorkspaceID, diags := customtypes.NewUUIDValueMust(workspaceID) + resp.Diagnostics.Append(diags...) + + uuidFabricItemID, diags := customtypes.NewUUIDValueMust(fabricItemID) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + var timeout timeouts.Value + if resp.Diagnostics.Append(resp.State.GetAttribute(ctx, path.Root("timeouts"), &timeout)...); resp.Diagnostics.HasError() { + return + } + + state := ResourceFabricItemPropertiesModel[Ttfprop, Titemprop]{ + FabricItemPropertiesModel: FabricItemPropertiesModel[Ttfprop, Titemprop]{ + ID: uuidFabricItemID, + WorkspaceID: uuidWorkspaceID, + }, + Timeouts: timeout, + } + + if resp.Diagnostics.Append(r.get(ctx, &state)...); resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + + tflog.Debug(ctx, "IMPORT", map[string]any{ + "action": "end", + }) + + if resp.Diagnostics.HasError() { + return + } +} + +func (r *ResourceFabricItemProperties[Ttfprop, Titemprop]) get(ctx context.Context, model *ResourceFabricItemPropertiesModel[Ttfprop, Titemprop]) diag.Diagnostics { + tflog.Trace(ctx, fmt.Sprintf("getting %s by ID: %s", r.Name, model.ID.ValueString())) + + var fabricItem FabricItemProperties[Titemprop] + + err := r.ItemGetter(ctx, *r.pConfigData.FabricClient, *model, &fabricItem) + if diags := utils.GetDiagsFromError(ctx, err, utils.OperationRead, nil); diags.HasError() { + return diags + } + + model.set(fabricItem) + + return r.PropertiesSetter(ctx, fabricItem.Properties, model) +} diff --git a/internal/pkg/fabricitem/resource_schema.go b/internal/pkg/fabricitem/resource_schema.go index 8d86724..7db13fc 100644 --- a/internal/pkg/fabricitem/resource_schema.go +++ b/internal/pkg/fabricitem/resource_schema.go @@ -57,6 +57,17 @@ func GetResourceFabricItemPropertiesSchema(ctx context.Context, itemName, markdo } } +func GetResourceFabricItemPropertiesSchema1[Ttfprop, Titemprop any](ctx context.Context, r ResourceFabricItemProperties[Ttfprop, Titemprop]) schema.Schema { + attributes := getResourceFabricItemBaseAttributes(ctx, r.Name, r.DisplayNameMaxLength, r.DescriptionMaxLength, r.NameRenameAllowed) + + attributes["properties"] = r.PropertiesSchema + + return schema.Schema{ + MarkdownDescription: r.MarkdownDescription, + Attributes: attributes, + } +} + func GetResourceFabricItemDefinitionPropertiesSchema(ctx context.Context, r ResourceFabricItemDefinition, properties schema.SingleNestedAttribute) schema.Schema { attributes := getResourceFabricItemBaseAttributes(ctx, r.Name, r.DisplayNameMaxLength, r.DescriptionMaxLength, r.NameRenameAllowed) attributes["properties"] = properties diff --git a/internal/provider/provider.go b/internal/provider/provider.go index e9c89e0..cb78e4e 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -372,7 +372,7 @@ func (p *FabricProvider) Resources(ctx context.Context) []func() resource.Resour spark.NewResourceSparkEnvironmentSettings, spark.NewResourceSparkWorkspaceSettings, func() resource.Resource { return sparkjobdefinition.NewResourceSparkJobDefinition(ctx) }, - warehouse.NewResourceWarehouse, + func() resource.Resource { return warehouse.NewResourceWarehouse(ctx) }, workspace.NewResourceWorkspace, workspace.NewResourceWorkspaceRoleAssignment, workspace.NewResourceWorkspaceGit, @@ -422,8 +422,8 @@ func (p *FabricProvider) DataSources(ctx context.Context) []func() datasource.Da func() datasource.DataSource { return sparkjobdefinition.NewDataSourceSparkJobDefinition(ctx) }, func() datasource.DataSource { return sparkjobdefinition.NewDataSourceSparkJobDefinitions(ctx) }, sqlendpoint.NewDataSourceSQLEndpoints, - warehouse.NewDataSourceWarehouse, - warehouse.NewDataSourceWarehouses, + func() datasource.DataSource { return warehouse.NewDataSourceWarehouse(ctx) }, + func() datasource.DataSource { return warehouse.NewDataSourceWarehouses(ctx) }, workspace.NewDataSourceWorkspace, workspace.NewDataSourceWorkspaces, workspace.NewDataSourceWorkspaceRoleAssignments, diff --git a/internal/services/sparkjobdefinition/data_spark_job_definition.go b/internal/services/sparkjobdefinition/data_spark_job_definition.go index 7fd54d1..4d29ec5 100644 --- a/internal/services/sparkjobdefinition/data_spark_job_definition.go +++ b/internal/services/sparkjobdefinition/data_spark_job_definition.go @@ -37,8 +37,7 @@ func NewDataSourceSparkJobDefinition(ctx context.Context) datasource.DataSource propertiesModel := &sparkJobDefinitionPropertiesModel{} propertiesModel.set(from) - diags := properties.Set(ctx, propertiesModel) - if diags.HasError() { + if diags := properties.Set(ctx, propertiesModel); diags.HasError() { return diags } } diff --git a/internal/services/sparkjobdefinition/data_spark_job_definitions.go b/internal/services/sparkjobdefinition/data_spark_job_definitions.go index fe77242..ce376ec 100644 --- a/internal/services/sparkjobdefinition/data_spark_job_definitions.go +++ b/internal/services/sparkjobdefinition/data_spark_job_definitions.go @@ -36,8 +36,7 @@ func NewDataSourceSparkJobDefinitions(ctx context.Context) datasource.DataSource propertiesModel := &sparkJobDefinitionPropertiesModel{} propertiesModel.set(from) - diags := properties.Set(ctx, propertiesModel) - if diags.HasError() { + if diags := properties.Set(ctx, propertiesModel); diags.HasError() { return diags } } diff --git a/internal/services/sparkjobdefinition/resource_spark_job_definition_test.go b/internal/services/sparkjobdefinition/resource_spark_job_definition_test.go index eba7fd2..5576696 100644 --- a/internal/services/sparkjobdefinition/resource_spark_job_definition_test.go +++ b/internal/services/sparkjobdefinition/resource_spark_job_definition_test.go @@ -242,6 +242,7 @@ func TestUnit_SparkJobDefinitionResource_CRUD(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPtr(testResourceItemFQN, "display_name", entityAfter.DisplayName), resource.TestCheckResourceAttr(testResourceItemFQN, "definition_update_enabled", "true"), + // resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.onelake_root_path"), ), }, // Delete testing automatically occurs in TestCase @@ -273,6 +274,7 @@ func TestAcc_SparkJobDefinitionResource_CRUD(t *testing.T) { resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityCreateDisplayName), resource.TestCheckResourceAttr(testResourceItemFQN, "description", ""), resource.TestCheckResourceAttr(testResourceItemFQN, "definition_update_enabled", "true"), + resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.onelake_root_path"), ), }, // Update and Read @@ -291,6 +293,7 @@ func TestAcc_SparkJobDefinitionResource_CRUD(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), resource.TestCheckResourceAttr(testResourceItemFQN, "definition_update_enabled", "true"), + resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.onelake_root_path"), ), }, }, diff --git a/internal/services/warehouse/data_warehouse.go b/internal/services/warehouse/data_warehouse.go index 3f106b3..0fd8644 100644 --- a/internal/services/warehouse/data_warehouse.go +++ b/internal/services/warehouse/data_warehouse.go @@ -5,206 +5,109 @@ package warehouse import ( "context" - "fmt" supertypes "github.com/FrangipaneTeam/terraform-plugin-framework-supertypes" - "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - "github.com/hashicorp/terraform-plugin-framework-validators/datasourcevalidator" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/microsoft/fabric-sdk-go/fabric" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" fabwarehouse "github.com/microsoft/fabric-sdk-go/fabric/warehouse" - "github.com/microsoft/terraform-provider-fabric/internal/common" - "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" - "github.com/microsoft/terraform-provider-fabric/internal/pkg/utils" - pconfig "github.com/microsoft/terraform-provider-fabric/internal/provider/config" + "github.com/microsoft/terraform-provider-fabric/internal/pkg/fabricitem" ) -// Ensure the implementation satisfies the expected interfaces. -var ( - _ datasource.DataSourceWithConfigValidators = (*dataSourceWarehouse)(nil) - _ datasource.DataSourceWithConfigure = (*dataSourceWarehouse)(nil) -) - -type dataSourceWarehouse struct { - pConfigData *pconfig.ProviderData - client *fabwarehouse.ItemsClient -} - -func NewDataSourceWarehouse() datasource.DataSource { - return &dataSourceWarehouse{} -} - -func (d *dataSourceWarehouse) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_" + ItemTFName -} - -func (d *dataSourceWarehouse) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { - resp.Schema = schema.Schema{ - MarkdownDescription: "Get a Fabric Warehouse.\n\n" + - "Use this data source to fetch a [Warehouse](https://learn.microsoft.com/fabric/data-warehouse/data-warehousing).\n\n" + - ItemDocsSPNSupport, +func NewDataSourceWarehouse(ctx context.Context) datasource.DataSource { + propertiesSchema := schema.SingleNestedAttribute{ + MarkdownDescription: "The " + ItemName + " properties.", + Computed: true, + CustomType: supertypes.NewSingleNestedObjectTypeOf[warehousePropertiesModel](ctx), Attributes: map[string]schema.Attribute{ - "workspace_id": schema.StringAttribute{ - MarkdownDescription: "The Workspace ID.", - Required: true, - CustomType: customtypes.UUIDType{}, - }, - "id": schema.StringAttribute{ - MarkdownDescription: "The Warehouse ID.", - Optional: true, - Computed: true, - CustomType: customtypes.UUIDType{}, - }, - "display_name": schema.StringAttribute{ - MarkdownDescription: "The Warehouse display name.", - Optional: true, + "connection_string": schema.StringAttribute{ + MarkdownDescription: "The SQL connection string connected to the workspace containing this warehouse.", Computed: true, }, - "description": schema.StringAttribute{ - MarkdownDescription: "The Warehouse description.", + "created_date": schema.StringAttribute{ + MarkdownDescription: "The date and time the warehouse was created.", Computed: true, + CustomType: timetypes.RFC3339Type{}, }, - "properties": schema.SingleNestedAttribute{ + "last_updated_time": schema.StringAttribute{ + MarkdownDescription: "The date and time the warehouse was last updated.", Computed: true, - MarkdownDescription: "The Warehouse properties.", - CustomType: supertypes.NewSingleNestedObjectTypeOf[warehousePropertiesModel](ctx), - Attributes: map[string]schema.Attribute{ - "connection_string": schema.StringAttribute{ - MarkdownDescription: "Connection String", - Computed: true, - }, - "created_date": schema.StringAttribute{ - MarkdownDescription: "Created Date", - Computed: true, - CustomType: timetypes.RFC3339Type{}, - }, - "last_updated_time": schema.StringAttribute{ - MarkdownDescription: "Last Updated Time", - Computed: true, - CustomType: timetypes.RFC3339Type{}, - }, - }, + CustomType: timetypes.RFC3339Type{}, }, - "timeouts": timeouts.Attributes(ctx), }, } -} - -func (d *dataSourceWarehouse) ConfigValidators(_ context.Context) []datasource.ConfigValidator { - return []datasource.ConfigValidator{ - datasourcevalidator.Conflicting( - path.MatchRoot("id"), - path.MatchRoot("display_name"), - ), - datasourcevalidator.ExactlyOneOf( - path.MatchRoot("id"), - path.MatchRoot("display_name"), - ), - } -} - -func (d *dataSourceWarehouse) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - - pConfigData, ok := req.ProviderData.(*pconfig.ProviderData) - if !ok { - resp.Diagnostics.AddError( - common.ErrorDataSourceConfigType, - fmt.Sprintf(common.ErrorFabricClientType, req.ProviderData), - ) - - return - } - d.pConfigData = pConfigData - d.client = fabwarehouse.NewClientFactoryWithClient(*pConfigData.FabricClient).NewItemsClient() -} - -// Read refreshes the Terraform state with the latest data. -func (d *dataSourceWarehouse) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - tflog.Debug(ctx, "READ", map[string]any{ - "action": "start", - }) - tflog.Trace(ctx, "READ", map[string]any{ - "config": req.Config, - }) - - var data dataSourceWarehouseModel - - if resp.Diagnostics.Append(req.Config.Get(ctx, &data)...); resp.Diagnostics.HasError() { - return - } + propertiesSetter := func(ctx context.Context, from *fabwarehouse.Properties, to *fabricitem.DataSourceFabricItemPropertiesModel[warehousePropertiesModel, fabwarehouse.Properties]) diag.Diagnostics { + properties := supertypes.NewSingleNestedObjectValueOfNull[warehousePropertiesModel](ctx) - timeout, diags := data.Timeouts.Read(ctx, d.pConfigData.Timeout) - if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { - return - } + if from != nil { + propertiesModel := &warehousePropertiesModel{} + propertiesModel.set(from) - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() + if diags := properties.Set(ctx, propertiesModel); diags.HasError() { + return diags + } + } - if data.ID.ValueString() != "" { - diags = d.getByID(ctx, &data) - } else { - diags = d.getByDisplayName(ctx, &data) - } + to.Properties = properties - if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { - return + return nil } - resp.Diagnostics.Append(resp.State.Set(ctx, data)...) - - tflog.Debug(ctx, "READ", map[string]any{ - "action": "end", - }) + itemGetter := func(ctx context.Context, fabricClient fabric.Client, model fabricitem.DataSourceFabricItemPropertiesModel[warehousePropertiesModel, fabwarehouse.Properties], fabricItem *fabricitem.FabricItemProperties[fabwarehouse.Properties]) error { + client := fabwarehouse.NewClientFactoryWithClient(fabricClient).NewItemsClient() - if resp.Diagnostics.HasError() { - return - } -} + respGet, err := client.GetWarehouse(ctx, model.WorkspaceID.ValueString(), model.ID.ValueString(), nil) + if err != nil { + return err + } -func (d *dataSourceWarehouse) getByID(ctx context.Context, model *dataSourceWarehouseModel) diag.Diagnostics { - tflog.Trace(ctx, "getting Warehouse by 'id'") + fabricItem.Set(respGet.Warehouse) - respGet, err := d.client.GetWarehouse(ctx, model.WorkspaceID.ValueString(), model.ID.ValueString(), nil) - if diags := utils.GetDiagsFromError(ctx, err, utils.OperationRead, nil); diags.HasError() { - return diags + return nil } - return model.set(ctx, respGet.Warehouse) -} - -func (d *dataSourceWarehouse) getByDisplayName(ctx context.Context, model *dataSourceWarehouseModel) diag.Diagnostics { - tflog.Trace(ctx, "getting Warehouse by 'display_name'") + itemListGetter := func(ctx context.Context, fabricClient fabric.Client, model fabricitem.DataSourceFabricItemPropertiesModel[warehousePropertiesModel, fabwarehouse.Properties], errNotFound fabcore.ResponseError, fabricItem *fabricitem.FabricItemProperties[fabwarehouse.Properties]) error { + client := fabwarehouse.NewClientFactoryWithClient(fabricClient).NewItemsClient() - var diags diag.Diagnostics + pager := client.NewListWarehousesPager(model.WorkspaceID.ValueString(), nil) + for pager.More() { + page, err := pager.NextPage(ctx) + if err != nil { + return err + } - pager := d.client.NewListWarehousesPager(model.WorkspaceID.ValueString(), nil) - for pager.More() { - page, err := pager.NextPage(ctx) - if diags := utils.GetDiagsFromError(ctx, err, utils.OperationList, nil); diags.HasError() { - return diags - } + for _, entity := range page.Value { + if *entity.DisplayName == model.DisplayName.ValueString() { + fabricItem.Set(entity) - for _, entity := range page.Value { - if *entity.DisplayName == model.DisplayName.ValueString() { - return model.set(ctx, entity) + return nil + } } } + + return &errNotFound } - diags.AddError( - common.ErrorReadHeader, - fmt.Sprintf("Unable to find Warehouse with 'display_name': %s in the Workspace ID: %s ", model.DisplayName.ValueString(), model.WorkspaceID.ValueString()), - ) + config := fabricitem.DataSourceFabricItemProperties[warehousePropertiesModel, fabwarehouse.Properties]{ + DataSourceFabricItem: fabricitem.DataSourceFabricItem{ + Type: ItemType, + Name: ItemName, + TFName: ItemTFName, + MarkdownDescription: "Get a Fabric " + ItemName + ".\n\n" + + "Use this data source to fetch a [" + ItemName + "](" + ItemDocsURL + ").\n\n" + + ItemDocsSPNSupport, + IsDisplayNameUnique: true, + }, + PropertiesSchema: propertiesSchema, + PropertiesSetter: propertiesSetter, + ItemGetter: itemGetter, + ItemListGetter: itemListGetter, + } - return diags + return fabricitem.NewDataSourceFabricItemProperties(config) } diff --git a/internal/services/warehouse/data_warehouse_test.go b/internal/services/warehouse/data_warehouse_test.go index 12db769..ec6fac8 100644 --- a/internal/services/warehouse/data_warehouse_test.go +++ b/internal/services/warehouse/data_warehouse_test.go @@ -105,6 +105,9 @@ func TestUnit_WarehouseDataSource(t *testing.T) { resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "id", entity.ID), resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "display_name", entity.DisplayName), resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "description", entity.Description), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.connection_string"), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.created_date"), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.last_updated_time"), ), }, // read by id - not found @@ -133,6 +136,9 @@ func TestUnit_WarehouseDataSource(t *testing.T) { resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "id", entity.ID), resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "display_name", entity.DisplayName), resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "description", entity.Description), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.connection_string"), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.created_date"), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.last_updated_time"), ), }, // read by name - not found @@ -177,6 +183,9 @@ func TestAcc_WarehouseDataSource(t *testing.T) { resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityID), resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityDisplayName), resource.TestCheckResourceAttr(testDataSourceItemFQN, "description", entityDescription), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.connection_string"), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.created_date"), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.last_updated_time"), ), }, // read by id - not found @@ -204,6 +213,9 @@ func TestAcc_WarehouseDataSource(t *testing.T) { resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityID), resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityDisplayName), resource.TestCheckResourceAttr(testDataSourceItemFQN, "description", entityDescription), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.connection_string"), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.created_date"), + resource.TestCheckResourceAttrSet(testDataSourceItemFQN, "properties.last_updated_time"), ), }, // read by name - not found diff --git a/internal/services/warehouse/data_warehouses.go b/internal/services/warehouse/data_warehouses.go index cc228a9..ddf73ae 100644 --- a/internal/services/warehouse/data_warehouses.go +++ b/internal/services/warehouse/data_warehouses.go @@ -4,21 +4,96 @@ package warehouse import ( + "context" + + supertypes "github.com/FrangipaneTeam/terraform-plugin-framework-supertypes" + "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/microsoft/fabric-sdk-go/fabric" + fabwarehouse "github.com/microsoft/fabric-sdk-go/fabric/warehouse" "github.com/microsoft/terraform-provider-fabric/internal/pkg/fabricitem" ) -func NewDataSourceWarehouses() datasource.DataSource { - config := fabricitem.DataSourceFabricItems{ - Type: ItemType, - Name: ItemName, - Names: ItemsName, - TFName: ItemsTFName, - MarkdownDescription: "List a Fabric " + ItemsName + ".\n\n" + - "Use this data source to list [" + ItemsName + "](" + ItemDocsURL + ").\n\n" + - ItemDocsSPNSupport, +func NewDataSourceWarehouses(ctx context.Context) datasource.DataSource { + propertiesSchema := schema.SingleNestedAttribute{ + MarkdownDescription: "The " + ItemName + " properties.", + Computed: true, + CustomType: supertypes.NewSingleNestedObjectTypeOf[warehousePropertiesModel](ctx), + Attributes: map[string]schema.Attribute{ + "connection_string": schema.StringAttribute{ + MarkdownDescription: "The SQL connection string connected to the workspace containing this warehouse.", + Computed: true, + }, + "created_date": schema.StringAttribute{ + MarkdownDescription: "The date and time the warehouse was created.", + Computed: true, + CustomType: timetypes.RFC3339Type{}, + }, + "last_updated_time": schema.StringAttribute{ + MarkdownDescription: "The date and time the warehouse was last updated.", + Computed: true, + CustomType: timetypes.RFC3339Type{}, + }, + }, + } + + propertiesSetter := func(ctx context.Context, from *fabwarehouse.Properties, to *fabricitem.FabricItemPropertiesModel[warehousePropertiesModel, fabwarehouse.Properties]) diag.Diagnostics { + properties := supertypes.NewSingleNestedObjectValueOfNull[warehousePropertiesModel](ctx) + + if from != nil { + propertiesModel := &warehousePropertiesModel{} + propertiesModel.set(from) + + if diags := properties.Set(ctx, propertiesModel); diags.HasError() { + return diags + } + } + + to.Properties = properties + + return nil + } + + itemListGetter := func(ctx context.Context, fabricClient fabric.Client, model fabricitem.DataSourceFabricItemsPropertiesModel[warehousePropertiesModel, fabwarehouse.Properties], fabricItems *[]fabricitem.FabricItemProperties[fabwarehouse.Properties]) error { + client := fabwarehouse.NewClientFactoryWithClient(fabricClient).NewItemsClient() + + fabItems := make([]fabricitem.FabricItemProperties[fabwarehouse.Properties], 0) + + respList, err := client.ListWarehouses(ctx, model.WorkspaceID.ValueString(), nil) + if err != nil { + return err + } + + for _, entity := range respList { + var fabricItem fabricitem.FabricItemProperties[fabwarehouse.Properties] + + fabricItem.Set(entity) + + fabItems = append(fabItems, fabricItem) + } + + *fabricItems = fabItems + + return nil + } + + config := fabricitem.DataSourceFabricItemsProperties[warehousePropertiesModel, fabwarehouse.Properties]{ + DataSourceFabricItems: fabricitem.DataSourceFabricItems{ + Type: ItemType, + Name: ItemName, + Names: ItemsName, + TFName: ItemsTFName, + MarkdownDescription: "List a Fabric " + ItemsName + ".\n\n" + + "Use this data source to list [" + ItemsName + "](" + ItemDocsURL + ").\n\n" + + ItemDocsSPNSupport, + }, + PropertiesSchema: propertiesSchema, + PropertiesSetter: propertiesSetter, + ItemListGetter: itemListGetter, } - return fabricitem.NewDataSourceFabricItems(config) + return fabricitem.NewDataSourceFabricItemsProperties(config) } diff --git a/internal/services/warehouse/data_warehouses_test.go b/internal/services/warehouse/data_warehouses_test.go index f8e2274..18c6811 100644 --- a/internal/services/warehouse/data_warehouses_test.go +++ b/internal/services/warehouse/data_warehouses_test.go @@ -22,11 +22,11 @@ var ( func TestUnit_WarehousesDataSource(t *testing.T) { workspaceID := testhelp.RandomUUID() - entity := fakes.NewRandomItemWithWorkspace(itemType, workspaceID) + entity := fakes.NewRandomWarehouseWithWorkspace(workspaceID) - fakes.FakeServer.Upsert(fakes.NewRandomItemWithWorkspace(itemType, workspaceID)) + fakes.FakeServer.Upsert(fakes.NewRandomWarehouseWithWorkspace(workspaceID)) fakes.FakeServer.Upsert(entity) - fakes.FakeServer.Upsert(fakes.NewRandomItemWithWorkspace(itemType, workspaceID)) + fakes.FakeServer.Upsert(fakes.NewRandomWarehouseWithWorkspace(workspaceID)) resource.ParallelTest(t, testhelp.NewTestUnitCase(t, nil, fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ // error - no attributes diff --git a/internal/services/warehouse/models_resource_warehouse.go b/internal/services/warehouse/models_resource_warehouse.go deleted file mode 100644 index 081483d..0000000 --- a/internal/services/warehouse/models_resource_warehouse.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation -// SPDX-License-Identifier: MPL-2.0 - -package warehouse - -import ( - "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" - fabwarehouse "github.com/microsoft/fabric-sdk-go/fabric/warehouse" -) - -type resourceWarehouseModel struct { - baseWarehouseModel - Timeouts timeouts.Value `tfsdk:"timeouts"` -} - -type requestCreateWarehouse struct { - fabwarehouse.CreateWarehouseRequest -} - -func (to *requestCreateWarehouse) set(from resourceWarehouseModel) { - to.DisplayName = from.DisplayName.ValueStringPointer() - to.Description = from.Description.ValueStringPointer() -} - -type requestUpdateWarehouse struct { - fabwarehouse.UpdateWarehouseRequest -} - -func (to *requestUpdateWarehouse) set(from resourceWarehouseModel) { - to.DisplayName = from.DisplayName.ValueStringPointer() - to.Description = from.Description.ValueStringPointer() -} diff --git a/internal/services/warehouse/resource_warehouse.go b/internal/services/warehouse/resource_warehouse.go index 81f5f55..704507e 100644 --- a/internal/services/warehouse/resource_warehouse.go +++ b/internal/services/warehouse/resource_warehouse.go @@ -5,385 +5,87 @@ package warehouse import ( "context" - "fmt" - "strings" supertypes "github.com/FrangipaneTeam/terraform-plugin-framework-supertypes" - "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" "github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-log/tflog" - fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + "github.com/microsoft/fabric-sdk-go/fabric" fabwarehouse "github.com/microsoft/fabric-sdk-go/fabric/warehouse" - "github.com/microsoft/terraform-provider-fabric/internal/common" - "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" - "github.com/microsoft/terraform-provider-fabric/internal/pkg/utils" - pconfig "github.com/microsoft/terraform-provider-fabric/internal/provider/config" + "github.com/microsoft/terraform-provider-fabric/internal/pkg/fabricitem" ) -// Ensure the implementation satisfies the expected interfaces. -var ( - _ resource.ResourceWithConfigure = (*resourceWarehouse)(nil) - _ resource.ResourceWithImportState = (*resourceWarehouse)(nil) -) - -type resourceWarehouse struct { - pConfigData *pconfig.ProviderData - client *fabwarehouse.ItemsClient -} - -func NewResourceWarehouse() resource.Resource { - return &resourceWarehouse{} -} - -func (r *resourceWarehouse) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_" + ItemTFName -} - -func (r *resourceWarehouse) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ - MarkdownDescription: "This resource manages a Fabric Warehouse.\n\n" + - "See [Warehouse](https://learn.microsoft.com/fabric/data-warehouse/data-warehousing) for more information.\n\n" + - ItemDocsSPNSupport, +func NewResourceWarehouse(ctx context.Context) resource.Resource { + propertiesSchema := schema.SingleNestedAttribute{ + MarkdownDescription: "The " + ItemName + " properties.", + Computed: true, + CustomType: supertypes.NewSingleNestedObjectTypeOf[warehousePropertiesModel](ctx), Attributes: map[string]schema.Attribute{ - "workspace_id": schema.StringAttribute{ - MarkdownDescription: "The Workspace ID.", - Required: true, - CustomType: customtypes.UUIDType{}, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, - "id": schema.StringAttribute{ - MarkdownDescription: "The Warehouse ID.", + "connection_string": schema.StringAttribute{ + MarkdownDescription: "The SQL connection string connected to the workspace containing this warehouse.", Computed: true, - CustomType: customtypes.UUIDType{}, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - "display_name": schema.StringAttribute{ - MarkdownDescription: "The Warehouse display name.", - Required: true, - Validators: []validator.String{ - stringvalidator.LengthAtMost(123), - }, }, - "description": schema.StringAttribute{ - MarkdownDescription: "The Warehouse description.", - Optional: true, + "created_date": schema.StringAttribute{ + MarkdownDescription: "The date and time the warehouse was created.", Computed: true, - Default: stringdefault.StaticString(""), - Validators: []validator.String{ - stringvalidator.LengthAtMost(256), - }, + CustomType: timetypes.RFC3339Type{}, }, - "properties": schema.SingleNestedAttribute{ + "last_updated_time": schema.StringAttribute{ + MarkdownDescription: "The date and time the warehouse was last updated.", Computed: true, - MarkdownDescription: "The Warehouse properties.", - CustomType: supertypes.NewSingleNestedObjectTypeOf[warehousePropertiesModel](ctx), - Attributes: map[string]schema.Attribute{ - "connection_string": schema.StringAttribute{ - MarkdownDescription: "Connection String", - Computed: true, - }, - "created_date": schema.StringAttribute{ - MarkdownDescription: "Created Date", - Computed: true, - CustomType: timetypes.RFC3339Type{}, - }, - "last_updated_time": schema.StringAttribute{ - MarkdownDescription: "Last Updated Time", - Computed: true, - CustomType: timetypes.RFC3339Type{}, - }, - }, + CustomType: timetypes.RFC3339Type{}, }, - "timeouts": timeouts.AttributesAll(ctx), }, } -} - -func (r *resourceWarehouse) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - - pConfigData, ok := req.ProviderData.(*pconfig.ProviderData) - if !ok { - resp.Diagnostics.AddError( - common.ErrorResourceConfigType, - fmt.Sprintf(common.ErrorFabricClientType, req.ProviderData), - ) - - return - } - - r.pConfigData = pConfigData - r.client = fabwarehouse.NewClientFactoryWithClient(*pConfigData.FabricClient).NewItemsClient() -} - -func (r *resourceWarehouse) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - tflog.Debug(ctx, "CREATE", map[string]any{ - "action": "start", - }) - tflog.Trace(ctx, "CREATE", map[string]any{ - "config": req.Config, - "plan": req.Plan, - }) - - var plan resourceWarehouseModel - - if resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...); resp.Diagnostics.HasError() { - return - } - - timeout, diags := plan.Timeouts.Create(ctx, r.pConfigData.Timeout) - if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { - return - } - - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - var reqCreate requestCreateWarehouse - - reqCreate.set(plan) - - respCreate, err := r.client.CreateWarehouse(ctx, plan.WorkspaceID.ValueString(), reqCreate.CreateWarehouseRequest, nil) - if resp.Diagnostics.Append(utils.GetDiagsFromError(ctx, err, utils.OperationCreate, nil)...); resp.Diagnostics.HasError() { - return - } - - if resp.Diagnostics.Append(plan.set(ctx, respCreate.Warehouse)...); resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) - if resp.Diagnostics.Append(r.get(ctx, &plan, utils.OperationCreate)...); resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) - - tflog.Debug(ctx, "CREATE", map[string]any{ - "action": "end", - }) - - if resp.Diagnostics.HasError() { - return - } -} + propertiesSetter := func(ctx context.Context, from *fabwarehouse.Properties, to *fabricitem.ResourceFabricItemPropertiesModel[warehousePropertiesModel, fabwarehouse.Properties]) diag.Diagnostics { + properties := supertypes.NewSingleNestedObjectValueOfNull[warehousePropertiesModel](ctx) -func (r *resourceWarehouse) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - tflog.Debug(ctx, "READ", map[string]any{ - "action": "start", - }) - tflog.Trace(ctx, "READ", map[string]any{ - "state": req.State, - }) + if from != nil { + propertiesModel := &warehousePropertiesModel{} + propertiesModel.set(from) - var state resourceWarehouseModel - - if resp.Diagnostics.Append(req.State.Get(ctx, &state)...); resp.Diagnostics.HasError() { - return - } - - timeout, diags := state.Timeouts.Read(ctx, r.pConfigData.Timeout) - if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { - return - } - - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - if diags := r.get(ctx, &state, utils.OperationRead); diags.HasError() { - if utils.IsErrNotFound(state.ID.ValueString(), &diags, fabcore.ErrCommon.EntityNotFound) { - resp.State.RemoveResource(ctx) + if diags := properties.Set(ctx, propertiesModel); diags.HasError() { + return diags + } } - resp.Diagnostics.Append(diags...) - - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, state)...) - - tflog.Debug(ctx, "READ", map[string]any{ - "action": "end", - }) - - if resp.Diagnostics.HasError() { - return - } -} - -func (r *resourceWarehouse) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - tflog.Debug(ctx, "UPDATE", map[string]any{ - "action": "start", - }) - tflog.Trace(ctx, "UPDATE", map[string]any{ - "config": req.Config, - "plan": req.Plan, - "state": req.State, - }) - - var plan resourceWarehouseModel - - if resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...); resp.Diagnostics.HasError() { - return - } - - timeout, diags := plan.Timeouts.Update(ctx, r.pConfigData.Timeout) - if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { - return - } - - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - var reqUpdate requestUpdateWarehouse - - reqUpdate.set(plan) - - respUpdate, err := r.client.UpdateWarehouse(ctx, plan.WorkspaceID.ValueString(), plan.ID.ValueString(), reqUpdate.UpdateWarehouseRequest, nil) - if resp.Diagnostics.Append(utils.GetDiagsFromError(ctx, err, utils.OperationUpdate, nil)...); resp.Diagnostics.HasError() { - return - } - - if resp.Diagnostics.Append(plan.set(ctx, respUpdate.Warehouse)...); resp.Diagnostics.HasError() { - return - } - - if resp.Diagnostics.Append(resp.State.Set(ctx, plan)...); resp.Diagnostics.HasError() { - return - } - - if resp.Diagnostics.Append(r.get(ctx, &plan, utils.OperationUpdate)...); resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) - - tflog.Debug(ctx, "UPDATE", map[string]any{ - "action": "end", - }) - - if resp.Diagnostics.HasError() { - return - } -} - -func (r *resourceWarehouse) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - tflog.Debug(ctx, "DELETE", map[string]any{ - "action": "start", - }) - tflog.Trace(ctx, "DELETE", map[string]any{ - "state": req.State, - }) - - var state resourceWarehouseModel + to.Properties = properties - if resp.Diagnostics.Append(req.State.Get(ctx, &state)...); resp.Diagnostics.HasError() { - return + return nil } - timeout, diags := state.Timeouts.Delete(ctx, r.pConfigData.Timeout) - if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { - return - } - - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - _, err := r.client.DeleteWarehouse(ctx, state.WorkspaceID.ValueString(), state.ID.ValueString(), nil) - if resp.Diagnostics.Append(utils.GetDiagsFromError(ctx, err, utils.OperationDelete, nil)...); resp.Diagnostics.HasError() { - return - } - - tflog.Debug(ctx, "DELETE", map[string]any{ - "action": "end", - }) -} - -func (r *resourceWarehouse) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - tflog.Debug(ctx, "IMPORT", map[string]any{ - "action": "start", - }) - tflog.Trace(ctx, "IMPORT", map[string]any{ - "id": req.ID, - }) + itemGetter := func(ctx context.Context, fabricClient fabric.Client, model fabricitem.ResourceFabricItemPropertiesModel[warehousePropertiesModel, fabwarehouse.Properties], fabricItem *fabricitem.FabricItemProperties[fabwarehouse.Properties]) error { + client := fabwarehouse.NewClientFactoryWithClient(fabricClient).NewItemsClient() - workspaceID, warehouseID, found := strings.Cut(req.ID, "/") - if !found { - resp.Diagnostics.AddError( - common.ErrorImportIdentifierHeader, - fmt.Sprintf(common.ErrorImportIdentifierDetails, "WorkspaceID/WarehouseID"), - ) - - return - } - - uuidWorkspaceID, diags := customtypes.NewUUIDValueMust(workspaceID) - resp.Diagnostics.Append(diags...) - - uuidID, diags := customtypes.NewUUIDValueMust(warehouseID) - resp.Diagnostics.Append(diags...) + respGet, err := client.GetWarehouse(ctx, model.WorkspaceID.ValueString(), model.ID.ValueString(), nil) + if err != nil { + return err + } - if resp.Diagnostics.HasError() { - return - } + fabricItem.Set(respGet.Warehouse) - var timeout timeouts.Value - if resp.Diagnostics.Append(resp.State.GetAttribute(ctx, path.Root("timeouts"), &timeout)...); resp.Diagnostics.HasError() { - return + return nil } - state := resourceWarehouseModel{ - baseWarehouseModel: baseWarehouseModel{ - ID: uuidID, - WorkspaceID: uuidWorkspaceID, + config := fabricitem.ResourceFabricItemProperties[warehousePropertiesModel, fabwarehouse.Properties]{ + ResourceFabricItem: fabricitem.ResourceFabricItem{ + Type: ItemType, + Name: ItemName, + NameRenameAllowed: true, + TFName: ItemTFName, + MarkdownDescription: "Manage a Fabric " + ItemName + ".\n\n" + + "Use this resource to manage a [" + ItemName + "](" + ItemDocsURL + ").\n\n" + + ItemDocsSPNSupport, + DisplayNameMaxLength: 123, + DescriptionMaxLength: 256, }, - Timeouts: timeout, - } - - if resp.Diagnostics.Append(r.get(ctx, &state, utils.OperationImport)...); resp.Diagnostics.HasError() { - return - } - - resp.Diagnostics.Append(resp.State.Set(ctx, state)...) - - tflog.Debug(ctx, "IMPORT", map[string]any{ - "action": "end", - }) - - if resp.Diagnostics.HasError() { - return - } -} - -func (r *resourceWarehouse) get(ctx context.Context, model *resourceWarehouseModel, operation utils.Operation) diag.Diagnostics { - tflog.Trace(ctx, "getting Warehouse") - - var errIs error - if operation == utils.OperationRead { - errIs = fabcore.ErrCommon.EntityNotFound - } - - respGet, err := r.client.GetWarehouse(ctx, model.WorkspaceID.ValueString(), model.ID.ValueString(), nil) - if diags := utils.GetDiagsFromError(ctx, err, operation, errIs); diags.HasError() { - return diags + PropertiesSchema: propertiesSchema, + PropertiesSetter: propertiesSetter, + ItemGetter: itemGetter, } - return model.set(ctx, respGet.Warehouse) + return fabricitem.NewResourceFabricItemProperties(config) } diff --git a/internal/services/warehouse/resource_warehouse_test.go b/internal/services/warehouse/resource_warehouse_test.go index 70de53c..9648bf8 100644 --- a/internal/services/warehouse/resource_warehouse_test.go +++ b/internal/services/warehouse/resource_warehouse_test.go @@ -204,6 +204,9 @@ func TestUnit_WarehouseResource_CRUD(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPtr(testResourceItemFQN, "display_name", entityAfter.DisplayName), resource.TestCheckResourceAttrPtr(testResourceItemFQN, "description", entityAfter.Description), + // resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.connection_string"), + // resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.created_date"), + // resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.last_updated_time"), ), }, // Delete testing automatically occurs in TestCase @@ -236,6 +239,9 @@ func TestAcc_WarehouseResource_CRUD(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityCreateDisplayName), resource.TestCheckResourceAttr(testResourceItemFQN, "description", ""), + resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.connection_string"), + resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.created_date"), + resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.last_updated_time"), ), }, // Update and Read @@ -252,6 +258,9 @@ func TestAcc_WarehouseResource_CRUD(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), + resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.connection_string"), + resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.created_date"), + resource.TestCheckResourceAttrSet(testResourceItemFQN, "properties.last_updated_time"), ), }, }, diff --git a/internal/testhelp/fakes/fabric_sparkjobdefinition.go b/internal/testhelp/fakes/fabric_sparkjobdefinition.go index 325eed7..58de117 100644 --- a/internal/testhelp/fakes/fabric_sparkjobdefinition.go +++ b/internal/testhelp/fakes/fabric_sparkjobdefinition.go @@ -94,8 +94,8 @@ func (o *operationsSparkJobDefinition) TransformUpdate(entity fabsparkjobdefinit // Update implements concreteOperations. func (o *operationsSparkJobDefinition) Update(base fabsparkjobdefinition.SparkJobDefinition, data fabsparkjobdefinition.UpdateSparkJobDefinitionRequest) fabsparkjobdefinition.SparkJobDefinition { - base.Description = data.Description base.DisplayName = data.DisplayName + base.Description = data.Description return base }