From 89b87aca08664d85ef256ff603c284078d51188a Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Sun, 26 Jan 2025 12:38:53 +0000 Subject: [PATCH 01/14] add gateway resource + datasource w/o tests --- internal/provider/provider.go | 2 + internal/services/gateway/base.go | 23 ++ internal/services/gateway/data_gateway.go | 189 +++++++++ internal/services/gateway/data_gateways.go | 174 ++++++++ internal/services/gateway/models.go | 74 ++++ .../services/gateway/models_data_gateway.go | 13 + .../services/gateway/models_data_gateways.go | 30 ++ .../gateway/models_resource_gateway.go | 82 ++++ internal/services/gateway/resource_gateway.go | 390 ++++++++++++++++++ 9 files changed, 977 insertions(+) create mode 100644 internal/services/gateway/base.go create mode 100644 internal/services/gateway/data_gateway.go create mode 100644 internal/services/gateway/data_gateways.go create mode 100644 internal/services/gateway/models.go create mode 100644 internal/services/gateway/models_data_gateway.go create mode 100644 internal/services/gateway/models_data_gateways.go create mode 100644 internal/services/gateway/models_resource_gateway.go create mode 100644 internal/services/gateway/resource_gateway.go diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 12b191b4..99e3ef45 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -41,6 +41,7 @@ import ( "github.com/microsoft/terraform-provider-fabric/internal/services/environment" "github.com/microsoft/terraform-provider-fabric/internal/services/eventhouse" "github.com/microsoft/terraform-provider-fabric/internal/services/eventstream" + "github.com/microsoft/terraform-provider-fabric/internal/services/gateway" "github.com/microsoft/terraform-provider-fabric/internal/services/kqldatabase" "github.com/microsoft/terraform-provider-fabric/internal/services/kqlqueryset" "github.com/microsoft/terraform-provider-fabric/internal/services/lakehouse" @@ -396,6 +397,7 @@ func (p *FabricProvider) DataSources(ctx context.Context) []func() datasource.Da func() datasource.DataSource { return eventhouse.NewDataSourceEventhouses(ctx) }, eventstream.NewDataSourceEventstream, eventstream.NewDataSourceEventstreams, + gateway.NewDataSourceGateway, kqldatabase.NewDataSourceKQLDatabase, kqldatabase.NewDataSourceKQLDatabases, kqlqueryset.NewDataSourceKQLQueryset, diff --git a/internal/services/gateway/base.go b/internal/services/gateway/base.go new file mode 100644 index 00000000..1d4c87c8 --- /dev/null +++ b/internal/services/gateway/base.go @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "github.com/microsoft/terraform-provider-fabric/internal/common" +) + +const ( + ItemName = "Gateway" + ItemTFName = "gateway" + ItemsName = "Gateways" + ItemsTFName = "gateways" + ItemDocsSPNSupport = common.DocsSPNSupported + ItemDocsURL = "https://learn.microsoft.com/en-us/power-bi/guidance/powerbi-implementation-planning-data-gateways" +) + +var ( + PossibleInactivityMinutesBeforeSleepValues = []int32{30, 60, 90, 120, 150, 240, 360, 480, 720, 1440} + MinNumberOfMemberGatewaysValues = int32(1) + MaxNumberOfMemberGatewaysValues = int32(7) +) diff --git a/internal/services/gateway/data_gateway.go b/internal/services/gateway/data_gateway.go new file mode 100644 index 00000000..c389441b --- /dev/null +++ b/internal/services/gateway/data_gateway.go @@ -0,0 +1,189 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" + "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" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" + + "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" +) + +var ( + _ datasource.DataSourceWithConfigValidators = (*dataSourceGateway)(nil) + _ datasource.DataSourceWithConfigure = (*dataSourceGateway)(nil) +) + +type dataSourceGateway struct { + pConfigData *pconfig.ProviderData + client *fabcore.GatewaysClient +} + +func NewDataSourceGateway() datasource.DataSource { + return &dataSourceGateway{} +} + +func (d *dataSourceGateway) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_" + ItemTFName +} + +func (d *dataSourceGateway) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Get a Fabric " + ItemName + ".\n\n" + + "Use this data source to get [" + ItemName + "](" + ItemDocsURL + ").\n\n" + + ItemDocsSPNSupport, + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " ID.", + Optional: true, + Computed: true, + CustomType: customtypes.UUIDType{}, + }, + "display_name": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " display name.", + Optional: true, + Computed: true, + }, + "type": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " type. Possible values: " + utils.ConvertStringSlicesToString(fabcore.PossibleGatewayTypeValues(), true, true), + Computed: true, + }, + "capacity_id": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " capacity ID.", + Computed: true, + CustomType: customtypes.UUIDType{}, + }, + "inactivity_minutes_before_sleep": schema.Int32Attribute{ + MarkdownDescription: "The " + ItemName + " inactivity minutes before sleep. Possible values: " + utils.ConvertStringSlicesToString(PossibleInactivityMinutesBeforeSleepValues, true, true), + Computed: true, + }, + "number_of_member_gateways": schema.Int32Attribute{ + MarkdownDescription: "The number of member gateways. Possible values: " + fmt.Sprint(MinNumberOfMemberGatewaysValues) + " to " + fmt.Sprint(MaxNumberOfMemberGatewaysValues) + ".", + Computed: true, + }, + "virtual_network_azure_resource": schema.SingleNestedAttribute{ + MarkdownDescription: "The Azure virtual network resource.", + Computed: true, + CustomType: supertypes.NewSingleNestedObjectTypeOf[virtualNetworkAzureResourceModel](ctx), + Attributes: map[string]schema.Attribute{ + "resource_group_name": schema.StringAttribute{ + MarkdownDescription: "The resource group name.", + Computed: true, + }, + "subnet_name": schema.StringAttribute{ + MarkdownDescription: "The subnet name.", + Computed: true, + }, + "subscription_id": schema.StringAttribute{ + MarkdownDescription: "The subscription ID.", + Computed: true, + CustomType: customtypes.UUIDType{}, + }, + "virtual_network_name": schema.StringAttribute{ + MarkdownDescription: "The virtual network name.", + Computed: true, + }, + }, + }, + "timeouts": timeouts.Attributes(ctx), + }, + } +} + +func (d *dataSourceGateway) 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 *dataSourceGateway) 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 = fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewGatewaysClient() +} + +func (d *dataSourceGateway) 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 dataSourceGatewayModel + + 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 resp.Diagnostics.Append(d.get(ctx, &data)...); 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 *dataSourceGateway) get(ctx context.Context, model *dataSourceGatewayModel) diag.Diagnostics { + tflog.Trace(ctx, "GET "+ItemName+" BY ID", map[string]any{ + "id": model.DisplayName.ValueString(), + }) + + respGet, err := d.client.GetGateway(ctx, model.ID.ValueString(), nil) + if diags := utils.GetDiagsFromError(ctx, err, utils.OperationRead, nil); diags.HasError() { + return diags + } + + model.set(ctx, respGet.GatewayClassification) + + return nil +} diff --git a/internal/services/gateway/data_gateways.go b/internal/services/gateway/data_gateways.go new file mode 100644 index 00000000..103d9856 --- /dev/null +++ b/internal/services/gateway/data_gateways.go @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" + "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-log/tflog" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" + + "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" +) + +var _ datasource.DataSourceWithConfigure = (*dataSourceGateways)(nil) + +type dataSourceGateways struct { + pConfigData *pconfig.ProviderData + client *fabcore.GatewaysClient +} + +func NewDataSourceGateways() datasource.DataSource { + return &dataSourceGateways{} +} + +func (d *dataSourceGateways) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_" + ItemsTFName +} + +func (d *dataSourceGateways) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "List a Fabric " + ItemsName + ".\n\n" + + "Use this data source to list [" + ItemsName + "](" + ItemDocsURL + ").\n\n" + + ItemDocsSPNSupport, + Attributes: map[string]schema.Attribute{ + "values": schema.ListNestedAttribute{ + MarkdownDescription: "The list of " + ItemsName + ".", + Computed: true, + CustomType: supertypes.NewListNestedObjectTypeOf[baseGatewayModel](ctx), + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " ID.", + Computed: true, + CustomType: customtypes.UUIDType{}, + }, + "display_name": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " display name.", + Computed: true, + }, + "type": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " type. Possible values: " + utils.ConvertStringSlicesToString(fabcore.PossibleGatewayTypeValues(), true, true), + Computed: true, + }, + "capacity_id": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " capacity ID.", + Computed: true, + CustomType: customtypes.UUIDType{}, + }, + "inactivity_minutes_before_sleep": schema.Int32Attribute{ + MarkdownDescription: "The " + ItemName + " inactivity minutes before sleep. Possible values: " + utils.ConvertStringSlicesToString(PossibleInactivityMinutesBeforeSleepValues, true, true), + Computed: true, + }, + "number_of_member_gateways": schema.Int32Attribute{ + MarkdownDescription: "The number of member gateways. Possible values: " + fmt.Sprint(MinNumberOfMemberGatewaysValues) + " to " + fmt.Sprint(MaxNumberOfMemberGatewaysValues) + ".", + Computed: true, + }, + "virtual_network_azure_resource": schema.SingleNestedAttribute{ + MarkdownDescription: "The Azure virtual network resource.", + Computed: true, + CustomType: supertypes.NewSingleNestedObjectTypeOf[virtualNetworkAzureResourceModel](ctx), + Attributes: map[string]schema.Attribute{ + "resource_group_name": schema.StringAttribute{ + MarkdownDescription: "The resource group name.", + Computed: true, + }, + "subnet_name": schema.StringAttribute{ + MarkdownDescription: "The subnet name.", + Computed: true, + }, + "subscription_id": schema.StringAttribute{ + MarkdownDescription: "The subscription ID.", + Computed: true, + CustomType: customtypes.UUIDType{}, + }, + "virtual_network_name": schema.StringAttribute{ + MarkdownDescription: "The virtual network name.", + Computed: true, + }, + }, + }, + }, + }, + }, + "timeouts": timeouts.Attributes(ctx), + }, + } +} + +func (d *dataSourceGateways) 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 = fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewGatewaysClient() +} + +func (d *dataSourceGateways) 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 dataSourceGatewaysModel + + 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 resp.Diagnostics.Append(d.list(ctx, &data)...); 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 *dataSourceGateways) list(ctx context.Context, model *dataSourceGatewaysModel) diag.Diagnostics { + tflog.Trace(ctx, "getting "+ItemsName) + + respList, err := d.client.ListGateways(ctx, nil) + if diags := utils.GetDiagsFromError(ctx, err, utils.OperationList, nil); diags.HasError() { + return diags + } + + return model.setValues(ctx, respList) +} diff --git a/internal/services/gateway/models.go b/internal/services/gateway/models.go new file mode 100644 index 00000000..74e54105 --- /dev/null +++ b/internal/services/gateway/models.go @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" + + "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" +) + +type baseGatewayModel struct { + ID customtypes.UUID `tfsdk:"id"` + Type types.String `tfsdk:"type"` + + DisplayName types.String `tfsdk:"display_name"` + CapacityID customtypes.UUID `tfsdk:"capacity_id"` + InactivityMinutesBeforeSleep types.Int32 `tfsdk:"inactivity_minutes_before_sleep"` + NumberOfMemberGateways types.Int32 `tfsdk:"number_of_member_gateways"` + VirtualNetworkAzureResource supertypes.SingleNestedObjectValueOf[virtualNetworkAzureResourceModel] `tfsdk:"virtual_network_azure_resource"` +} + +func (to *baseGatewayModel) set(ctx context.Context, from fabcore.GatewayClassification) diag.Diagnostics { + var diags diag.Diagnostics + + switch gateway := from.(type) { + case *fabcore.VirtualNetworkGateway: + to.ID = customtypes.NewUUIDPointerValue(gateway.ID) + to.Type = types.StringPointerValue((*string)(gateway.Type)) + to.DisplayName = types.StringPointerValue(gateway.DisplayName) + to.CapacityID = customtypes.NewUUIDPointerValue(gateway.CapacityID) + to.InactivityMinutesBeforeSleep = types.Int32PointerValue(gateway.InactivityMinutesBeforeSleep) + to.NumberOfMemberGateways = types.Int32PointerValue(gateway.NumberOfMemberGateways) + + virtualNetworkAzureResource := supertypes.NewSingleNestedObjectValueOfNull[virtualNetworkAzureResourceModel](ctx) + if gateway.VirtualNetworkAzureResource != nil { + virtualNetworkAzureResourceModel := &virtualNetworkAzureResourceModel{} + virtualNetworkAzureResourceModel.set(gateway.VirtualNetworkAzureResource) + + if diags := virtualNetworkAzureResource.Set(ctx, virtualNetworkAzureResourceModel); diags.HasError() { + return diags + } + } + + to.VirtualNetworkAzureResource = virtualNetworkAzureResource + + default: + diags.AddError("Unsupported Gateway type", fmt.Sprintf("The Gateway type '%T' is not supported.", gateway)) + + return diags + } + + return nil +} + +type virtualNetworkAzureResourceModel struct { + ResourceGroupName types.String `tfsdk:"resource_group_name"` + SubnetName types.String `tfsdk:"subnet_name"` + SubscriptionID customtypes.UUID `tfsdk:"subscription_id"` + VirtualNetworkName types.String `tfsdk:"virtual_network_name"` // Rename to just 'name' or 'display_name'? +} + +func (to *virtualNetworkAzureResourceModel) set(from *fabcore.VirtualNetworkAzureResource) { + to.ResourceGroupName = types.StringPointerValue(from.ResourceGroupName) + to.SubnetName = types.StringPointerValue(from.SubnetName) + to.SubscriptionID = customtypes.NewUUIDPointerValue(from.SubscriptionID) + to.VirtualNetworkName = types.StringPointerValue(from.VirtualNetworkName) +} diff --git a/internal/services/gateway/models_data_gateway.go b/internal/services/gateway/models_data_gateway.go new file mode 100644 index 00000000..d7aebed6 --- /dev/null +++ b/internal/services/gateway/models_data_gateway.go @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" +) + +type dataSourceGatewayModel struct { + baseGatewayModel + Timeouts timeouts.Value `tfsdk:"timeouts"` +} diff --git a/internal/services/gateway/models_data_gateways.go b/internal/services/gateway/models_data_gateways.go new file mode 100644 index 00000000..8e9307cf --- /dev/null +++ b/internal/services/gateway/models_data_gateways.go @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/diag" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" +) + +type dataSourceGatewaysModel struct { + Values supertypes.ListNestedObjectValueOf[baseGatewayModel] `tfsdk:"values"` + Timeouts timeouts.Value `tfsdk:"timeouts"` +} + +func (to *dataSourceGatewaysModel) setValues(ctx context.Context, from []fabcore.GatewayClassification) diag.Diagnostics { + slice := make([]*baseGatewayModel, 0, len(from)) + + for _, entity := range from { + var entityModel baseGatewayModel + entityModel.set(ctx, entity) + slice = append(slice, &entityModel) + } + + return to.Values.Set(ctx, slice) +} diff --git a/internal/services/gateway/models_resource_gateway.go b/internal/services/gateway/models_resource_gateway.go new file mode 100644 index 00000000..c10862f1 --- /dev/null +++ b/internal/services/gateway/models_resource_gateway.go @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/diag" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" +) + +type resourceGatewayModel struct { + baseGatewayModel + Timeouts timeouts.Value `tfsdk:"timeouts"` +} + +type requestCreateGateway struct { + fabcore.CreateGatewayRequestClassification +} + +func (to *requestCreateGateway) set(ctx context.Context, from resourceGatewayModel) diag.Diagnostics { + var diags diag.Diagnostics + + gatewayType := (fabcore.GatewayType)(from.Type.ValueString()) + + switch gatewayType { + case fabcore.GatewayTypeVirtualNetwork: + virtualNetworkAzureResource, diags := from.VirtualNetworkAzureResource.Get(ctx) + if diags.HasError() { + return diags + } + + to.CreateGatewayRequestClassification = &fabcore.CreateVirtualNetworkGatewayRequest{ + Type: &gatewayType, + CapacityID: from.CapacityID.ValueStringPointer(), + DisplayName: from.DisplayName.ValueStringPointer(), + InactivityMinutesBeforeSleep: from.InactivityMinutesBeforeSleep.ValueInt32Pointer(), + NumberOfMemberGateways: from.NumberOfMemberGateways.ValueInt32Pointer(), + VirtualNetworkAzureResource: &fabcore.VirtualNetworkAzureResource{ + SubscriptionID: virtualNetworkAzureResource.SubscriptionID.ValueStringPointer(), + ResourceGroupName: virtualNetworkAzureResource.ResourceGroupName.ValueStringPointer(), + VirtualNetworkName: virtualNetworkAzureResource.VirtualNetworkName.ValueStringPointer(), + SubnetName: virtualNetworkAzureResource.SubnetName.ValueStringPointer(), + }, + } + default: + diags.AddError("Unsupported Gateway type", fmt.Sprintf("The Gateway type '%T' is not supported.", gatewayType)) + + return diags + } + + return nil +} + +type requestUpdateGateway struct { + fabcore.UpdateGatewayRequestClassification +} + +func (to *requestUpdateGateway) set(from resourceGatewayModel) diag.Diagnostics { + var diags diag.Diagnostics + + gatewayType := (fabcore.GatewayType)(from.Type.ValueString()) + + switch gatewayType { + case fabcore.GatewayTypeVirtualNetwork: + to.UpdateGatewayRequestClassification = &fabcore.UpdateVirtualNetworkGatewayRequest{ + Type: &gatewayType, + DisplayName: from.DisplayName.ValueStringPointer(), + CapacityID: from.CapacityID.ValueStringPointer(), + InactivityMinutesBeforeSleep: from.InactivityMinutesBeforeSleep.ValueInt32Pointer(), + } + default: + diags.AddError("Unsupported Gateway type", fmt.Sprintf("The Gateway type '%T' is not supported.", gatewayType)) + + return diags + } + + return nil +} diff --git a/internal/services/gateway/resource_gateway.go b/internal/services/gateway/resource_gateway.go new file mode 100644 index 00000000..777fdac4 --- /dev/null +++ b/internal/services/gateway/resource_gateway.go @@ -0,0 +1,390 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/int32validator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" + "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" + supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" + supersetvalidator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/setvalidator" + superstringvalidator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/stringvalidator" + + "github.com/hashicorp/terraform-plugin-framework/types" + + "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 = (*resourceGateway)(nil) + // _ resource.ResourceWithImportState = (*resourceGateway)(nil) +) + +type resourceGateway struct { + pConfigData *pconfig.ProviderData + client *fabcore.GatewaysClient +} + +func NewResourceGateway() resource.Resource { + return &resourceGateway{} +} + +func (r *resourceGateway) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_" + ItemTFName +} + +func (r *resourceGateway) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "This resource manages a Fabric " + ItemName + ".\n\n" + + "See [" + ItemName + "s](" + ItemDocsURL + ") for more information.\n\n" + + ItemDocsSPNSupport, + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " ID.", + Computed: true, + CustomType: customtypes.UUIDType{}, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "display_name": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " display name.", + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtMost(200), + superstringvalidator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), + []attr.Value{ + types.StringValue(string(fabcore.GatewayTypeVirtualNetwork)), + }), + }, + }, + "type": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " type. Accepted values: " + utils.ConvertStringSlicesToString(fabcore.PossibleGatewayTypeValues(), true, true), + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.OneOf(utils.ConvertEnumsToStringSlices(fabcore.PossibleGatewayTypeValues(), false)...), + }, + }, + "capacity_id": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " capacity ID.", + Optional: true, + CustomType: customtypes.UUIDType{}, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Validators: []validator.String{ + superstringvalidator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), + []attr.Value{ + types.StringValue(string(fabcore.GatewayTypeVirtualNetwork)), + }), + }, + }, + "inactivity_minutes_before_sleep": schema.Int32Attribute{ + MarkdownDescription: "The " + ItemName + " inactivity minutes before sleep.", + Required: true, + Validators: []validator.Int32{ + int32validator.OneOf(PossibleInactivityMinutesBeforeSleepValues...), + // TODO: There is only int64validator in the framework, figure out how to utilize it easily. In the meantime, I set Required to true. + // superint64validator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), + // []attr.Value{ + // types.StringValue(string(fabcore.GatewayTypeVirtualNetwork)), + // }), + }, + }, + "number_of_member_gateways": schema.Int32Attribute{ + MarkdownDescription: "The " + ItemName + " number of member gateways.", + Required: true, + Validators: []validator.Int32{ + int32validator.Between(MinNumberOfMemberGatewaysValues, MaxNumberOfMemberGatewaysValues), + // TODO: There is only int64validator in the framework, figure out how to utilize it easily. In the meantime, I set Required to true. + // superint64validator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), + // []attr.Value{ + // types.StringValue(string(fabcore.GatewayTypeVirtualNetwork)), + // }), + }, + }, + "virtual_network_azure_resource": schema.SetNestedAttribute{ + MarkdownDescription: "The " + ItemName + " virtual network Azure resource.", + Optional: true, + CustomType: supertypes.NewSetNestedObjectTypeOf[virtualNetworkAzureResourceModel](ctx), + PlanModifiers: []planmodifier.Set{ + setplanmodifier.RequiresReplace(), + }, + Validators: []validator.Set{ + supersetvalidator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), + []attr.Value{ + types.StringValue(string(fabcore.GatewayTypeVirtualNetwork)), + }), + }, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "virtual_network_name": schema.StringAttribute{ + MarkdownDescription: "The virtual network name.", + Required: true, + }, + "subnet_name": schema.StringAttribute{ + MarkdownDescription: "The subnet name.", + Required: true, + }, + "resource_group_name": schema.StringAttribute{ + MarkdownDescription: "The resource group name.", + Required: true, + }, + "subscription_id": schema.StringAttribute{ + MarkdownDescription: "The subscription ID.", + Required: true, + CustomType: customtypes.UUIDType{}, + }, + }, + }, + }, + "timeouts": timeouts.AttributesAll(ctx), + }, + } +} + +func (r *resourceGateway) 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 = fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewGatewaysClient() +} + +func (r *resourceGateway) 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, state resourceGatewayModel + + 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() + + state.Timeouts = plan.Timeouts + + var reqCreate requestCreateGateway + + reqCreate.set(ctx, plan) + + respCreate, err := r.client.CreateGateway(ctx, reqCreate.CreateGatewayRequestClassification, nil) + if resp.Diagnostics.Append(utils.GetDiagsFromError(ctx, err, utils.OperationCreate, nil)...); resp.Diagnostics.HasError() { + return + } + + state.set(ctx, respCreate.GatewayClassification) + + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + + tflog.Debug(ctx, "CREATE", map[string]any{ + "action": "end", + }) + + if resp.Diagnostics.HasError() { + return + } +} + +func (r *resourceGateway) 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, + }) + + var state resourceGatewayModel + + 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() + + err := r.get(ctx, &state) + if diags := utils.GetDiagsFromError(ctx, err, utils.OperationRead, fabcore.ErrCommon.EntityNotFound); diags.HasError() { + if utils.IsErrNotFound(state.ID.ValueString(), &diags, fabcore.ErrCommon.EntityNotFound) { + resp.State.RemoveResource(ctx) + } + + 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 *resourceGateway) 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 resourceGatewayModel + + 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 requestUpdateGateway + + reqUpdate.set(plan) + + respUpdate, err := r.client.UpdateGateway(ctx, plan.ID.ValueString(), reqUpdate.UpdateGatewayRequestClassification, nil) + if resp.Diagnostics.Append(utils.GetDiagsFromError(ctx, err, utils.OperationUpdate, nil)...); resp.Diagnostics.HasError() { + return + } + + plan.set(ctx, respUpdate.GatewayClassification) + + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + + tflog.Debug(ctx, "UPDATE", map[string]any{ + "action": "end", + }) + + if resp.Diagnostics.HasError() { + return + } +} + +func (r *resourceGateway) 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 resourceGatewayModel + + 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.DeleteGateway(ctx, 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 *resourceGateway) 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, +// }) + +// _, diags := customtypes.NewUUIDValueMust(req.ID) +// if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { +// return +// } + +// resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + +// tflog.Debug(ctx, "IMPORT", map[string]any{ +// "action": "end", +// }) + +// if resp.Diagnostics.HasError() { +// return +// } +// } + +func (r *resourceGateway) get(ctx context.Context, model *resourceGatewayModel) error { + tflog.Trace(ctx, "getting "+ItemName) + + respGet, err := r.client.GetGateway(ctx, model.ID.ValueString(), nil) + if err != nil { + return err + } + + model.set(ctx, respGet.GatewayClassification) + + return nil +} From 5959a1cdbf1997608e5a9d7214447cdbdcd1805f Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Sun, 2 Feb 2025 13:43:12 +0000 Subject: [PATCH 02/14] 1. support OnPremises and OnPremisesPersonal gateway types as data sources only. 2. Consume superint32validator 3. fix localization comment. --- internal/services/gateway/base.go | 2 +- internal/services/gateway/data_gateway.go | 31 +++++++++ internal/services/gateway/models.go | 64 +++++++++++++++++-- internal/services/gateway/resource_gateway.go | 19 +++--- 4 files changed, 100 insertions(+), 16 deletions(-) diff --git a/internal/services/gateway/base.go b/internal/services/gateway/base.go index 1d4c87c8..4653b15b 100644 --- a/internal/services/gateway/base.go +++ b/internal/services/gateway/base.go @@ -13,7 +13,7 @@ const ( ItemsName = "Gateways" ItemsTFName = "gateways" ItemDocsSPNSupport = common.DocsSPNSupported - ItemDocsURL = "https://learn.microsoft.com/en-us/power-bi/guidance/powerbi-implementation-planning-data-gateways" + ItemDocsURL = "https://learn.microsoft.com/power-bi/guidance/powerbi-implementation-planning-data-gateways" ) var ( diff --git a/internal/services/gateway/data_gateway.go b/internal/services/gateway/data_gateway.go index c389441b..6b61cd70 100644 --- a/internal/services/gateway/data_gateway.go +++ b/internal/services/gateway/data_gateway.go @@ -99,6 +99,37 @@ func (d *dataSourceGateway) Schema(ctx context.Context, _ datasource.SchemaReque }, }, }, + "allow_cloud_connection_refresh": schema.BoolAttribute{ + MarkdownDescription: "Allow cloud connection refresh.", + Computed: true, + }, + "allow_custom_connectors": schema.BoolAttribute{ + MarkdownDescription: "Allow custom connectors.", + Computed: true, + }, + "load_balancing_setting": schema.StringAttribute{ + MarkdownDescription: "The load balancing setting. Possible values: " + utils.ConvertStringSlicesToString(fabcore.PossibleLoadBalancingSettingValues(), true, true), + Computed: true, + }, + "public_key": schema.SingleNestedAttribute{ + MarkdownDescription: "The public key of the primary gateway member. Used to encrypt the credentials for creating and updating connections.", + Computed: true, + CustomType: supertypes.NewSingleNestedObjectTypeOf[publicKeyModel](ctx), + Attributes: map[string]schema.Attribute{ + "exponent": schema.StringAttribute{ + MarkdownDescription: "The exponent.", + Computed: true, + }, + "modulus": schema.StringAttribute{ + MarkdownDescription: "The modulus.", + Computed: true, + }, + }, + }, + "version": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " version.", + Computed: true, + }, "timeouts": timeouts.Attributes(ctx), }, } diff --git a/internal/services/gateway/models.go b/internal/services/gateway/models.go index 74e54105..36d9eb53 100644 --- a/internal/services/gateway/models.go +++ b/internal/services/gateway/models.go @@ -19,11 +19,16 @@ type baseGatewayModel struct { ID customtypes.UUID `tfsdk:"id"` Type types.String `tfsdk:"type"` - DisplayName types.String `tfsdk:"display_name"` - CapacityID customtypes.UUID `tfsdk:"capacity_id"` - InactivityMinutesBeforeSleep types.Int32 `tfsdk:"inactivity_minutes_before_sleep"` - NumberOfMemberGateways types.Int32 `tfsdk:"number_of_member_gateways"` - VirtualNetworkAzureResource supertypes.SingleNestedObjectValueOf[virtualNetworkAzureResourceModel] `tfsdk:"virtual_network_azure_resource"` + DisplayName types.String `tfsdk:"display_name"` // VirtualNetwork & OnPremises + CapacityID customtypes.UUID `tfsdk:"capacity_id"` // VirtualNetwork + InactivityMinutesBeforeSleep types.Int32 `tfsdk:"inactivity_minutes_before_sleep"` // VirtualNetwork + NumberOfMemberGateways types.Int32 `tfsdk:"number_of_member_gateways"` // VirtualNetwork & OnPremises + VirtualNetworkAzureResource supertypes.SingleNestedObjectValueOf[virtualNetworkAzureResourceModel] `tfsdk:"virtual_network_azure_resource"` // VirtualNetwork + AllowCloudConnectionRefresh types.Bool `tfsdk:"allow_cloud_connection_refresh"` // OnPremises + AllowCustomConnectors types.Bool `tfsdk:"allow_custom_connectors"` // OnPremises + LoadBalancingSetting types.String `tfsdk:"load_balancing_setting"` // OnPremises + PublicKey supertypes.SingleNestedObjectValueOf[publicKeyModel] `tfsdk:"public_key"` // OnPremises & OnPremisesPersonal + Version types.String `tfsdk:"version"` // OnPremises & OnPremisesPersonal } func (to *baseGatewayModel) set(ctx context.Context, from fabcore.GatewayClassification) diag.Diagnostics { @@ -50,6 +55,45 @@ func (to *baseGatewayModel) set(ctx context.Context, from fabcore.GatewayClassif to.VirtualNetworkAzureResource = virtualNetworkAzureResource + case *fabcore.OnPremisesGateway: + to.ID = customtypes.NewUUIDPointerValue(gateway.ID) + to.Type = types.StringPointerValue((*string)(gateway.Type)) + to.DisplayName = types.StringPointerValue(gateway.DisplayName) + to.NumberOfMemberGateways = types.Int32PointerValue(gateway.NumberOfMemberGateways) + to.AllowCloudConnectionRefresh = types.BoolPointerValue(gateway.AllowCloudConnectionRefresh) + to.AllowCustomConnectors = types.BoolPointerValue(gateway.AllowCustomConnectors) + to.LoadBalancingSetting = types.StringPointerValue((*string)(gateway.LoadBalancingSetting)) + to.Version = types.StringPointerValue(gateway.Version) + + publicKey := supertypes.NewSingleNestedObjectValueOfNull[publicKeyModel](ctx) + if gateway.PublicKey != nil { + publicKeyModel := &publicKeyModel{} + publicKeyModel.set(gateway.PublicKey) + + if diags := publicKey.Set(ctx, publicKeyModel); diags.HasError() { + return diags + } + } + + to.PublicKey = publicKey + + case *fabcore.OnPremisesGatewayPersonal: + to.ID = customtypes.NewUUIDPointerValue(gateway.ID) + to.Type = types.StringPointerValue((*string)(gateway.Type)) + to.Version = types.StringPointerValue(gateway.Version) + + publicKey := supertypes.NewSingleNestedObjectValueOfNull[publicKeyModel](ctx) + if gateway.PublicKey != nil { + publicKeyModel := &publicKeyModel{} + publicKeyModel.set(gateway.PublicKey) + + if diags := publicKey.Set(ctx, publicKeyModel); diags.HasError() { + return diags + } + } + + to.PublicKey = publicKey + default: diags.AddError("Unsupported Gateway type", fmt.Sprintf("The Gateway type '%T' is not supported.", gateway)) @@ -72,3 +116,13 @@ func (to *virtualNetworkAzureResourceModel) set(from *fabcore.VirtualNetworkAzur to.SubscriptionID = customtypes.NewUUIDPointerValue(from.SubscriptionID) to.VirtualNetworkName = types.StringPointerValue(from.VirtualNetworkName) } + +type publicKeyModel struct { + Exponent types.String `tfsdk:"exponent"` + Modulus types.String `tfsdk:"modulus"` +} + +func (to *publicKeyModel) set(from *fabcore.PublicKey) { + to.Exponent = types.StringPointerValue(from.Exponent) + to.Modulus = types.StringPointerValue(from.Modulus) +} diff --git a/internal/services/gateway/resource_gateway.go b/internal/services/gateway/resource_gateway.go index 777fdac4..6ce94158 100644 --- a/internal/services/gateway/resource_gateway.go +++ b/internal/services/gateway/resource_gateway.go @@ -21,6 +21,7 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" + superint32validator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/int32validator" supersetvalidator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/setvalidator" superstringvalidator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/stringvalidator" @@ -105,11 +106,10 @@ func (r *resourceGateway) Schema(ctx context.Context, _ resource.SchemaRequest, Required: true, Validators: []validator.Int32{ int32validator.OneOf(PossibleInactivityMinutesBeforeSleepValues...), - // TODO: There is only int64validator in the framework, figure out how to utilize it easily. In the meantime, I set Required to true. - // superint64validator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), - // []attr.Value{ - // types.StringValue(string(fabcore.GatewayTypeVirtualNetwork)), - // }), + superint32validator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), + []attr.Value{ + types.StringValue(string(fabcore.GatewayTypeVirtualNetwork)), + }), }, }, "number_of_member_gateways": schema.Int32Attribute{ @@ -117,11 +117,10 @@ func (r *resourceGateway) Schema(ctx context.Context, _ resource.SchemaRequest, Required: true, Validators: []validator.Int32{ int32validator.Between(MinNumberOfMemberGatewaysValues, MaxNumberOfMemberGatewaysValues), - // TODO: There is only int64validator in the framework, figure out how to utilize it easily. In the meantime, I set Required to true. - // superint64validator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), - // []attr.Value{ - // types.StringValue(string(fabcore.GatewayTypeVirtualNetwork)), - // }), + superint32validator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), + []attr.Value{ + types.StringValue(string(fabcore.GatewayTypeVirtualNetwork)), + }), }, }, "virtual_network_azure_resource": schema.SetNestedAttribute{ From 2d87bb68973d58f983fb5a8aec5e79ea9295f077 Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Sun, 2 Feb 2025 13:47:25 +0000 Subject: [PATCH 03/14] add docs --- docs/data-sources/gateway.md | 67 +++++++++++++++++++++++++++++++++++ docs/resources/gateway.md | 59 ++++++++++++++++++++++++++++++ internal/provider/provider.go | 1 + 3 files changed, 127 insertions(+) create mode 100644 docs/data-sources/gateway.md create mode 100644 docs/resources/gateway.md diff --git a/docs/data-sources/gateway.md b/docs/data-sources/gateway.md new file mode 100644 index 00000000..215d9639 --- /dev/null +++ b/docs/data-sources/gateway.md @@ -0,0 +1,67 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "fabric_gateway Data Source - terraform-provider-fabric" +subcategory: "" +description: |- + Get a Fabric Gateway. + Use this data source to get Gateway https://learn.microsoft.com/power-bi/guidance/powerbi-implementation-planning-data-gateways. + -> This item supports Service Principal authentication. +--- + +# fabric_gateway (Data Source) + +Get a Fabric Gateway. + +Use this data source to get [Gateway](https://learn.microsoft.com/power-bi/guidance/powerbi-implementation-planning-data-gateways). + +-> This item supports Service Principal authentication. + + +## Schema + +### Optional + +- `display_name` (String) The Gateway display name. +- `id` (String) The Gateway ID. +- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) + +### Read-Only + +- `allow_cloud_connection_refresh` (Boolean) Allow cloud connection refresh. +- `allow_custom_connectors` (Boolean) Allow custom connectors. +- `capacity_id` (String) The Gateway capacity ID. +- `inactivity_minutes_before_sleep` (Number) The Gateway inactivity minutes before sleep. Possible values: `120`, `1440`, `150`, `240`, `30`, `360`, `480`, `60`, `720`, `90` +- `load_balancing_setting` (String) The load balancing setting. Possible values: `DistributeEvenly`, `Failover` +- `number_of_member_gateways` (Number) The number of member gateways. Possible values: 1 to 7. +- `public_key` (Attributes) The public key of the primary gateway member. Used to encrypt the credentials for creating and updating connections. (see [below for nested schema](#nestedatt--public_key)) +- `type` (String) The Gateway type. Possible values: `OnPremises`, `OnPremisesPersonal`, `VirtualNetwork` +- `version` (String) The Gateway version. +- `virtual_network_azure_resource` (Attributes) The Azure virtual network resource. (see [below for nested schema](#nestedatt--virtual_network_azure_resource)) + + + +### Nested Schema for `timeouts` + +Optional: + +- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). + + + +### Nested Schema for `public_key` + +Read-Only: + +- `exponent` (String) The exponent. +- `modulus` (String) The modulus. + + + +### Nested Schema for `virtual_network_azure_resource` + +Read-Only: + +- `resource_group_name` (String) The resource group name. +- `subnet_name` (String) The subnet name. +- `subscription_id` (String) The subscription ID. +- `virtual_network_name` (String) The virtual network name. diff --git a/docs/resources/gateway.md b/docs/resources/gateway.md new file mode 100644 index 00000000..060c47c6 --- /dev/null +++ b/docs/resources/gateway.md @@ -0,0 +1,59 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "fabric_gateway Resource - terraform-provider-fabric" +subcategory: "" +description: |- + This resource manages a Fabric Gateway. + See Gateways https://learn.microsoft.com/power-bi/guidance/powerbi-implementation-planning-data-gateways for more information. + -> This item supports Service Principal authentication. +--- + +# fabric_gateway (Resource) + +This resource manages a Fabric Gateway. + +See [Gateways](https://learn.microsoft.com/power-bi/guidance/powerbi-implementation-planning-data-gateways) for more information. + +-> This item supports Service Principal authentication. + + +## Schema + +### Required + +- `inactivity_minutes_before_sleep` (Number) The Gateway inactivity minutes before sleep. +- `number_of_member_gateways` (Number) The Gateway number of member gateways. +- `type` (String) The Gateway type. Accepted values: `OnPremises`, `OnPremisesPersonal`, `VirtualNetwork` + +### Optional + +- `capacity_id` (String) The Gateway capacity ID. +- `display_name` (String) The Gateway display name. +- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) +- `virtual_network_azure_resource` (Attributes Set) The Gateway virtual network Azure resource. (see [below for nested schema](#nestedatt--virtual_network_azure_resource)) + +### Read-Only + +- `id` (String) The Gateway ID. + + + +### Nested Schema for `timeouts` + +Optional: + +- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). +- `delete` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Setting a timeout for a Delete operation is only applicable if changes are saved into state before the destroy operation occurs. +- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled. +- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). + + + +### Nested Schema for `virtual_network_azure_resource` + +Required: + +- `resource_group_name` (String) The resource group name. +- `subnet_name` (String) The subnet name. +- `subscription_id` (String) The subscription ID. +- `virtual_network_name` (String) The virtual network name. diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 2d2ab3d8..4cca5452 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -369,6 +369,7 @@ func (p *FabricProvider) Resources(ctx context.Context) []func() resource.Resour func() resource.Resource { return environment.NewResourceEnvironment(ctx) }, func() resource.Resource { return eventhouse.NewResourceEventhouse(ctx) }, eventstream.NewResourceEventstream, + gateway.NewResourceGateway, kqldashboard.NewResourceKQLDashboard, kqldatabase.NewResourceKQLDatabase, kqlqueryset.NewResourceKQLQueryset, From f7ce0e6d7da4e65cbaaefb85c3e85b592d7b9a5f Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Sun, 2 Feb 2025 16:14:38 +0000 Subject: [PATCH 04/14] 1. change get method from returning error to diag.Diagnostics 2. add //nolint:gochecknoglobals --- internal/services/gateway/base.go | 6 ++--- internal/services/gateway/data_gateway.go | 4 ++- .../services/gateway/models_data_gateways.go | 4 ++- internal/services/gateway/resource_gateway.go | 27 ++++++++++--------- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/internal/services/gateway/base.go b/internal/services/gateway/base.go index 4653b15b..977ce337 100644 --- a/internal/services/gateway/base.go +++ b/internal/services/gateway/base.go @@ -17,7 +17,7 @@ const ( ) var ( - PossibleInactivityMinutesBeforeSleepValues = []int32{30, 60, 90, 120, 150, 240, 360, 480, 720, 1440} - MinNumberOfMemberGatewaysValues = int32(1) - MaxNumberOfMemberGatewaysValues = int32(7) + PossibleInactivityMinutesBeforeSleepValues = []int32{30, 60, 90, 120, 150, 240, 360, 480, 720, 1440} //nolint:gochecknoglobals + MinNumberOfMemberGatewaysValues = int32(1) //nolint:gochecknoglobals + MaxNumberOfMemberGatewaysValues = int32(7) //nolint:gochecknoglobals ) diff --git a/internal/services/gateway/data_gateway.go b/internal/services/gateway/data_gateway.go index 6b61cd70..a915bcc8 100644 --- a/internal/services/gateway/data_gateway.go +++ b/internal/services/gateway/data_gateway.go @@ -214,7 +214,9 @@ func (d *dataSourceGateway) get(ctx context.Context, model *dataSourceGatewayMod return diags } - model.set(ctx, respGet.GatewayClassification) + if diags := model.set(ctx, respGet.GatewayClassification); diags.HasError() { + return diags + } return nil } diff --git a/internal/services/gateway/models_data_gateways.go b/internal/services/gateway/models_data_gateways.go index 8e9307cf..b8ebe885 100644 --- a/internal/services/gateway/models_data_gateways.go +++ b/internal/services/gateway/models_data_gateways.go @@ -22,7 +22,9 @@ func (to *dataSourceGatewaysModel) setValues(ctx context.Context, from []fabcore for _, entity := range from { var entityModel baseGatewayModel - entityModel.set(ctx, entity) + if diags := entityModel.set(ctx, entity); diags.HasError() { + return diags + } slice = append(slice, &entityModel) } diff --git a/internal/services/gateway/resource_gateway.go b/internal/services/gateway/resource_gateway.go index 6ce94158..0d6f01e0 100644 --- a/internal/services/gateway/resource_gateway.go +++ b/internal/services/gateway/resource_gateway.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/int32validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" + "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" @@ -216,7 +217,9 @@ func (r *resourceGateway) Create(ctx context.Context, req resource.CreateRequest return } - state.set(ctx, respCreate.GatewayClassification) + if resp.Diagnostics.Append(state.set(ctx, respCreate.GatewayClassification)...); resp.Diagnostics.HasError() { + return + } resp.Diagnostics.Append(resp.State.Set(ctx, state)...) @@ -251,11 +254,9 @@ func (r *resourceGateway) Read(ctx context.Context, req resource.ReadRequest, re ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() - err := r.get(ctx, &state) - if diags := utils.GetDiagsFromError(ctx, err, utils.OperationRead, fabcore.ErrCommon.EntityNotFound); diags.HasError() { - if utils.IsErrNotFound(state.ID.ValueString(), &diags, fabcore.ErrCommon.EntityNotFound) { - resp.State.RemoveResource(ctx) - } + diags = r.get(ctx, &state) + if utils.IsErrNotFound(state.ID.ValueString(), &diags, fabcore.ErrCommon.EntityNotFound) { + resp.State.RemoveResource(ctx) resp.Diagnostics.Append(diags...) @@ -306,7 +307,9 @@ func (r *resourceGateway) Update(ctx context.Context, req resource.UpdateRequest return } - plan.set(ctx, respUpdate.GatewayClassification) + if resp.Diagnostics.Append(plan.set(ctx, respUpdate.GatewayClassification)...); resp.Diagnostics.HasError() { + return + } resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) @@ -375,15 +378,13 @@ func (r *resourceGateway) Delete(ctx context.Context, req resource.DeleteRequest // } // } -func (r *resourceGateway) get(ctx context.Context, model *resourceGatewayModel) error { +func (r *resourceGateway) get(ctx context.Context, model *resourceGatewayModel) diag.Diagnostics { tflog.Trace(ctx, "getting "+ItemName) respGet, err := r.client.GetGateway(ctx, model.ID.ValueString(), nil) - if err != nil { - return err + if diags := utils.GetDiagsFromError(ctx, err, utils.OperationRead, nil); diags.HasError() { + return diags } - model.set(ctx, respGet.GatewayClassification) - - return nil + return model.set(ctx, respGet.GatewayClassification) } From 577dab42836ed8aea0e50aa85a02d990abfc1ff8 Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Tue, 11 Feb 2025 13:42:02 +0000 Subject: [PATCH 05/14] add unit tests phase 1 --- internal/provider/provider.go | 3 + internal/services/gateway/base.go | 17 +- internal/services/gateway/base_test.go | 15 + internal/services/gateway/data_gateway.go | 36 +- .../gateway/data_gateway_role_assignments.go | 171 +++++ .../data_gateway_role_assignments_test.go | 80 +++ .../services/gateway/data_gateway_test.go | 186 +++++ internal/services/gateway/data_gateways.go | 35 +- .../services/gateway/data_gateways_test.go | 73 ++ internal/services/gateway/fake_test.go | 70 ++ internal/services/gateway/models.go | 103 --- .../services/gateway/models_data_gateway.go | 109 ++- .../models_data_gateway_role_assignment.go | 88 +++ .../services/gateway/models_data_gateways.go | 8 +- .../gateway/models_resource_gateway.go | 49 +- ...models_resource_gateway_role_assignment.go | 45 ++ internal/services/gateway/resource_gateway.go | 109 +-- .../resource_gateway_role_assignment.go | 351 ++++++++++ .../services/gateway/resource_gateway_test.go | 663 ++++++++++++++++++ .../data_workspace_role_assignments.go | 2 +- .../resource_workspace_role_assignment.go | 2 +- internal/testhelp/fakes/fabric_gateway.go | 203 ++++++ internal/testhelp/fakes/fake_server.go | 17 +- internal/testhelp/fakes/fake_typedhandler.go | 6 + internal/testhelp/utils.go | 13 + 25 files changed, 2259 insertions(+), 195 deletions(-) create mode 100644 internal/services/gateway/base_test.go create mode 100644 internal/services/gateway/data_gateway_role_assignments.go create mode 100644 internal/services/gateway/data_gateway_role_assignments_test.go create mode 100644 internal/services/gateway/data_gateway_test.go create mode 100644 internal/services/gateway/data_gateways_test.go create mode 100644 internal/services/gateway/fake_test.go create mode 100644 internal/services/gateway/models_data_gateway_role_assignment.go create mode 100644 internal/services/gateway/models_resource_gateway_role_assignment.go create mode 100644 internal/services/gateway/resource_gateway_role_assignment.go create mode 100644 internal/services/gateway/resource_gateway_test.go create mode 100644 internal/testhelp/fakes/fabric_gateway.go diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 4cca5452..3bb902d8 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -370,6 +370,7 @@ func (p *FabricProvider) Resources(ctx context.Context) []func() resource.Resour func() resource.Resource { return eventhouse.NewResourceEventhouse(ctx) }, eventstream.NewResourceEventstream, gateway.NewResourceGateway, + gateway.NewResourceGatewayRoleAssignment, kqldashboard.NewResourceKQLDashboard, kqldatabase.NewResourceKQLDatabase, kqlqueryset.NewResourceKQLQueryset, @@ -408,6 +409,8 @@ func (p *FabricProvider) DataSources(ctx context.Context) []func() datasource.Da eventstream.NewDataSourceEventstream, eventstream.NewDataSourceEventstreams, gateway.NewDataSourceGateway, + gateway.NewDataSourceGateways, + gateway.NewDataSourceGatewayRoleAssignments, kqldashboard.NewDataSourceKQLDashboard, kqldashboard.NewDataSourceKQLDashboards, kqldatabase.NewDataSourceKQLDatabase, diff --git a/internal/services/gateway/base.go b/internal/services/gateway/base.go index 977ce337..73750c23 100644 --- a/internal/services/gateway/base.go +++ b/internal/services/gateway/base.go @@ -8,12 +8,17 @@ import ( ) const ( - ItemName = "Gateway" - ItemTFName = "gateway" - ItemsName = "Gateways" - ItemsTFName = "gateways" - ItemDocsSPNSupport = common.DocsSPNSupported - ItemDocsURL = "https://learn.microsoft.com/power-bi/guidance/powerbi-implementation-planning-data-gateways" + ItemName = "Gateway" + ItemTFName = "gateway" + ItemsName = "Gateways" + ItemsTFName = "gateways" + ItemDocsSPNSupport = common.DocsSPNSupported + ItemDocsURL = "https://learn.microsoft.com/power-bi/guidance/powerbi-implementation-planning-data-gateways" + GatewayRoleAssignmentName = "Gateway Role Assignment" + GatewayRoleAssignmentTFName = "gateway_role_assignment" + GatewayRoleAssignmentsName = "Gateway Role Assignments" + GatewayRoleAssignmentsTFName = "gateway_role_assignments" + ItemPreview = true ) var ( diff --git a/internal/services/gateway/base_test.go b/internal/services/gateway/base_test.go new file mode 100644 index 00000000..5b135de1 --- /dev/null +++ b/internal/services/gateway/base_test.go @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway_test + +import ( + "github.com/microsoft/terraform-provider-fabric/internal/services/gateway" +) + +const ( + itemTFName = gateway.ItemTFName + itemsTFName = gateway.ItemsTFName + gatewayRoleAssignmentTFName = gateway.GatewayRoleAssignmentTFName + gatewayRoleAssignmentsTFName = gateway.GatewayRoleAssignmentsTFName +) diff --git a/internal/services/gateway/data_gateway.go b/internal/services/gateway/data_gateway.go index a915bcc8..bbccf23c 100644 --- a/internal/services/gateway/data_gateway.go +++ b/internal/services/gateway/data_gateway.go @@ -8,11 +8,9 @@ import ( "fmt" "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" - "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" fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" @@ -23,10 +21,8 @@ import ( pconfig "github.com/microsoft/terraform-provider-fabric/internal/provider/config" ) -var ( - _ datasource.DataSourceWithConfigValidators = (*dataSourceGateway)(nil) - _ datasource.DataSourceWithConfigure = (*dataSourceGateway)(nil) -) +// _ datasource.DataSourceWithConfigValidators = (*dataSourceGateway)(nil) +var _ datasource.DataSourceWithConfigure = (*dataSourceGateway)(nil) type dataSourceGateway struct { pConfigData *pconfig.ProviderData @@ -49,13 +45,11 @@ func (d *dataSourceGateway) Schema(ctx context.Context, _ datasource.SchemaReque Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ MarkdownDescription: "The " + ItemName + " ID.", - Optional: true, - Computed: true, + Required: true, CustomType: customtypes.UUIDType{}, }, "display_name": schema.StringAttribute{ MarkdownDescription: "The " + ItemName + " display name.", - Optional: true, Computed: true, }, "type": schema.StringAttribute{ @@ -135,18 +129,18 @@ func (d *dataSourceGateway) Schema(ctx context.Context, _ datasource.SchemaReque } } -func (d *dataSourceGateway) 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 *dataSourceGateway) 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 *dataSourceGateway) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { if req.ProviderData == nil { diff --git a/internal/services/gateway/data_gateway_role_assignments.go b/internal/services/gateway/data_gateway_role_assignments.go new file mode 100644 index 00000000..1f69c251 --- /dev/null +++ b/internal/services/gateway/data_gateway_role_assignments.go @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" + "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-log/tflog" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" + + "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" +) + +var _ datasource.DataSourceWithConfigure = (*dataSourceGatewayRoleAssignments)(nil) + +type dataSourceGatewayRoleAssignments struct { + pConfigData *pconfig.ProviderData + client *fabcore.GatewaysClient +} + +func NewDataSourceGatewayRoleAssignments() datasource.DataSource { + return &dataSourceGatewayRoleAssignments{} +} + +func (d *dataSourceGatewayRoleAssignments) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_" + GatewayRoleAssignmentsTFName +} + +func (d *dataSourceGatewayRoleAssignments) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "List Fabric " + GatewayRoleAssignmentsName + ".\n\n" + + "Use this data source to list [" + GatewayRoleAssignmentsName + "].\n\n" + + ItemDocsSPNSupport, + Attributes: map[string]schema.Attribute{ + "gateway_id": schema.StringAttribute{ + MarkdownDescription: "The Gateway ID.", + Required: true, + CustomType: customtypes.UUIDType{}, + }, + "values": schema.ListNestedAttribute{ + MarkdownDescription: "The list of " + GatewayRoleAssignmentsName + ".", + Computed: true, + CustomType: supertypes.NewListNestedObjectTypeOf[gatewayRoleAssignmentModel](ctx), + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "The Principal ID.", + Computed: true, + CustomType: customtypes.UUIDType{}, + }, + "role": schema.StringAttribute{ + MarkdownDescription: "The gateway role of the principal. Possible values: " + utils.ConvertStringSlicesToString(fabcore.PossibleGatewayRoleValues(), true, true) + ".", + Computed: true, + }, + "display_name": schema.StringAttribute{ + MarkdownDescription: "The principal's display name.", + Computed: true, + }, + "type": schema.StringAttribute{ + MarkdownDescription: "The type of the principal. Possible values: " + utils.ConvertStringSlicesToString(fabcore.PossiblePrincipalTypeValues(), true, true) + ".", + Computed: true, + }, + "details": schema.SingleNestedAttribute{ + MarkdownDescription: "The principal details.", + Computed: true, + CustomType: supertypes.NewSingleNestedObjectTypeOf[principalDetailsModel](ctx), + Attributes: map[string]schema.Attribute{ + "user_principal_name": schema.StringAttribute{ + MarkdownDescription: "The user principal name.", + Computed: true, + }, + "group_type": schema.StringAttribute{ + MarkdownDescription: "The type of the group. Possible values: " + utils.ConvertStringSlicesToString(fabcore.PossibleGroupTypeValues(), true, true) + ".", + Computed: true, + }, + "app_id": schema.StringAttribute{ + MarkdownDescription: "The service principal's Microsoft Entra App ID.", + Computed: true, + CustomType: customtypes.UUIDType{}, + }, + "parent_principal_id": schema.StringAttribute{ + MarkdownDescription: "The parent principal ID of Service Principal Profile.", + Computed: true, + CustomType: customtypes.UUIDType{}, + }, + }, + }, + }, + }, + }, + "timeouts": timeouts.Attributes(ctx), + }, + } +} + +func (d *dataSourceGatewayRoleAssignments) 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 = fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewGatewaysClient() +} + +func (d *dataSourceGatewayRoleAssignments) 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 dataSourceGatewayRoleAssignmentsModel + + 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 resp.Diagnostics.Append(d.list(ctx, &data)...); 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 *dataSourceGatewayRoleAssignments) list(ctx context.Context, model *dataSourceGatewayRoleAssignmentsModel) diag.Diagnostics { + tflog.Trace(ctx, "getting "+GatewayRoleAssignmentsName) + + respList, err := d.client.ListGatewayRoleAssignments(ctx, model.GatewayID.ValueString(), nil) + if diags := utils.GetDiagsFromError(ctx, err, utils.OperationList, nil); diags.HasError() { + return diags + } + + return model.setValues(ctx, respList) +} diff --git a/internal/services/gateway/data_gateway_role_assignments_test.go b/internal/services/gateway/data_gateway_role_assignments_test.go new file mode 100644 index 00000000..31e85b13 --- /dev/null +++ b/internal/services/gateway/data_gateway_role_assignments_test.go @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway_test + +import ( + "regexp" + "testing" + + at "github.com/dcarbone/terraform-plugin-framework-utils/v3/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + "github.com/microsoft/terraform-provider-fabric/internal/testhelp" + "github.com/microsoft/terraform-provider-fabric/internal/testhelp/fakes" +) + +var ( + testDataSourceGatewayRoleAssignments = testhelp.DataSourceFQN("fabric", gatewayRoleAssignmentsTFName, "test") + testDataSourceGatewayRoleAssignmentsHeader = at.DataSourceHeader(testhelp.TypeName("fabric", gatewayRoleAssignmentsTFName), "test") +) + +func TestUnit_GatewayRoleAssignmentsDataSource(t *testing.T) { + gatewayID := testhelp.RandomUUID() + gatewayRoleAssignments := NewRandomGatewayRoleAssignments() + fakes.FakeServer.ServerFactory.Core.GatewaysServer.NewListGatewayRoleAssignmentsPager = fakeGatewayRoleAssignments(gatewayRoleAssignments) + + entity := gatewayRoleAssignments.Value[1] + + resource.ParallelTest(t, testhelp.NewTestUnitCase(t, nil, fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ + // error - unexpected_attr + { + Config: at.CompileConfig( + testDataSourceGatewayRoleAssignmentsHeader, + map[string]any{ + "gateway_id": gatewayID, + "unexpected_attr": "test", + }, + ), + ExpectError: regexp.MustCompile(`An argument named "unexpected_attr" is not expected here`), + }, + // read + { + Config: at.CompileConfig( + testDataSourceGatewayRoleAssignmentsHeader, + map[string]any{ + "gateway_id": gatewayID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testDataSourceGatewayRoleAssignments, "gateway_id", gatewayID), + resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.id", entity.ID), + resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.role", (*string)(entity.Role)), + resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.display_name", entity.Principal.DisplayName), + resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.type", (*string)(entity.Principal.Type)), + ), + }, + })) +} + +// func TestAcc_GatewayRoleAssignmentsDataSource(t *testing.T) { +// gateway := testhelp.WellKnown()["GatewayDS"].(map[string]any) +// gatewayID := gateway["id"].(string) + +// resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ +// // read +// { +// Config: at.CompileConfig( +// testDataSourceGatewayRoleAssignmentsHeader, +// map[string]any{ +// "gateway_id": gatewayID, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testDataSourceGatewayRoleAssignments, "gateway_id", gatewayID), +// resource.TestCheckResourceAttrSet(testDataSourceGatewayRoleAssignments, "values.0.id"), +// ), +// }, +// }, +// )) +// } diff --git a/internal/services/gateway/data_gateway_test.go b/internal/services/gateway/data_gateway_test.go new file mode 100644 index 00000000..002cab11 --- /dev/null +++ b/internal/services/gateway/data_gateway_test.go @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway_test + +import ( + "regexp" + "strconv" + "testing" + + at "github.com/dcarbone/terraform-plugin-framework-utils/v3/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + "github.com/microsoft/terraform-provider-fabric/internal/common" + "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" + "github.com/microsoft/terraform-provider-fabric/internal/testhelp" + "github.com/microsoft/terraform-provider-fabric/internal/testhelp/fakes" +) + +var ( + testDataSourceItemFQN = testhelp.DataSourceFQN("fabric", itemTFName, "test") + testDataSourceItemHeader = at.DataSourceHeader(testhelp.TypeName("fabric", itemTFName), "test") +) + +func TestUnit_GatewayDataSource(t *testing.T) { + virtualNetworkGateway := fakes.NewRandomVirtualNetworkGateway() + onPremisesGateway := fakes.NewRandomOnPremisesGateway() + onPremisesGatewayPersonalGateway := fakes.NewRandomOnPremisesGatewayPersonal() + + fakes.FakeServer.Upsert(fakes.NewRandomGateway()) + fakes.FakeServer.Upsert(virtualNetworkGateway) + fakes.FakeServer.Upsert(onPremisesGateway) + fakes.FakeServer.Upsert(onPremisesGatewayPersonalGateway) + fakes.FakeServer.Upsert(fakes.NewRandomGateway()) + + resource.ParallelTest(t, testhelp.NewTestUnitCase(t, nil, fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ + // error - no attributes + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{}, + ), + ExpectError: regexp.MustCompile(`Missing required argument`), + }, + // error - id - invalid UUID + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "id": "invalid uuid", + }, + ), + ExpectError: regexp.MustCompile(customtypes.UUIDTypeErrorInvalidStringHeader), + }, + // error - unexpected attribute + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "id": *virtualNetworkGateway.ID, + "unexpected_attr": "test", + }, + ), + ExpectError: regexp.MustCompile(`An argument named "unexpected_attr" is not expected here`), + }, + // read by id + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "id": *virtualNetworkGateway.ID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "id", virtualNetworkGateway.ID), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "type", (*string)(virtualNetworkGateway.Type)), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "display_name", virtualNetworkGateway.DisplayName), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "capacity_id", virtualNetworkGateway.CapacityID), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(*virtualNetworkGateway.InactivityMinutesBeforeSleep))), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(*virtualNetworkGateway.NumberOfMemberGateways))), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.resource_group_name", virtualNetworkGateway.VirtualNetworkAzureResource.ResourceGroupName), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.subnet_name", virtualNetworkGateway.VirtualNetworkAzureResource.SubnetName), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.subscription_id", virtualNetworkGateway.VirtualNetworkAzureResource.SubscriptionID), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.virtual_network_name", virtualNetworkGateway.VirtualNetworkAzureResource.VirtualNetworkName), + ), + }, + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "id": *onPremisesGateway.ID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "id", onPremisesGateway.ID), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "type", (*string)(onPremisesGateway.Type)), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "display_name", onPremisesGateway.DisplayName), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "allow_cloud_connection_refresh", strconv.FormatBool(*onPremisesGateway.AllowCloudConnectionRefresh)), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "allow_custom_connectors", strconv.FormatBool(*onPremisesGateway.AllowCustomConnectors)), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(*onPremisesGateway.NumberOfMemberGateways))), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "load_balancing_setting", string(*onPremisesGateway.LoadBalancingSetting)), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "version", onPremisesGateway.Version), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "public_key.exponent", onPremisesGateway.PublicKey.Exponent), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "public_key.modulus", onPremisesGateway.PublicKey.Modulus), + ), + }, + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "id": *onPremisesGatewayPersonalGateway.ID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "id", onPremisesGatewayPersonalGateway.ID), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "type", (*string)(onPremisesGatewayPersonalGateway.Type)), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "version", onPremisesGatewayPersonalGateway.Version), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "public_key.exponent", onPremisesGatewayPersonalGateway.PublicKey.Exponent), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "public_key.modulus", onPremisesGatewayPersonalGateway.PublicKey.Modulus), + ), + }, + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "id": *virtualNetworkGateway.ID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "id", virtualNetworkGateway.ID), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "type", (*string)(virtualNetworkGateway.Type)), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "display_name", virtualNetworkGateway.DisplayName), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "capacity_id", virtualNetworkGateway.CapacityID), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(*virtualNetworkGateway.InactivityMinutesBeforeSleep))), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(*virtualNetworkGateway.NumberOfMemberGateways))), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.resource_group_name", virtualNetworkGateway.VirtualNetworkAzureResource.ResourceGroupName), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.subnet_name", virtualNetworkGateway.VirtualNetworkAzureResource.SubnetName), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.subscription_id", virtualNetworkGateway.VirtualNetworkAzureResource.SubscriptionID), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.virtual_network_name", virtualNetworkGateway.VirtualNetworkAzureResource.VirtualNetworkName), + ), + }, + // read by id - not found + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "id": testhelp.RandomUUID(), + }, + ), + ExpectError: regexp.MustCompile(common.ErrorReadHeader), + }, + })) +} + +// func TestAcc_GatewayDataSource(t *testing.T) { +// entity := testhelp.WellKnown()["GatewayParent"].(map[string]any) +// entityID := entity["id"].(string) +// entityDisplayName := entity["displayName"].(string) +// entityDescription := entity["description"].(string) + +// resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ +// { +// Config: at.CompileConfig( +// testDataSourceItemHeader, +// map[string]any{ +// "id": entityID, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityID), +// resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityDisplayName), +// resource.TestCheckResourceAttr(testDataSourceItemFQN, "description", entityDescription), +// ), +// }, +// // read by id - not found +// { +// Config: at.CompileConfig( +// testDataSourceItemHeader, +// map[string]any{ +// "id": testhelp.RandomUUID(), +// }, +// ), +// ExpectError: regexp.MustCompile(common.ErrorReadHeader), +// }, +// })) +// } diff --git a/internal/services/gateway/data_gateways.go b/internal/services/gateway/data_gateways.go index 103d9856..04e2136b 100644 --- a/internal/services/gateway/data_gateways.go +++ b/internal/services/gateway/data_gateways.go @@ -45,12 +45,12 @@ func (d *dataSourceGateways) Schema(ctx context.Context, _ datasource.SchemaRequ "values": schema.ListNestedAttribute{ MarkdownDescription: "The list of " + ItemsName + ".", Computed: true, - CustomType: supertypes.NewListNestedObjectTypeOf[baseGatewayModel](ctx), + CustomType: supertypes.NewListNestedObjectTypeOf[baseDataSourceGatewayModel](ctx), NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ MarkdownDescription: "The " + ItemName + " ID.", - Computed: true, + Required: true, CustomType: customtypes.UUIDType{}, }, "display_name": schema.StringAttribute{ @@ -98,6 +98,37 @@ func (d *dataSourceGateways) Schema(ctx context.Context, _ datasource.SchemaRequ }, }, }, + "allow_cloud_connection_refresh": schema.BoolAttribute{ + MarkdownDescription: "Allow cloud connection refresh.", + Computed: true, + }, + "allow_custom_connectors": schema.BoolAttribute{ + MarkdownDescription: "Allow custom connectors.", + Computed: true, + }, + "load_balancing_setting": schema.StringAttribute{ + MarkdownDescription: "The load balancing setting. Possible values: " + utils.ConvertStringSlicesToString(fabcore.PossibleLoadBalancingSettingValues(), true, true), + Computed: true, + }, + "public_key": schema.SingleNestedAttribute{ + MarkdownDescription: "The public key of the primary gateway member. Used to encrypt the credentials for creating and updating connections.", + Computed: true, + CustomType: supertypes.NewSingleNestedObjectTypeOf[publicKeyModel](ctx), + Attributes: map[string]schema.Attribute{ + "exponent": schema.StringAttribute{ + MarkdownDescription: "The exponent.", + Computed: true, + }, + "modulus": schema.StringAttribute{ + MarkdownDescription: "The modulus.", + Computed: true, + }, + }, + }, + "version": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " version.", + Computed: true, + }, }, }, }, diff --git a/internal/services/gateway/data_gateways_test.go b/internal/services/gateway/data_gateways_test.go new file mode 100644 index 00000000..49d3a24f --- /dev/null +++ b/internal/services/gateway/data_gateways_test.go @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway_test + +import ( + "regexp" + "testing" + + at "github.com/dcarbone/terraform-plugin-framework-utils/v3/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + "github.com/microsoft/terraform-provider-fabric/internal/testhelp" + "github.com/microsoft/terraform-provider-fabric/internal/testhelp/fakes" +) + +var ( + testDataSourceItemsFQN = testhelp.DataSourceFQN("fabric", itemsTFName, "test") + testDataSourceItemsHeader = at.DataSourceHeader(testhelp.TypeName("fabric", itemsTFName), "test") +) + +func TestUnit_GatewaysDataSource(t *testing.T) { + // virtualNetworkGateway := fakes.NewRandomVirtualNetworkGateway() + // onPremisesGateway := fakes.NewRandomOnPremisesGateway() + // onPremisesGatewayPersonalGateway := fakes.NewRandomOnPremisesGatewayPersonal() + + // fakes.FakeServer.Upsert(fakes.NewRandomGateway()) + // fakes.FakeServer.Upsert(virtualNetworkGateway) + // fakes.FakeServer.Upsert(onPremisesGateway) + // fakes.FakeServer.Upsert(onPremisesGatewayPersonalGateway) + // fakes.FakeServer.Upsert(fakes.NewRandomGateway()) + + resource.ParallelTest(t, testhelp.NewTestUnitCase(t, nil, fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ + // error - unexpected_attr + { + Config: at.CompileConfig( + testDataSourceItemsHeader, + map[string]any{ + "unexpected_attr": "test", + }, + ), + ExpectError: regexp.MustCompile(`An argument named "unexpected_attr" is not expected here`), + }, + // read + // { + // Config: at.CompileConfig( + // testDataSourceItemsHeader, + // map[string]any{}, + // ), + // Check: resource.ComposeAggregateTestCheckFunc( + // resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.0.id"), + // resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.2.id"), + // resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.3.id"), + // ), + // }, + })) +} + +// func TestAcc_GatewaysDataSource(t *testing.T) { +// resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ +// // read +// { +// Config: at.CompileConfig( +// testDataSourceItemsHeader, +// map[string]any{}, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.0.id"), +// ), +// }, +// }, +// )) +// } diff --git a/internal/services/gateway/fake_test.go b/internal/services/gateway/fake_test.go new file mode 100644 index 00000000..30705893 --- /dev/null +++ b/internal/services/gateway/fake_test.go @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway_test + +import ( + "net/http" + + azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + azto "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + + "github.com/microsoft/terraform-provider-fabric/internal/testhelp" +) + +func fakeGatewayRoleAssignments(exampleResp fabcore.GatewayRoleAssignments) func(gatewayID string, options *fabcore.GatewaysClientListGatewayRoleAssignmentsOptions) (resp azfake.PagerResponder[fabcore.GatewaysClientListGatewayRoleAssignmentsResponse]) { + return func(_ string, _ *fabcore.GatewaysClientListGatewayRoleAssignmentsOptions) (resp azfake.PagerResponder[fabcore.GatewaysClientListGatewayRoleAssignmentsResponse]) { + resp = azfake.PagerResponder[fabcore.GatewaysClientListGatewayRoleAssignmentsResponse]{} + resp.AddPage(http.StatusOK, fabcore.GatewaysClientListGatewayRoleAssignmentsResponse{GatewayRoleAssignments: exampleResp}, nil) + + return + } +} + +func NewRandomGatewayRoleAssignments() fabcore.GatewayRoleAssignments { + principal0ID := testhelp.RandomUUID() + principal1ID := testhelp.RandomUUID() + principal2ID := testhelp.RandomUUID() + + return fabcore.GatewayRoleAssignments{ + Value: []fabcore.GatewayRoleAssignment{ + { + ID: azto.Ptr(principal0ID), + Role: azto.Ptr(fabcore.GatewayRoleAdmin), + Principal: &fabcore.Principal{ + ID: azto.Ptr(principal0ID), + Type: azto.Ptr(fabcore.PrincipalTypeGroup), + DisplayName: azto.Ptr(testhelp.RandomName()), + GroupDetails: &fabcore.PrincipalGroupDetails{ + GroupType: azto.Ptr(fabcore.GroupTypeSecurityGroup), + }, + }, + }, + { + ID: azto.Ptr(principal1ID), + Role: azto.Ptr(fabcore.GatewayRoleConnectionCreator), + Principal: &fabcore.Principal{ + ID: azto.Ptr(principal1ID), + Type: azto.Ptr(fabcore.PrincipalTypeUser), + DisplayName: azto.Ptr(testhelp.RandomName()), + UserDetails: &fabcore.PrincipalUserDetails{ + UserPrincipalName: azto.Ptr(testhelp.RandomName()), + }, + }, + }, + { + ID: azto.Ptr(principal2ID), + Role: azto.Ptr(fabcore.GatewayRoleConnectionCreatorWithResharing), + Principal: &fabcore.Principal{ + ID: azto.Ptr(principal2ID), + Type: azto.Ptr(fabcore.PrincipalTypeServicePrincipal), + DisplayName: azto.Ptr(testhelp.RandomName()), + ServicePrincipalDetails: &fabcore.PrincipalServicePrincipalDetails{ + AADAppID: azto.Ptr(testhelp.RandomUUID()), + }, + }, + }, + }, + } +} diff --git a/internal/services/gateway/models.go b/internal/services/gateway/models.go index 36d9eb53..e19d6518 100644 --- a/internal/services/gateway/models.go +++ b/internal/services/gateway/models.go @@ -4,105 +4,12 @@ package gateway import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" - supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" ) -type baseGatewayModel struct { - ID customtypes.UUID `tfsdk:"id"` - Type types.String `tfsdk:"type"` - - DisplayName types.String `tfsdk:"display_name"` // VirtualNetwork & OnPremises - CapacityID customtypes.UUID `tfsdk:"capacity_id"` // VirtualNetwork - InactivityMinutesBeforeSleep types.Int32 `tfsdk:"inactivity_minutes_before_sleep"` // VirtualNetwork - NumberOfMemberGateways types.Int32 `tfsdk:"number_of_member_gateways"` // VirtualNetwork & OnPremises - VirtualNetworkAzureResource supertypes.SingleNestedObjectValueOf[virtualNetworkAzureResourceModel] `tfsdk:"virtual_network_azure_resource"` // VirtualNetwork - AllowCloudConnectionRefresh types.Bool `tfsdk:"allow_cloud_connection_refresh"` // OnPremises - AllowCustomConnectors types.Bool `tfsdk:"allow_custom_connectors"` // OnPremises - LoadBalancingSetting types.String `tfsdk:"load_balancing_setting"` // OnPremises - PublicKey supertypes.SingleNestedObjectValueOf[publicKeyModel] `tfsdk:"public_key"` // OnPremises & OnPremisesPersonal - Version types.String `tfsdk:"version"` // OnPremises & OnPremisesPersonal -} - -func (to *baseGatewayModel) set(ctx context.Context, from fabcore.GatewayClassification) diag.Diagnostics { - var diags diag.Diagnostics - - switch gateway := from.(type) { - case *fabcore.VirtualNetworkGateway: - to.ID = customtypes.NewUUIDPointerValue(gateway.ID) - to.Type = types.StringPointerValue((*string)(gateway.Type)) - to.DisplayName = types.StringPointerValue(gateway.DisplayName) - to.CapacityID = customtypes.NewUUIDPointerValue(gateway.CapacityID) - to.InactivityMinutesBeforeSleep = types.Int32PointerValue(gateway.InactivityMinutesBeforeSleep) - to.NumberOfMemberGateways = types.Int32PointerValue(gateway.NumberOfMemberGateways) - - virtualNetworkAzureResource := supertypes.NewSingleNestedObjectValueOfNull[virtualNetworkAzureResourceModel](ctx) - if gateway.VirtualNetworkAzureResource != nil { - virtualNetworkAzureResourceModel := &virtualNetworkAzureResourceModel{} - virtualNetworkAzureResourceModel.set(gateway.VirtualNetworkAzureResource) - - if diags := virtualNetworkAzureResource.Set(ctx, virtualNetworkAzureResourceModel); diags.HasError() { - return diags - } - } - - to.VirtualNetworkAzureResource = virtualNetworkAzureResource - - case *fabcore.OnPremisesGateway: - to.ID = customtypes.NewUUIDPointerValue(gateway.ID) - to.Type = types.StringPointerValue((*string)(gateway.Type)) - to.DisplayName = types.StringPointerValue(gateway.DisplayName) - to.NumberOfMemberGateways = types.Int32PointerValue(gateway.NumberOfMemberGateways) - to.AllowCloudConnectionRefresh = types.BoolPointerValue(gateway.AllowCloudConnectionRefresh) - to.AllowCustomConnectors = types.BoolPointerValue(gateway.AllowCustomConnectors) - to.LoadBalancingSetting = types.StringPointerValue((*string)(gateway.LoadBalancingSetting)) - to.Version = types.StringPointerValue(gateway.Version) - - publicKey := supertypes.NewSingleNestedObjectValueOfNull[publicKeyModel](ctx) - if gateway.PublicKey != nil { - publicKeyModel := &publicKeyModel{} - publicKeyModel.set(gateway.PublicKey) - - if diags := publicKey.Set(ctx, publicKeyModel); diags.HasError() { - return diags - } - } - - to.PublicKey = publicKey - - case *fabcore.OnPremisesGatewayPersonal: - to.ID = customtypes.NewUUIDPointerValue(gateway.ID) - to.Type = types.StringPointerValue((*string)(gateway.Type)) - to.Version = types.StringPointerValue(gateway.Version) - - publicKey := supertypes.NewSingleNestedObjectValueOfNull[publicKeyModel](ctx) - if gateway.PublicKey != nil { - publicKeyModel := &publicKeyModel{} - publicKeyModel.set(gateway.PublicKey) - - if diags := publicKey.Set(ctx, publicKeyModel); diags.HasError() { - return diags - } - } - - to.PublicKey = publicKey - - default: - diags.AddError("Unsupported Gateway type", fmt.Sprintf("The Gateway type '%T' is not supported.", gateway)) - - return diags - } - - return nil -} - type virtualNetworkAzureResourceModel struct { ResourceGroupName types.String `tfsdk:"resource_group_name"` SubnetName types.String `tfsdk:"subnet_name"` @@ -116,13 +23,3 @@ func (to *virtualNetworkAzureResourceModel) set(from *fabcore.VirtualNetworkAzur to.SubscriptionID = customtypes.NewUUIDPointerValue(from.SubscriptionID) to.VirtualNetworkName = types.StringPointerValue(from.VirtualNetworkName) } - -type publicKeyModel struct { - Exponent types.String `tfsdk:"exponent"` - Modulus types.String `tfsdk:"modulus"` -} - -func (to *publicKeyModel) set(from *fabcore.PublicKey) { - to.Exponent = types.StringPointerValue(from.Exponent) - to.Modulus = types.StringPointerValue(from.Modulus) -} diff --git a/internal/services/gateway/models_data_gateway.go b/internal/services/gateway/models_data_gateway.go index d7aebed6..ac9f9884 100644 --- a/internal/services/gateway/models_data_gateway.go +++ b/internal/services/gateway/models_data_gateway.go @@ -4,10 +4,117 @@ package gateway import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" + + "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" ) type dataSourceGatewayModel struct { - baseGatewayModel + baseDataSourceGatewayModel Timeouts timeouts.Value `tfsdk:"timeouts"` } + +type baseDataSourceGatewayModel struct { + ID customtypes.UUID `tfsdk:"id"` + Type types.String `tfsdk:"type"` + + DisplayName types.String `tfsdk:"display_name"` // VirtualNetwork & OnPremises + CapacityID customtypes.UUID `tfsdk:"capacity_id"` // VirtualNetwork + InactivityMinutesBeforeSleep types.Int32 `tfsdk:"inactivity_minutes_before_sleep"` // VirtualNetwork + NumberOfMemberGateways types.Int32 `tfsdk:"number_of_member_gateways"` // VirtualNetwork & OnPremises + VirtualNetworkAzureResource supertypes.SingleNestedObjectValueOf[virtualNetworkAzureResourceModel] `tfsdk:"virtual_network_azure_resource"` // VirtualNetwork + AllowCloudConnectionRefresh types.Bool `tfsdk:"allow_cloud_connection_refresh"` // OnPremises + AllowCustomConnectors types.Bool `tfsdk:"allow_custom_connectors"` // OnPremises + LoadBalancingSetting types.String `tfsdk:"load_balancing_setting"` // OnPremises + PublicKey supertypes.SingleNestedObjectValueOf[publicKeyModel] `tfsdk:"public_key"` // OnPremises & OnPremisesPersonal + Version types.String `tfsdk:"version"` // OnPremises & OnPremisesPersonal +} + +func (to *baseDataSourceGatewayModel) set(ctx context.Context, from fabcore.GatewayClassification) diag.Diagnostics { + var diags diag.Diagnostics + + switch gateway := from.(type) { + case *fabcore.VirtualNetworkGateway: + to.ID = customtypes.NewUUIDPointerValue(gateway.ID) + to.Type = types.StringPointerValue((*string)(gateway.Type)) + to.DisplayName = types.StringPointerValue(gateway.DisplayName) + to.CapacityID = customtypes.NewUUIDPointerValue(gateway.CapacityID) + to.InactivityMinutesBeforeSleep = types.Int32PointerValue(gateway.InactivityMinutesBeforeSleep) + to.NumberOfMemberGateways = types.Int32PointerValue(gateway.NumberOfMemberGateways) + + virtualNetworkAzureResource := supertypes.NewSingleNestedObjectValueOfNull[virtualNetworkAzureResourceModel](ctx) + if gateway.VirtualNetworkAzureResource != nil { + virtualNetworkAzureResourceModel := &virtualNetworkAzureResourceModel{} + virtualNetworkAzureResourceModel.set(gateway.VirtualNetworkAzureResource) + + if diags := virtualNetworkAzureResource.Set(ctx, virtualNetworkAzureResourceModel); diags.HasError() { + return diags + } + } + + to.VirtualNetworkAzureResource = virtualNetworkAzureResource + + case *fabcore.OnPremisesGateway: + to.ID = customtypes.NewUUIDPointerValue(gateway.ID) + to.Type = types.StringPointerValue((*string)(gateway.Type)) + to.DisplayName = types.StringPointerValue(gateway.DisplayName) + to.NumberOfMemberGateways = types.Int32PointerValue(gateway.NumberOfMemberGateways) + to.AllowCloudConnectionRefresh = types.BoolPointerValue(gateway.AllowCloudConnectionRefresh) + to.AllowCustomConnectors = types.BoolPointerValue(gateway.AllowCustomConnectors) + to.LoadBalancingSetting = types.StringPointerValue((*string)(gateway.LoadBalancingSetting)) + to.Version = types.StringPointerValue(gateway.Version) + + publicKey := supertypes.NewSingleNestedObjectValueOfNull[publicKeyModel](ctx) + if gateway.PublicKey != nil { + publicKeyModel := &publicKeyModel{} + publicKeyModel.set(gateway.PublicKey) + + if diags := publicKey.Set(ctx, publicKeyModel); diags.HasError() { + return diags + } + } + + to.PublicKey = publicKey + + case *fabcore.OnPremisesGatewayPersonal: + to.ID = customtypes.NewUUIDPointerValue(gateway.ID) + to.Type = types.StringPointerValue((*string)(gateway.Type)) + to.Version = types.StringPointerValue(gateway.Version) + + publicKey := supertypes.NewSingleNestedObjectValueOfNull[publicKeyModel](ctx) + if gateway.PublicKey != nil { + publicKeyModel := &publicKeyModel{} + publicKeyModel.set(gateway.PublicKey) + + if diags := publicKey.Set(ctx, publicKeyModel); diags.HasError() { + return diags + } + } + + to.PublicKey = publicKey + + default: + diags.AddError("Unsupported Gateway type", fmt.Sprintf("The Gateway type '%T' is not supported.", gateway)) + + return diags + } + + return nil +} + +type publicKeyModel struct { + Exponent types.String `tfsdk:"exponent"` + Modulus types.String `tfsdk:"modulus"` +} + +func (to *publicKeyModel) set(from *fabcore.PublicKey) { + to.Exponent = types.StringPointerValue(from.Exponent) + to.Modulus = types.StringPointerValue(from.Modulus) +} diff --git a/internal/services/gateway/models_data_gateway_role_assignment.go b/internal/services/gateway/models_data_gateway_role_assignment.go new file mode 100644 index 00000000..8b5e4f95 --- /dev/null +++ b/internal/services/gateway/models_data_gateway_role_assignment.go @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" + + "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" +) + +type dataSourceGatewayRoleAssignmentsModel struct { + GatewayID customtypes.UUID `tfsdk:"gateway_id"` + Values supertypes.ListNestedObjectValueOf[gatewayRoleAssignmentModel] `tfsdk:"values"` + Timeouts timeouts.Value `tfsdk:"timeouts"` +} + +func (to *dataSourceGatewayRoleAssignmentsModel) setValues(ctx context.Context, from []fabcore.GatewayRoleAssignment) diag.Diagnostics { + slice := make([]*gatewayRoleAssignmentModel, 0, len(from)) + + for _, entity := range from { + var entityModel gatewayRoleAssignmentModel + + if diags := entityModel.set(ctx, entity); diags.HasError() { + return diags + } + + slice = append(slice, &entityModel) + } + + return to.Values.Set(ctx, slice) +} + +type gatewayRoleAssignmentModel struct { + ID customtypes.UUID `tfsdk:"id"` + Role types.String `tfsdk:"role"` + DisplayName types.String `tfsdk:"display_name"` + Type types.String `tfsdk:"type"` + Details supertypes.SingleNestedObjectValueOf[principalDetailsModel] `tfsdk:"details"` +} + +func (to *gatewayRoleAssignmentModel) set(ctx context.Context, from fabcore.GatewayRoleAssignment) diag.Diagnostics { + to.ID = customtypes.NewUUIDPointerValue(from.ID) + to.Role = types.StringPointerValue((*string)(from.Role)) + + detailsModel := &principalDetailsModel{} + detailsModel.set(from.Principal, to) + + if diags := to.Details.Set(ctx, detailsModel); diags.HasError() { + return diags + } + + return nil +} + +type principalDetailsModel struct { + UserPrincipalName types.String `tfsdk:"user_principal_name"` + GroupType types.String `tfsdk:"group_type"` + AppID customtypes.UUID `tfsdk:"app_id"` + ParentPrincipalID customtypes.UUID `tfsdk:"parent_principal_id"` +} + +func (to *principalDetailsModel) set(from *fabcore.Principal, roleAssignment *gatewayRoleAssignmentModel) { + to.UserPrincipalName = types.StringNull() + to.GroupType = types.StringNull() + to.AppID = customtypes.NewUUIDNull() + to.ParentPrincipalID = customtypes.NewUUIDNull() + + roleAssignment.DisplayName = types.StringPointerValue(from.DisplayName) + roleAssignment.Type = types.StringPointerValue((*string)(from.Type)) + + switch *from.Type { + case fabcore.PrincipalTypeUser: + to.UserPrincipalName = types.StringPointerValue(from.UserDetails.UserPrincipalName) + case fabcore.PrincipalTypeGroup: + to.GroupType = types.StringPointerValue((*string)(from.GroupDetails.GroupType)) + case fabcore.PrincipalTypeServicePrincipal: + to.AppID = customtypes.NewUUIDPointerValue(from.ServicePrincipalDetails.AADAppID) + case fabcore.PrincipalTypeServicePrincipalProfile: + to.ParentPrincipalID = customtypes.NewUUIDPointerValue(from.ServicePrincipalProfileDetails.ParentPrincipal.ID) + } +} diff --git a/internal/services/gateway/models_data_gateways.go b/internal/services/gateway/models_data_gateways.go index b8ebe885..6357a7fc 100644 --- a/internal/services/gateway/models_data_gateways.go +++ b/internal/services/gateway/models_data_gateways.go @@ -13,15 +13,15 @@ import ( ) type dataSourceGatewaysModel struct { - Values supertypes.ListNestedObjectValueOf[baseGatewayModel] `tfsdk:"values"` - Timeouts timeouts.Value `tfsdk:"timeouts"` + Values supertypes.ListNestedObjectValueOf[baseDataSourceGatewayModel] `tfsdk:"values"` + Timeouts timeouts.Value `tfsdk:"timeouts"` } func (to *dataSourceGatewaysModel) setValues(ctx context.Context, from []fabcore.GatewayClassification) diag.Diagnostics { - slice := make([]*baseGatewayModel, 0, len(from)) + slice := make([]*baseDataSourceGatewayModel, 0, len(from)) for _, entity := range from { - var entityModel baseGatewayModel + var entityModel baseDataSourceGatewayModel if diags := entityModel.set(ctx, entity); diags.HasError() { return diags } diff --git a/internal/services/gateway/models_resource_gateway.go b/internal/services/gateway/models_resource_gateway.go index c10862f1..ccf5d4b8 100644 --- a/internal/services/gateway/models_resource_gateway.go +++ b/internal/services/gateway/models_resource_gateway.go @@ -9,14 +9,60 @@ import ( "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" + + "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" ) type resourceGatewayModel struct { - baseGatewayModel + baseResourceGatewayModel Timeouts timeouts.Value `tfsdk:"timeouts"` } +type baseResourceGatewayModel struct { + ID customtypes.UUID `tfsdk:"id"` + Type types.String `tfsdk:"type"` + + DisplayName types.String `tfsdk:"display_name"` // VirtualNetwork + CapacityID customtypes.UUID `tfsdk:"capacity_id"` // VirtualNetwork + InactivityMinutesBeforeSleep types.Int32 `tfsdk:"inactivity_minutes_before_sleep"` // VirtualNetwork + NumberOfMemberGateways types.Int32 `tfsdk:"number_of_member_gateways"` // VirtualNetwork + VirtualNetworkAzureResource supertypes.SingleNestedObjectValueOf[virtualNetworkAzureResourceModel] `tfsdk:"virtual_network_azure_resource"` // VirtualNetwork +} + +func (to *baseResourceGatewayModel) set(ctx context.Context, from fabcore.GatewayClassification) diag.Diagnostics { + var diags diag.Diagnostics + + switch gateway := from.(type) { + case *fabcore.VirtualNetworkGateway: + to.ID = customtypes.NewUUIDPointerValue(gateway.ID) + to.Type = types.StringPointerValue((*string)(gateway.Type)) + to.DisplayName = types.StringPointerValue(gateway.DisplayName) + to.CapacityID = customtypes.NewUUIDPointerValue(gateway.CapacityID) + to.InactivityMinutesBeforeSleep = types.Int32PointerValue(gateway.InactivityMinutesBeforeSleep) + to.NumberOfMemberGateways = types.Int32PointerValue(gateway.NumberOfMemberGateways) + + virtualNetworkAzureResource := supertypes.NewSingleNestedObjectValueOfNull[virtualNetworkAzureResourceModel](ctx) + if gateway.VirtualNetworkAzureResource != nil { + virtualNetworkAzureResourceModel := &virtualNetworkAzureResourceModel{} + virtualNetworkAzureResourceModel.set(gateway.VirtualNetworkAzureResource) + + if diags := virtualNetworkAzureResource.Set(ctx, virtualNetworkAzureResourceModel); diags.HasError() { + return diags + } + } + + to.VirtualNetworkAzureResource = virtualNetworkAzureResource + default: + diags.AddError("Unsupported Gateway type", fmt.Sprintf("The Gateway type '%T' is not supported.", gateway)) + return diags + } + + return nil +} + type requestCreateGateway struct { fabcore.CreateGatewayRequestClassification } @@ -71,6 +117,7 @@ func (to *requestUpdateGateway) set(from resourceGatewayModel) diag.Diagnostics DisplayName: from.DisplayName.ValueStringPointer(), CapacityID: from.CapacityID.ValueStringPointer(), InactivityMinutesBeforeSleep: from.InactivityMinutesBeforeSleep.ValueInt32Pointer(), + NumberOfMemberGateways: from.NumberOfMemberGateways.ValueInt32Pointer(), } default: diags.AddError("Unsupported Gateway type", fmt.Sprintf("The Gateway type '%T' is not supported.", gatewayType)) diff --git a/internal/services/gateway/models_resource_gateway_role_assignment.go b/internal/services/gateway/models_resource_gateway_role_assignment.go new file mode 100644 index 00000000..3f84aa22 --- /dev/null +++ b/internal/services/gateway/models_resource_gateway_role_assignment.go @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework/types" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + + "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" +) + +type resourceGatewayRoleAssignmentModel struct { + ID customtypes.UUID `tfsdk:"id"` + PrincipalID customtypes.UUID `tfsdk:"principal_id"` + PrincipalType types.String `tfsdk:"principal_type"` + Role types.String `tfsdk:"role"` + GatewayID customtypes.UUID `tfsdk:"gateway_id"` + Timeouts timeouts.Value `tfsdk:"timeouts"` +} + +func (to *resourceGatewayRoleAssignmentModel) set(from fabcore.GatewayRoleAssignment) { + to.ID = customtypes.NewUUIDPointerValue(from.ID) + to.PrincipalID = customtypes.NewUUIDPointerValue(from.Principal.ID) + to.PrincipalType = types.StringPointerValue((*string)(from.Principal.Type)) + to.Role = types.StringPointerValue((*string)(from.Role)) +} + +type requestCreateGatewayRoleAssignment struct { + fabcore.AddGatewayRoleAssignmentRequest +} + +func (to *requestCreateGatewayRoleAssignment) set(from resourceGatewayRoleAssignmentModel) { + to.Principal = &fabcore.Principal{ID: from.PrincipalID.ValueStringPointer(), Type: (*fabcore.PrincipalType)(from.PrincipalType.ValueStringPointer())} + to.Role = (*fabcore.GatewayRole)(from.Role.ValueStringPointer()) +} + +type requestUpdateGatewayRoleAssignment struct { + fabcore.UpdateGatewayRoleAssignmentRequest +} + +func (to *requestUpdateGatewayRoleAssignment) set(from resourceGatewayRoleAssignmentModel) { + to.Role = (*fabcore.GatewayRole)(from.Role.ValueStringPointer()) +} diff --git a/internal/services/gateway/resource_gateway.go b/internal/services/gateway/resource_gateway.go index 0d6f01e0..016df317 100644 --- a/internal/services/gateway/resource_gateway.go +++ b/internal/services/gateway/resource_gateway.go @@ -15,15 +15,15 @@ import ( "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/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" "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" supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" superint32validator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/int32validator" - supersetvalidator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/setvalidator" + superobjectvalidator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/objectvalidator" superstringvalidator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -70,6 +70,7 @@ func (r *resourceGateway) Schema(ctx context.Context, _ resource.SchemaRequest, "display_name": schema.StringAttribute{ MarkdownDescription: "The " + ItemName + " display name.", Optional: true, + Computed: true, Validators: []validator.String{ stringvalidator.LengthAtMost(200), superstringvalidator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), @@ -85,12 +86,13 @@ func (r *resourceGateway) Schema(ctx context.Context, _ resource.SchemaRequest, stringplanmodifier.RequiresReplace(), }, Validators: []validator.String{ - stringvalidator.OneOf(utils.ConvertEnumsToStringSlices(fabcore.PossibleGatewayTypeValues(), false)...), + stringvalidator.OneOf(string(fabcore.GatewayTypeVirtualNetwork)), }, }, "capacity_id": schema.StringAttribute{ MarkdownDescription: "The " + ItemName + " capacity ID.", Optional: true, + Computed: true, CustomType: customtypes.UUIDType{}, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), @@ -104,7 +106,8 @@ func (r *resourceGateway) Schema(ctx context.Context, _ resource.SchemaRequest, }, "inactivity_minutes_before_sleep": schema.Int32Attribute{ MarkdownDescription: "The " + ItemName + " inactivity minutes before sleep.", - Required: true, + Optional: true, + Computed: true, Validators: []validator.Int32{ int32validator.OneOf(PossibleInactivityMinutesBeforeSleepValues...), superint32validator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), @@ -115,7 +118,8 @@ func (r *resourceGateway) Schema(ctx context.Context, _ resource.SchemaRequest, }, "number_of_member_gateways": schema.Int32Attribute{ MarkdownDescription: "The " + ItemName + " number of member gateways.", - Required: true, + Optional: true, + Computed: true, Validators: []validator.Int32{ int32validator.Between(MinNumberOfMemberGatewaysValues, MaxNumberOfMemberGatewaysValues), superint32validator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), @@ -124,38 +128,37 @@ func (r *resourceGateway) Schema(ctx context.Context, _ resource.SchemaRequest, }), }, }, - "virtual_network_azure_resource": schema.SetNestedAttribute{ + "virtual_network_azure_resource": schema.SingleNestedAttribute{ MarkdownDescription: "The " + ItemName + " virtual network Azure resource.", Optional: true, - CustomType: supertypes.NewSetNestedObjectTypeOf[virtualNetworkAzureResourceModel](ctx), - PlanModifiers: []planmodifier.Set{ - setplanmodifier.RequiresReplace(), + Computed: true, + CustomType: supertypes.NewSingleNestedObjectTypeOf[virtualNetworkAzureResourceModel](ctx), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.RequiresReplace(), }, - Validators: []validator.Set{ - supersetvalidator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), + Validators: []validator.Object{ + superobjectvalidator.RequireIfAttributeIsOneOf(path.MatchRoot("type"), []attr.Value{ types.StringValue(string(fabcore.GatewayTypeVirtualNetwork)), }), }, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "virtual_network_name": schema.StringAttribute{ - MarkdownDescription: "The virtual network name.", - Required: true, - }, - "subnet_name": schema.StringAttribute{ - MarkdownDescription: "The subnet name.", - Required: true, - }, - "resource_group_name": schema.StringAttribute{ - MarkdownDescription: "The resource group name.", - Required: true, - }, - "subscription_id": schema.StringAttribute{ - MarkdownDescription: "The subscription ID.", - Required: true, - CustomType: customtypes.UUIDType{}, - }, + Attributes: map[string]schema.Attribute{ + "virtual_network_name": schema.StringAttribute{ + MarkdownDescription: "The virtual network name.", + Required: true, + }, + "subnet_name": schema.StringAttribute{ + MarkdownDescription: "The subnet name.", + Required: true, + }, + "resource_group_name": schema.StringAttribute{ + MarkdownDescription: "The resource group name.", + Required: true, + }, + "subscription_id": schema.StringAttribute{ + MarkdownDescription: "The subscription ID.", + Required: true, + CustomType: customtypes.UUIDType{}, }, }, }, @@ -354,29 +357,29 @@ func (r *resourceGateway) Delete(ctx context.Context, req resource.DeleteRequest }) } -// func (r *resourceGateway) 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, -// }) - -// _, diags := customtypes.NewUUIDValueMust(req.ID) -// if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { -// return -// } - -// resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) - -// tflog.Debug(ctx, "IMPORT", map[string]any{ -// "action": "end", -// }) - -// if resp.Diagnostics.HasError() { -// return -// } -// } +func (r *resourceGateway) 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, + }) + + _, diags := customtypes.NewUUIDValueMust(req.ID) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + + tflog.Debug(ctx, "IMPORT", map[string]any{ + "action": "end", + }) + + if resp.Diagnostics.HasError() { + return + } +} func (r *resourceGateway) get(ctx context.Context, model *resourceGatewayModel) diag.Diagnostics { tflog.Trace(ctx, "getting "+ItemName) diff --git a/internal/services/gateway/resource_gateway_role_assignment.go b/internal/services/gateway/resource_gateway_role_assignment.go new file mode 100644 index 00000000..83a5282f --- /dev/null +++ b/internal/services/gateway/resource_gateway_role_assignment.go @@ -0,0 +1,351 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "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/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/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 provider defined types fully satisfy framework interfaces. +var ( + _ resource.ResourceWithConfigure = (*resourceGatewayRoleAssignment)(nil) + _ resource.ResourceWithImportState = (*resourceGatewayRoleAssignment)(nil) +) + +type resourceGatewayRoleAssignment struct { + pConfigData *pconfig.ProviderData + client *fabcore.GatewaysClient +} + +func NewResourceGatewayRoleAssignment() resource.Resource { + return &resourceGatewayRoleAssignment{} +} + +func (r *resourceGatewayRoleAssignment) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_" + GatewayRoleAssignmentTFName +} + +func (r *resourceGatewayRoleAssignment) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Manage a " + GatewayRoleAssignmentName + ".\n\n" + + ItemDocsSPNSupport, + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "The " + GatewayRoleAssignmentName + " ID.", + Computed: true, + CustomType: customtypes.UUIDType{}, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "principal_id": schema.StringAttribute{ + MarkdownDescription: "The Principal ID.", + Required: true, + CustomType: customtypes.UUIDType{}, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "principal_type": schema.StringAttribute{ + MarkdownDescription: "The type of the principal. Accepted values: " + utils.ConvertStringSlicesToString(fabcore.PossiblePrincipalTypeValues(), true, true) + ".", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + Validators: []validator.String{ + stringvalidator.OneOf(utils.ConvertEnumsToStringSlices(fabcore.PossiblePrincipalTypeValues(), false)...), + }, + }, + "role": schema.StringAttribute{ + MarkdownDescription: "The Gateway Role of the principal. Accepted values: " + utils.ConvertStringSlicesToString(fabcore.PossibleGatewayRoleValues(), true, true) + ".", + Required: true, + Validators: []validator.String{ + stringvalidator.OneOf(utils.ConvertEnumsToStringSlices(fabcore.PossibleGatewayRoleValues(), false)...), + }, + }, + "gateway_id": schema.StringAttribute{ + MarkdownDescription: "The " + ItemName + " ID.", + Required: true, + CustomType: customtypes.UUIDType{}, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "timeouts": timeouts.AttributesAll(ctx), + }, + } +} + +func (r *resourceGatewayRoleAssignment) 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 = fabcore.NewClientFactoryWithClient(*pConfigData.FabricClient).NewGatewaysClient() +} + +func (r *resourceGatewayRoleAssignment) 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 resourceGatewayRoleAssignmentModel + + 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 requestCreateGatewayRoleAssignment + + reqCreate.set(plan) + + respCreate, err := r.client.AddGatewayRoleAssignment(ctx, plan.GatewayID.ValueString(), reqCreate.AddGatewayRoleAssignmentRequest, nil) + if resp.Diagnostics.Append(utils.GetDiagsFromError(ctx, err, utils.OperationCreate, nil)...); resp.Diagnostics.HasError() { + return + } + + plan.set(respCreate.GatewayRoleAssignment) + + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + + tflog.Debug(ctx, "CREATE", map[string]any{ + "action": "end", + }) + + if resp.Diagnostics.HasError() { + return + } +} + +func (r *resourceGatewayRoleAssignment) 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, + }) + + var state resourceGatewayRoleAssignmentModel + + 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() + + err := r.get(ctx, &state) + if diags := utils.GetDiagsFromError(ctx, err, utils.OperationRead, fabcore.ErrCommon.EntityNotFound); resp.Diagnostics.HasError() { + if utils.IsErrNotFound(state.ID.ValueString(), &diags, fabcore.ErrCommon.EntityNotFound) { + resp.State.RemoveResource(ctx) + } + + 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 *resourceGatewayRoleAssignment) 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 resourceGatewayRoleAssignmentModel + + 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 requestUpdateGatewayRoleAssignment + + reqUpdate.set(plan) + + respUpdate, err := r.client.UpdateGatewayRoleAssignment(ctx, plan.GatewayID.ValueString(), plan.ID.ValueString(), reqUpdate.UpdateGatewayRoleAssignmentRequest, nil) + if resp.Diagnostics.Append(utils.GetDiagsFromError(ctx, err, utils.OperationUpdate, nil)...); resp.Diagnostics.HasError() { + return + } + + plan.set(respUpdate.GatewayRoleAssignment) + + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + + tflog.Debug(ctx, "UPDATE", map[string]any{ + "action": "end", + }) + + if resp.Diagnostics.HasError() { + return + } +} + +func (r *resourceGatewayRoleAssignment) 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 resourceGatewayRoleAssignmentModel + + 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.DeleteGatewayRoleAssignment(ctx, state.GatewayID.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 *resourceGatewayRoleAssignment) 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, + }) + + gatewayID, gatewayRoleAssignmentID, found := strings.Cut(req.ID, "/") + if !found { + resp.Diagnostics.AddError( + common.ErrorImportIdentifierHeader, + fmt.Sprintf(common.ErrorImportIdentifierDetails, "GatewayID/GatewayRoleAssignmentID"), + ) + + return + } + + uuidGatewayID, diags := customtypes.NewUUIDValueMust(gatewayID) + resp.Diagnostics.Append(diags...) + + uuidGatewayRoleAssignmentID, diags := customtypes.NewUUIDValueMust(gatewayRoleAssignmentID) + 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 := resourceGatewayRoleAssignmentModel{ + ID: uuidGatewayRoleAssignmentID, + GatewayID: uuidGatewayID, + Timeouts: timeout, + } + + err := r.get(ctx, &state) + if resp.Diagnostics.Append(utils.GetDiagsFromError(ctx, err, utils.OperationImport, nil)...); 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 *resourceGatewayRoleAssignment) get(ctx context.Context, model *resourceGatewayRoleAssignmentModel) error { + tflog.Trace(ctx, "getting Gateway Role Assignment") + + respGetInfo, err := r.client.GetGatewayRoleAssignment(ctx, model.GatewayID.ValueString(), model.ID.ValueString(), nil) + if err != nil { + return err + } + + model.set(respGetInfo.GatewayRoleAssignment) + + return nil +} diff --git a/internal/services/gateway/resource_gateway_test.go b/internal/services/gateway/resource_gateway_test.go new file mode 100644 index 00000000..4689b18e --- /dev/null +++ b/internal/services/gateway/resource_gateway_test.go @@ -0,0 +1,663 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway_test + +import ( + "errors" + "fmt" + "regexp" + "strconv" + "testing" + + at "github.com/dcarbone/terraform-plugin-framework-utils/v3/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + 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/services/gateway" + "github.com/microsoft/terraform-provider-fabric/internal/testhelp" + "github.com/microsoft/terraform-provider-fabric/internal/testhelp/fakes" +) + +var ( + testResourceItemFQN = testhelp.ResourceFQN("fabric", itemTFName, "test") + testResourceItemHeader = at.ResourceHeader(testhelp.TypeName("fabric", itemTFName), "test") +) + +func TestUnit_GatewayResource_Attributes(t *testing.T) { + resource.ParallelTest(t, testhelp.NewTestUnitCase(t, &testResourceItemFQN, fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ + // error - missing attributes + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{}, + ), + ExpectError: regexp.MustCompile(`Missing required argument`), + }, + // error - unexpected attribute + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeVirtualNetwork), + "display_name": "test", + "unexpected_attr": "test", + }, + ), + ExpectError: regexp.MustCompile(`An argument named "unexpected_attr" is not expected here`), + }, + // error - missing required attributes - type + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{}, + ), + ExpectError: regexp.MustCompile(`The argument "type" is required, but no definition was found.`), + }, + // error - missing required attributes - display_name + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeVirtualNetwork), + "inactivity_minutes_before_sleep": (int)(gateway.PossibleInactivityMinutesBeforeSleepValues[0]), + "number_of_member_gateways": (int)(gateway.MinNumberOfMemberGatewaysValues), + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": "test", + "virtual_network_name": "test", + "subnet_name": "test", + "subscription_id": "00000000-0000-0000-0000-000000000000", + }, + "capacity_id": "00000000-0000-0000-0000-000000000000", + }, + ), + ExpectError: regexp.MustCompile(`Invalid configuration for attribute display_name`), + }, + // error - missing required attributes - inactivity_minutes_before_sleep + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeVirtualNetwork), + "display_name": "test", + "number_of_member_gateways": (int)(gateway.MinNumberOfMemberGatewaysValues), + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": "test", + "virtual_network_name": "test", + "subnet_name": "test", + "subscription_id": "00000000-0000-0000-0000-000000000000", + }, + "capacity_id": "00000000-0000-0000-0000-000000000000", + }, + ), + ExpectError: regexp.MustCompile(`Invalid configuration for attribute inactivity_minutes_before_sleep`), + }, + // error - missing required attributes - number_of_member_gateways + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeVirtualNetwork), + "display_name": "test", + "inactivity_minutes_before_sleep": (int)(gateway.PossibleInactivityMinutesBeforeSleepValues[0]), + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": "test", + "virtual_network_name": "test", + "subnet_name": "test", + "subscription_id": "00000000-0000-0000-0000-000000000000", + }, + "capacity_id": "00000000-0000-0000-0000-000000000000", + }, + ), + ExpectError: regexp.MustCompile(`Invalid configuration for attribute number_of_member_gateways`), + }, + // error - missing required attributes - virtual_network_azure_resource + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeVirtualNetwork), + "display_name": "test", + "inactivity_minutes_before_sleep": (int)(gateway.PossibleInactivityMinutesBeforeSleepValues[0]), + "number_of_member_gateways": (int)(gateway.MinNumberOfMemberGatewaysValues), + "capacity_id": "00000000-0000-0000-0000-000000000000", + }, + ), + ExpectError: regexp.MustCompile(`Invalid configuration for attribute virtual_network_azure_resource`), + }, + // error - missing required attributes - capacity_id + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeVirtualNetwork), + "display_name": "test", + "inactivity_minutes_before_sleep": (int)(gateway.PossibleInactivityMinutesBeforeSleepValues[0]), + "number_of_member_gateways": (int)(gateway.MinNumberOfMemberGatewaysValues), + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": "test", + "virtual_network_name": "test", + "subnet_name": "test", + "subscription_id": "00000000-0000-0000-0000-000000000000", + }, + }, + ), + ExpectError: regexp.MustCompile(`Invalid configuration for attribute capacity_id`), + }, + // error - invalid attribute value - inactivity_minutes_before_sleep + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeVirtualNetwork), + "display_name": "test", + "inactivity_minutes_before_sleep": (int)(gateway.PossibleInactivityMinutesBeforeSleepValues[0]) - 1, + "number_of_member_gateways": (int)(gateway.MinNumberOfMemberGatewaysValues), + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": "test", + "virtual_network_name": "test", + "subnet_name": "test", + "subscription_id": "00000000-0000-0000-0000-000000000000", + }, + "capacity_id": "00000000-0000-0000-0000-000000000000", + }, + ), + ExpectError: regexp.MustCompile(`Invalid Attribute Value`), + }, + // error - invalid attribute value - number_of_member_gateways + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeVirtualNetwork), + "display_name": "test", + "inactivity_minutes_before_sleep": (int)(gateway.PossibleInactivityMinutesBeforeSleepValues[0]), + "number_of_member_gateways": (int)(gateway.MinNumberOfMemberGatewaysValues) - 1, + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": "test", + "virtual_network_name": "test", + "subnet_name": "test", + "subscription_id": "00000000-0000-0000-0000-000000000000", + }, + "capacity_id": "00000000-0000-0000-0000-000000000000", + }, + ), + ExpectError: regexp.MustCompile(fmt.Sprintf(`Attribute number_of_member_gateways value must be between %d and %d`, gateway.MinNumberOfMemberGatewaysValues, gateway.MaxNumberOfMemberGatewaysValues)), + }, + // error - invalid uuid - capacity_id + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeVirtualNetwork), + "display_name": "test", + "inactivity_minutes_before_sleep": (int)(gateway.PossibleInactivityMinutesBeforeSleepValues[0]), + "number_of_member_gateways": (int)(gateway.MinNumberOfMemberGatewaysValues), + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": "test", + "virtual_network_name": "test", + "subnet_name": "test", + "subscription_id": "00000000-0000-0000-0000-000000000000", + }, + "capacity_id": "invalid uuid", + }, + ), + ExpectError: regexp.MustCompile(customtypes.UUIDTypeErrorInvalidStringHeader), + }, + // error - unsupported gateway type - OnPremises + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeOnPremises), + }, + ), + ExpectError: regexp.MustCompile(common.ErrorAttValueMatch), + }, + // error - unsupported gateway type - OnPremisesPersonal + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeOnPremisesPersonal), + }, + ), + ExpectError: regexp.MustCompile(common.ErrorAttValueMatch), + }, + })) +} + +func TestUnit_GatewayResource_ImportState(t *testing.T) { + entity := fakes.NewRandomVirtualNetworkGateway() + + fakes.FakeServer.Upsert(fakes.NewRandomVirtualNetworkGateway()) + fakes.FakeServer.Upsert(entity) + fakes.FakeServer.Upsert(fakes.NewRandomVirtualNetworkGateway()) + + testCase := at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": (string)(*entity.Type), + "display_name": *entity.DisplayName, + "inactivity_minutes_before_sleep": (int)(*entity.InactivityMinutesBeforeSleep), + "number_of_member_gateways": (int)(*entity.NumberOfMemberGateways), + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": *entity.VirtualNetworkAzureResource.ResourceGroupName, + "virtual_network_name": *entity.VirtualNetworkAzureResource.VirtualNetworkName, + "subnet_name": *entity.VirtualNetworkAzureResource.SubnetName, + "subscription_id": *entity.VirtualNetworkAzureResource.SubscriptionID, + }, + "capacity_id": *entity.CapacityID, + }, + ) + + resource.Test(t, testhelp.NewTestUnitCase(t, &testResourceItemFQN, fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ + { + ResourceName: testResourceItemFQN, + Config: testCase, + ImportStateId: "not-valid", + ImportState: true, + ExpectError: regexp.MustCompile(customtypes.UUIDTypeErrorInvalidStringHeader), + }, + // Import state testing + { + ResourceName: testResourceItemFQN, + Config: testCase, + ImportStateId: *entity.ID, + ImportState: true, + ImportStatePersist: true, + ImportStateCheck: func(is []*terraform.InstanceState) error { + if len(is) != 1 { + return errors.New("expected one instance state") + } + + if is[0].ID != *entity.ID { + return errors.New(testResourceItemFQN + ": unexpected ID") + } + + return nil + }, + }, + })) +} + +func TestUnit_GatewayResource_CRUD(t *testing.T) { + entityExist := fakes.NewRandomVirtualNetworkGateway() + entityBefore := fakes.NewRandomVirtualNetworkGateway() + entityAfter := fakes.NewRandomVirtualNetworkGateway() + + fakes.FakeServer.Upsert(fakes.NewRandomVirtualNetworkGateway()) + fakes.FakeServer.Upsert(entityExist) + fakes.FakeServer.Upsert(fakes.NewRandomVirtualNetworkGateway()) + + resource.Test(t, testhelp.NewTestUnitCase(t, &testResourceItemFQN, fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ + // error - create - existing entity + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": string(fabcore.GatewayTypeVirtualNetwork), + "display_name": *entityExist.DisplayName, + "inactivity_minutes_before_sleep": (int)(gateway.PossibleInactivityMinutesBeforeSleepValues[0]), + "number_of_member_gateways": (int)(gateway.MinNumberOfMemberGatewaysValues), + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": "test", + "virtual_network_name": "test", + "subnet_name": "test", + "subscription_id": "00000000-0000-0000-0000-000000000000", + }, + "capacity_id": "00000000-0000-0000-0000-000000000000", + }, + ), + ExpectError: regexp.MustCompile(common.ErrorCreateHeader), + }, + // Create and Read + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": (string)(*entityBefore.Type), + "display_name": *entityBefore.DisplayName, + "inactivity_minutes_before_sleep": (int)(*entityBefore.InactivityMinutesBeforeSleep), + "number_of_member_gateways": (int)(*entityBefore.NumberOfMemberGateways), + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": *entityBefore.VirtualNetworkAzureResource.ResourceGroupName, + "virtual_network_name": *entityBefore.VirtualNetworkAzureResource.VirtualNetworkName, + "subnet_name": *entityBefore.VirtualNetworkAzureResource.SubnetName, + "subscription_id": *entityBefore.VirtualNetworkAzureResource.SubscriptionID, + }, + "capacity_id": *entityBefore.CapacityID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "type", (*string)(entityBefore.Type)), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "display_name", entityBefore.DisplayName), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "capacity_id", entityBefore.CapacityID), + resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(*entityBefore.InactivityMinutesBeforeSleep))), + resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(*entityBefore.NumberOfMemberGateways))), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.resource_group_name", entityBefore.VirtualNetworkAzureResource.ResourceGroupName), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.subnet_name", entityBefore.VirtualNetworkAzureResource.SubnetName), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.subscription_id", entityBefore.VirtualNetworkAzureResource.SubscriptionID), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.virtual_network_name", entityBefore.VirtualNetworkAzureResource.VirtualNetworkName), + ), + }, + // Update and Read - no replacement + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": (string)(*entityBefore.Type), + "display_name": *entityAfter.DisplayName, + "inactivity_minutes_before_sleep": (int)(*entityAfter.InactivityMinutesBeforeSleep), + "number_of_member_gateways": (int)(*entityAfter.NumberOfMemberGateways), + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": *entityBefore.VirtualNetworkAzureResource.ResourceGroupName, + "virtual_network_name": *entityBefore.VirtualNetworkAzureResource.VirtualNetworkName, + "subnet_name": *entityBefore.VirtualNetworkAzureResource.SubnetName, + "subscription_id": *entityBefore.VirtualNetworkAzureResource.SubscriptionID, + }, + "capacity_id": *entityAfter.CapacityID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceItemFQN, "type", string(*entityBefore.Type)), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "display_name", entityAfter.DisplayName), + resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(*entityAfter.InactivityMinutesBeforeSleep))), + resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(*entityAfter.NumberOfMemberGateways))), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "capacity_id", entityAfter.CapacityID), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.resource_group_name", entityBefore.VirtualNetworkAzureResource.ResourceGroupName), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.subnet_name", entityBefore.VirtualNetworkAzureResource.SubnetName), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.subscription_id", entityBefore.VirtualNetworkAzureResource.SubscriptionID), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.virtual_network_name", entityBefore.VirtualNetworkAzureResource.VirtualNetworkName), + ), + }, + // Update and Read - with replacement + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": (string)(*entityBefore.Type), + "display_name": *entityAfter.DisplayName, + "inactivity_minutes_before_sleep": (int)(*entityAfter.InactivityMinutesBeforeSleep), + "number_of_member_gateways": (int)(*entityAfter.NumberOfMemberGateways), + "virtual_network_azure_resource": map[string]any{ + "resource_group_name": *entityAfter.VirtualNetworkAzureResource.ResourceGroupName, + "virtual_network_name": *entityAfter.VirtualNetworkAzureResource.VirtualNetworkName, + "subnet_name": *entityAfter.VirtualNetworkAzureResource.SubnetName, + "subscription_id": *entityAfter.VirtualNetworkAzureResource.SubscriptionID, + }, + "capacity_id": *entityAfter.CapacityID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceItemFQN, "type", string(*entityBefore.Type)), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "display_name", entityAfter.DisplayName), + resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(*entityAfter.InactivityMinutesBeforeSleep))), + resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(*entityAfter.NumberOfMemberGateways))), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "capacity_id", entityAfter.CapacityID), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.resource_group_name", entityAfter.VirtualNetworkAzureResource.ResourceGroupName), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.subnet_name", entityAfter.VirtualNetworkAzureResource.SubnetName), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.subscription_id", entityAfter.VirtualNetworkAzureResource.SubscriptionID), + resource.TestCheckResourceAttrPtr(testResourceItemFQN, "virtual_network_azure_resource.virtual_network_name", entityAfter.VirtualNetworkAzureResource.VirtualNetworkName), + ), + }, + // Delete testing automatically occurs in TestCase + })) +} + +// func TestAcc_GatewayResource_CRUD(t *testing.T) { +// capacity := testhelp.WellKnown()["Capacity"].(map[string]any) +// capacityID := capacity["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.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityCreateDisplayName, +// "capacity_id": capacityID, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityCreateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", ""), +// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), +// ), +// }, +// // Update and Read +// { +// ResourceName: testResourceItemFQN, +// Config: at.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityUpdateDisplayName, +// "description": entityUpdateDescription, +// "capacity_id": capacityID, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), +// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), +// ), +// }, +// // Update - unassign capacity +// { +// ResourceName: testResourceItemFQN, +// Config: at.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityUpdateDisplayName, +// "description": entityUpdateDescription, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), +// resource.TestCheckNoResourceAttr(testResourceItemFQN, "capacity_id"), +// ), +// }, +// // Update - assign capacity +// { +// ResourceName: testResourceItemFQN, +// Config: at.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityUpdateDisplayName, +// "description": entityUpdateDescription, +// "capacity_id": capacityID, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), +// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), +// ), +// }, +// // Update - assign identity +// { +// ResourceName: testResourceItemFQN, +// Config: at.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityUpdateDisplayName, +// "description": entityUpdateDescription, +// "capacity_id": capacityID, +// "identity": map[string]any{ +// "type": "SystemAssigned", +// }, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), +// resource.TestCheckResourceAttrSet(testResourceItemFQN, "identity.application_id"), +// ), +// }, +// // Update - unassign identity +// { +// ResourceName: testResourceItemFQN, +// Config: at.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityUpdateDisplayName, +// "description": entityUpdateDescription, +// "capacity_id": capacityID, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), +// resource.TestCheckNoResourceAttr(testResourceItemFQN, "identity"), +// ), +// }, +// }, +// )) +// } + +// func TestAcc_GatewayResource_Identity_CRUD(t *testing.T) { +// capacity := testhelp.WellKnown()["Capacity"].(map[string]any) +// capacityID := capacity["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.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityCreateDisplayName, +// "capacity_id": capacityID, +// "identity": map[string]any{ +// "type": "SystemAssigned", +// }, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityCreateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", ""), +// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), +// resource.TestCheckResourceAttrSet(testResourceItemFQN, "identity.application_id"), +// ), +// }, +// // Update and Read +// { +// ResourceName: testResourceItemFQN, +// Config: at.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityUpdateDisplayName, +// "description": entityUpdateDescription, +// "capacity_id": capacityID, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), +// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), +// ), +// }, +// // Update - unassign capacity +// { +// ResourceName: testResourceItemFQN, +// Config: at.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityUpdateDisplayName, +// "description": entityUpdateDescription, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), +// resource.TestCheckNoResourceAttr(testResourceItemFQN, "capacity_id"), +// ), +// }, +// // Update - assign capacity +// { +// ResourceName: testResourceItemFQN, +// Config: at.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityUpdateDisplayName, +// "description": entityUpdateDescription, +// "capacity_id": capacityID, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), +// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), +// ), +// }, +// // Update - unassign identity +// { +// ResourceName: testResourceItemFQN, +// Config: at.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityUpdateDisplayName, +// "description": entityUpdateDescription, +// "capacity_id": capacityID, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), +// resource.TestCheckNoResourceAttr(testResourceItemFQN, "identity"), +// ), +// }, +// // Update - assign identity +// { +// ResourceName: testResourceItemFQN, +// Config: at.CompileConfig( +// testResourceItemHeader, +// map[string]any{ +// "display_name": entityUpdateDisplayName, +// "description": entityUpdateDescription, +// "capacity_id": capacityID, +// "identity": map[string]any{ +// "type": "SystemAssigned", +// }, +// }, +// ), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), +// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), +// resource.TestCheckResourceAttrSet(testResourceItemFQN, "identity.application_id"), +// ), +// }, +// }, +// )) +// } diff --git a/internal/services/workspace/data_workspace_role_assignments.go b/internal/services/workspace/data_workspace_role_assignments.go index 643d89cf..c4429978 100644 --- a/internal/services/workspace/data_workspace_role_assignments.go +++ b/internal/services/workspace/data_workspace_role_assignments.go @@ -38,7 +38,7 @@ func (d *dataSourceWorkspaceRoleAssignments) Metadata(_ context.Context, req dat func (d *dataSourceWorkspaceRoleAssignments) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ - MarkdownDescription: "List a Fabric " + WorkspaceRoleAssignmentsName + ".\n\n" + + MarkdownDescription: "List Fabric " + WorkspaceRoleAssignmentsName + ".\n\n" + "Use this data source to list [" + WorkspaceRoleAssignmentsName + "](" + WorkspaceRoleAssignmentDocsURL + ").\n\n" + ItemDocsSPNSupport, Attributes: map[string]schema.Attribute{ diff --git a/internal/services/workspace/resource_workspace_role_assignment.go b/internal/services/workspace/resource_workspace_role_assignment.go index 2ee8637f..fc6af65d 100644 --- a/internal/services/workspace/resource_workspace_role_assignment.go +++ b/internal/services/workspace/resource_workspace_role_assignment.go @@ -46,7 +46,7 @@ func (r *resourceWorkspaceRoleAssignment) Metadata(_ context.Context, req resour func (r *resourceWorkspaceRoleAssignment) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - MarkdownDescription: "Manage a Workspace Role Assignment.\n\n" + + MarkdownDescription: "Manage a " + WorkspaceRoleAssignmentName + ".\n\n" + "See [Roles in Workspaces](https://learn.microsoft.com/fabric/get-started/roles-workspaces) for more information.\n\n" + ItemDocsSPNSupport, Attributes: map[string]schema.Attribute{ diff --git a/internal/testhelp/fakes/fabric_gateway.go b/internal/testhelp/fakes/fabric_gateway.go new file mode 100644 index 00000000..19da1757 --- /dev/null +++ b/internal/testhelp/fakes/fabric_gateway.go @@ -0,0 +1,203 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package fakes + +import ( + "net/http" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + fabfake "github.com/microsoft/fabric-sdk-go/fabric/fake" + + "github.com/microsoft/terraform-provider-fabric/internal/services/gateway" + "github.com/microsoft/terraform-provider-fabric/internal/testhelp" +) + +// operationsGateway implements SimpleIDOperations. +type operationsGateway struct{} + +func (o *operationsGateway) Create(data fabcore.CreateGatewayRequestClassification) fabcore.GatewayClassification { + switch gateway := data.(type) { + case *fabcore.CreateVirtualNetworkGatewayRequest: + entity := NewRandomVirtualNetworkGateway() + entity.Type = gateway.Type + entity.DisplayName = gateway.DisplayName + entity.CapacityID = gateway.CapacityID + entity.InactivityMinutesBeforeSleep = gateway.InactivityMinutesBeforeSleep + entity.NumberOfMemberGateways = gateway.NumberOfMemberGateways + entity.VirtualNetworkAzureResource = gateway.VirtualNetworkAzureResource + return entity + default: + panic("Unsupported Gateway type") + } +} + +func (o *operationsGateway) TransformCreate(entity fabcore.GatewayClassification) fabcore.GatewaysClientCreateGatewayResponse { + return fabcore.GatewaysClientCreateGatewayResponse{ + GatewayClassification: entity, + } +} + +func (o *operationsGateway) TransformGet(entity fabcore.GatewayClassification) fabcore.GatewaysClientGetGatewayResponse { + return fabcore.GatewaysClientGetGatewayResponse{ + GatewayClassification: entity, + } +} + +func (o *operationsGateway) TransformList(list []fabcore.GatewayClassification) fabcore.GatewaysClientListGatewaysResponse { + return fabcore.GatewaysClientListGatewaysResponse{ + ListGatewaysResponse: fabcore.ListGatewaysResponse{ + Value: list, + }, + } +} + +func (o *operationsGateway) TransformUpdate(entity fabcore.GatewayClassification) fabcore.GatewaysClientUpdateGatewayResponse { + return fabcore.GatewaysClientUpdateGatewayResponse{ + GatewayClassification: entity, + } +} + +func (o *operationsGateway) Update(base fabcore.GatewayClassification, data fabcore.UpdateGatewayRequestClassification) fabcore.GatewayClassification { + switch request := data.(type) { + case *fabcore.UpdateVirtualNetworkGatewayRequest: + gateway, _ := base.(*fabcore.VirtualNetworkGateway) + gateway.CapacityID = request.CapacityID + gateway.DisplayName = request.DisplayName + gateway.InactivityMinutesBeforeSleep = request.InactivityMinutesBeforeSleep + gateway.NumberOfMemberGateways = request.NumberOfMemberGateways + return gateway + default: + panic("Unsupported Gateway type") + } +} + +func (o *operationsGateway) Validate(newEntity fabcore.GatewayClassification, existing []fabcore.GatewayClassification) (int, error) { + for _, existingGateway := range existing { + if *(existingGateway.GetGateway().Type) != *(newEntity.GetGateway().Type) { + continue + } + switch gateway := newEntity.(type) { + case *fabcore.VirtualNetworkGateway: + vng := existingGateway.(*fabcore.VirtualNetworkGateway) + if *vng.DisplayName == *gateway.DisplayName { + // TODO(badeamarjieh): add error code to GO SDK + return http.StatusConflict, fabfake.SetResponseError(http.StatusConflict, "DuplicateGatewayName", fabcore.ErrWorkspace.WorkspaceNameAlreadyExists.Error()) + } + } + } + + return http.StatusCreated, nil +} + +func (o *operationsGateway) GetID(entity fabcore.GatewayClassification) string { + return *entity.GetGateway().ID +} + +func configureVirtualNetworkGateway(server *fakeServer) fabcore.VirtualNetworkGateway { + configureGateway(server) + return fabcore.VirtualNetworkGateway{} +} + +func configureOnPremisesGateway(server *fakeServer) fabcore.OnPremisesGateway { + configureGateway(server) + return fabcore.OnPremisesGateway{} +} + +func configureOnPremisesGatewayPersonal(server *fakeServer) fabcore.OnPremisesGatewayPersonal { + configureGateway(server) + return fabcore.OnPremisesGatewayPersonal{} +} + +func configureGateway(server *fakeServer) { + type concreteEntityOperations interface { + simpleIDOperations[ + fabcore.GatewayClassification, + fabcore.GatewaysClientGetGatewayResponse, + fabcore.GatewaysClientUpdateGatewayResponse, + fabcore.GatewaysClientCreateGatewayResponse, + fabcore.GatewaysClientListGatewaysResponse, + fabcore.CreateGatewayRequestClassification, + fabcore.UpdateGatewayRequestClassification] + } + + var entityOperations concreteEntityOperations = &operationsGateway{} + + handler := newTypedHandler(server, entityOperations) + + configureEntityPagerWithSimpleID( + handler, + entityOperations, + &handler.ServerFactory.Core.GatewaysServer.GetGateway, + &handler.ServerFactory.Core.GatewaysServer.UpdateGateway, + &handler.ServerFactory.Core.GatewaysServer.CreateGateway, + &handler.ServerFactory.Core.GatewaysServer.NewListGatewaysPager, + &handler.ServerFactory.Core.GatewaysServer.DeleteGateway) +} + +func NewRandomGateway() fabcore.GatewayClassification { + gatewayType := testhelp.RandomElement(fabcore.PossibleGatewayTypeValues()) + + switch gatewayType { + case fabcore.GatewayTypeOnPremises: + return NewRandomOnPremisesGateway() + case fabcore.GatewayTypeOnPremisesPersonal: + return NewRandomOnPremisesGatewayPersonal() + case fabcore.GatewayTypeVirtualNetwork: + return NewRandomVirtualNetworkGateway() + default: + panic("Unsupported Gateway type") + } +} + +func NewRandomOnPremisesGateway() *fabcore.OnPremisesGateway { + return &fabcore.OnPremisesGateway{ + ID: to.Ptr(testhelp.RandomUUID()), + Type: to.Ptr(fabcore.GatewayTypeOnPremises), + DisplayName: to.Ptr(testhelp.RandomName()), + AllowCloudConnectionRefresh: to.Ptr(testhelp.RandomBool()), + AllowCustomConnectors: to.Ptr(testhelp.RandomBool()), + NumberOfMemberGateways: to.Ptr(int32(testhelp.RandomInt(int(gateway.MinNumberOfMemberGatewaysValues), int(gateway.MaxNumberOfMemberGatewaysValues)))), + LoadBalancingSetting: to.Ptr(testhelp.RandomElement(fabcore.PossibleLoadBalancingSettingValues())), + Version: to.Ptr(testhelp.RandomName()), + PublicKey: NewRadomPublicKey(), + } +} + +func NewRandomOnPremisesGatewayPersonal() *fabcore.OnPremisesGatewayPersonal { + return &fabcore.OnPremisesGatewayPersonal{ + ID: to.Ptr(testhelp.RandomUUID()), + Type: to.Ptr(fabcore.GatewayTypeOnPremisesPersonal), + Version: to.Ptr(testhelp.RandomName()), + PublicKey: NewRadomPublicKey(), + } +} + +func NewRandomVirtualNetworkGateway() *fabcore.VirtualNetworkGateway { + return &fabcore.VirtualNetworkGateway{ + ID: to.Ptr(testhelp.RandomUUID()), + Type: to.Ptr(fabcore.GatewayTypeVirtualNetwork), + DisplayName: to.Ptr(testhelp.RandomName()), + CapacityID: to.Ptr(testhelp.RandomUUID()), + InactivityMinutesBeforeSleep: to.Ptr(testhelp.RandomElement(gateway.PossibleInactivityMinutesBeforeSleepValues)), + NumberOfMemberGateways: to.Ptr(int32(testhelp.RandomInt(int(gateway.MinNumberOfMemberGatewaysValues), int(gateway.MaxNumberOfMemberGatewaysValues)))), + VirtualNetworkAzureResource: NewRandomVirtualNetworkAzureResource(), + } +} + +func NewRadomPublicKey() *fabcore.PublicKey { + return &fabcore.PublicKey{ + Exponent: to.Ptr(testhelp.RandomName()), + Modulus: to.Ptr(testhelp.RandomName()), + } +} + +func NewRandomVirtualNetworkAzureResource() *fabcore.VirtualNetworkAzureResource { + return &fabcore.VirtualNetworkAzureResource{ + ResourceGroupName: to.Ptr(testhelp.RandomName()), + SubnetName: to.Ptr(testhelp.RandomName()), + SubscriptionID: to.Ptr(testhelp.RandomUUID()), + VirtualNetworkName: to.Ptr(testhelp.RandomName()), + } +} diff --git a/internal/testhelp/fakes/fake_server.go b/internal/testhelp/fakes/fake_server.go index b6f671e9..58259ba9 100644 --- a/internal/testhelp/fakes/fake_server.go +++ b/internal/testhelp/fakes/fake_server.go @@ -35,6 +35,9 @@ func newFakeServer() *fakeServer { handleEntity(server, configureDomain) handleEntity(server, configureEventhouse) handleEntity(server, configureEnvironment) + handleEntity(server, configureVirtualNetworkGateway) + handleEntity(server, configureOnPremisesGateway) + handleEntity(server, configureOnPremisesGatewayPersonal) handleEntity(server, configureKQLDatabase) handleEntity(server, configureLakehouse) handleEntity(server, configureNotebook) @@ -57,7 +60,12 @@ func handleEntity[TEntity any](server *fakeServer, configureFunction func(server // SupportsType returns true if the server supports the given type. func (s *fakeServer) isSupportedType(t reflect.Type) bool { for _, supportedType := range s.types { - if supportedType == t { + // if supportedType is an interface, check if t implements it + if supportedType.Kind() == reflect.Interface { + if t.Implements(supportedType) { + return true + } + } else if supportedType == t { return true } } @@ -68,7 +76,12 @@ func (s *fakeServer) isSupportedType(t reflect.Type) bool { // Upsert inserts or updates an element in the server. // It panics if the element type is not supported. func (s *fakeServer) Upsert(element any) { - if !s.isSupportedType(reflect.TypeOf(element)) { + elementType := reflect.TypeOf(element) + // if elementType is a pointer, get the underlying type + if elementType.Kind() == reflect.Ptr { + elementType = elementType.Elem() + } + if !s.isSupportedType(elementType) { panic("Unsupported type: " + reflect.TypeOf(element).String() + ". Did you forget to call HandleEntity in NewFakeServer?") // lintignore:R009 } diff --git a/internal/testhelp/fakes/fake_typedhandler.go b/internal/testhelp/fakes/fake_typedhandler.go index 02edc146..7b47d5e0 100644 --- a/internal/testhelp/fakes/fake_typedhandler.go +++ b/internal/testhelp/fakes/fake_typedhandler.go @@ -287,6 +287,12 @@ func (h *typedHandler[TEntity]) entityTypeIsFabricItem() bool { func (h *typedHandler[TEntity]) entityTypeCanBeConvertedToFabricItem() bool { var entity TEntity + // if entity is an interface, return false + entityAsAny := (any)(entity) + if entityAsAny == nil { + return false + } + requiredPropertyNames := []string{"ID", "WorkspaceID", "DisplayName", "Description", "Type"} for _, propertyName := range requiredPropertyNames { diff --git a/internal/testhelp/utils.go b/internal/testhelp/utils.go index 167b1d45..e1fc94e0 100644 --- a/internal/testhelp/utils.go +++ b/internal/testhelp/utils.go @@ -27,12 +27,25 @@ func RandomName(length ...int) string { return acctest.RandStringFromCharSet(size, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") } +// RandomInt returns a random integer between minInt (inclusive) and maxInt (exclusive) +func RandomInt(minInt int, maxInt int) int { + return acctest.RandIntRange(minInt, maxInt) +} + +func RandomBool() bool { + return acctest.RandIntRange(0, 2) == 1 +} + func RandomUUID() string { result, _ := uuid.GenerateUUID() return result } +func RandomElement[T any](elements []T) T { + return elements[RandomInt(0, len(elements))] +} + func RandomURI() string { return fmt.Sprintf("https://%s.com", strings.ToLower(RandomName())) } From c4714b273b97344e176c520017efae45a7825b35 Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Tue, 11 Feb 2025 13:44:40 +0000 Subject: [PATCH 06/14] add docs --- docs/data-sources/gateway.md | 7 +- docs/data-sources/gateway_role_assignments.md | 63 +++++++++++++++ docs/data-sources/gateways.md | 78 +++++++++++++++++++ .../workspace_role_assignments.md | 4 +- docs/resources/gateway.md | 6 +- docs/resources/gateway_role_assignment.md | 43 ++++++++++ 6 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 docs/data-sources/gateway_role_assignments.md create mode 100644 docs/data-sources/gateways.md create mode 100644 docs/resources/gateway_role_assignment.md diff --git a/docs/data-sources/gateway.md b/docs/data-sources/gateway.md index 215d9639..137e7b08 100644 --- a/docs/data-sources/gateway.md +++ b/docs/data-sources/gateway.md @@ -19,10 +19,12 @@ Use this data source to get [Gateway](https://learn.microsoft.com/power-bi/guida ## Schema -### Optional +### Required -- `display_name` (String) The Gateway display name. - `id` (String) The Gateway ID. + +### Optional + - `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) ### Read-Only @@ -30,6 +32,7 @@ Use this data source to get [Gateway](https://learn.microsoft.com/power-bi/guida - `allow_cloud_connection_refresh` (Boolean) Allow cloud connection refresh. - `allow_custom_connectors` (Boolean) Allow custom connectors. - `capacity_id` (String) The Gateway capacity ID. +- `display_name` (String) The Gateway display name. - `inactivity_minutes_before_sleep` (Number) The Gateway inactivity minutes before sleep. Possible values: `120`, `1440`, `150`, `240`, `30`, `360`, `480`, `60`, `720`, `90` - `load_balancing_setting` (String) The load balancing setting. Possible values: `DistributeEvenly`, `Failover` - `number_of_member_gateways` (Number) The number of member gateways. Possible values: 1 to 7. diff --git a/docs/data-sources/gateway_role_assignments.md b/docs/data-sources/gateway_role_assignments.md new file mode 100644 index 00000000..2b50fbcf --- /dev/null +++ b/docs/data-sources/gateway_role_assignments.md @@ -0,0 +1,63 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "fabric_gateway_role_assignments Data Source - terraform-provider-fabric" +subcategory: "" +description: |- + List Fabric Gateway Role Assignments. + Use this data source to list [Gateway Role Assignments]. + -> This item supports Service Principal authentication. +--- + +# fabric_gateway_role_assignments (Data Source) + +List Fabric Gateway Role Assignments. + +Use this data source to list [Gateway Role Assignments]. + +-> This item supports Service Principal authentication. + + +## Schema + +### Required + +- `gateway_id` (String) The Gateway ID. + +### Optional + +- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) + +### Read-Only + +- `values` (Attributes List) The list of Gateway Role Assignments. (see [below for nested schema](#nestedatt--values)) + + + +### Nested Schema for `timeouts` + +Optional: + +- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). + + + +### Nested Schema for `values` + +Read-Only: + +- `details` (Attributes) The principal details. (see [below for nested schema](#nestedatt--values--details)) +- `display_name` (String) The principal's display name. +- `id` (String) The Principal ID. +- `role` (String) The gateway role of the principal. Possible values: `Admin`, `ConnectionCreator`, `ConnectionCreatorWithResharing`. +- `type` (String) The type of the principal. Possible values: `Group`, `ServicePrincipal`, `ServicePrincipalProfile`, `User`. + + + +### Nested Schema for `values.details` + +Read-Only: + +- `app_id` (String) The service principal's Microsoft Entra App ID. +- `group_type` (String) The type of the group. Possible values: `DistributionList`, `SecurityGroup`, `Unknown`. +- `parent_principal_id` (String) The parent principal ID of Service Principal Profile. +- `user_principal_name` (String) The user principal name. diff --git a/docs/data-sources/gateways.md b/docs/data-sources/gateways.md new file mode 100644 index 00000000..87d86e53 --- /dev/null +++ b/docs/data-sources/gateways.md @@ -0,0 +1,78 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "fabric_gateways Data Source - terraform-provider-fabric" +subcategory: "" +description: |- + List a Fabric Gateways. + Use this data source to list Gateways https://learn.microsoft.com/power-bi/guidance/powerbi-implementation-planning-data-gateways. + -> This item supports Service Principal authentication. +--- + +# fabric_gateways (Data Source) + +List a Fabric Gateways. + +Use this data source to list [Gateways](https://learn.microsoft.com/power-bi/guidance/powerbi-implementation-planning-data-gateways). + +-> This item supports Service Principal authentication. + + +## Schema + +### Optional + +- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) + +### Read-Only + +- `values` (Attributes List) The list of Gateways. (see [below for nested schema](#nestedatt--values)) + + + +### Nested Schema for `timeouts` + +Optional: + +- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). + + + +### Nested Schema for `values` + +Required: + +- `id` (String) The Gateway ID. + +Read-Only: + +- `allow_cloud_connection_refresh` (Boolean) Allow cloud connection refresh. +- `allow_custom_connectors` (Boolean) Allow custom connectors. +- `capacity_id` (String) The Gateway capacity ID. +- `display_name` (String) The Gateway display name. +- `inactivity_minutes_before_sleep` (Number) The Gateway inactivity minutes before sleep. Possible values: `120`, `1440`, `150`, `240`, `30`, `360`, `480`, `60`, `720`, `90` +- `load_balancing_setting` (String) The load balancing setting. Possible values: `DistributeEvenly`, `Failover` +- `number_of_member_gateways` (Number) The number of member gateways. Possible values: 1 to 7. +- `public_key` (Attributes) The public key of the primary gateway member. Used to encrypt the credentials for creating and updating connections. (see [below for nested schema](#nestedatt--values--public_key)) +- `type` (String) The Gateway type. Possible values: `OnPremises`, `OnPremisesPersonal`, `VirtualNetwork` +- `version` (String) The Gateway version. +- `virtual_network_azure_resource` (Attributes) The Azure virtual network resource. (see [below for nested schema](#nestedatt--values--virtual_network_azure_resource)) + + + +### Nested Schema for `values.public_key` + +Read-Only: + +- `exponent` (String) The exponent. +- `modulus` (String) The modulus. + + + +### Nested Schema for `values.virtual_network_azure_resource` + +Read-Only: + +- `resource_group_name` (String) The resource group name. +- `subnet_name` (String) The subnet name. +- `subscription_id` (String) The subscription ID. +- `virtual_network_name` (String) The virtual network name. diff --git a/docs/data-sources/workspace_role_assignments.md b/docs/data-sources/workspace_role_assignments.md index f85c6967..a4df7148 100644 --- a/docs/data-sources/workspace_role_assignments.md +++ b/docs/data-sources/workspace_role_assignments.md @@ -3,14 +3,14 @@ page_title: "fabric_workspace_role_assignments Data Source - terraform-provider-fabric" subcategory: "" description: |- - List a Fabric Workspace Role Assignments. + List Fabric Workspace Role Assignments. Use this data source to list Workspace Role Assignments https://learn.microsoft.com/power-bi/collaborate-share/service-roles-new-workspaces. -> This item supports Service Principal authentication. --- # fabric_workspace_role_assignments (Data Source) -List a Fabric Workspace Role Assignments. +List Fabric Workspace Role Assignments. Use this data source to list [Workspace Role Assignments](https://learn.microsoft.com/power-bi/collaborate-share/service-roles-new-workspaces). diff --git a/docs/resources/gateway.md b/docs/resources/gateway.md index 060c47c6..f1c876a0 100644 --- a/docs/resources/gateway.md +++ b/docs/resources/gateway.md @@ -21,16 +21,16 @@ See [Gateways](https://learn.microsoft.com/power-bi/guidance/powerbi-implementat ### Required -- `inactivity_minutes_before_sleep` (Number) The Gateway inactivity minutes before sleep. -- `number_of_member_gateways` (Number) The Gateway number of member gateways. - `type` (String) The Gateway type. Accepted values: `OnPremises`, `OnPremisesPersonal`, `VirtualNetwork` ### Optional - `capacity_id` (String) The Gateway capacity ID. - `display_name` (String) The Gateway display name. +- `inactivity_minutes_before_sleep` (Number) The Gateway inactivity minutes before sleep. +- `number_of_member_gateways` (Number) The Gateway number of member gateways. - `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) -- `virtual_network_azure_resource` (Attributes Set) The Gateway virtual network Azure resource. (see [below for nested schema](#nestedatt--virtual_network_azure_resource)) +- `virtual_network_azure_resource` (Attributes) The Gateway virtual network Azure resource. (see [below for nested schema](#nestedatt--virtual_network_azure_resource)) ### Read-Only diff --git a/docs/resources/gateway_role_assignment.md b/docs/resources/gateway_role_assignment.md new file mode 100644 index 00000000..8457db24 --- /dev/null +++ b/docs/resources/gateway_role_assignment.md @@ -0,0 +1,43 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "fabric_gateway_role_assignment Resource - terraform-provider-fabric" +subcategory: "" +description: |- + Manage a Gateway Role Assignment. + -> This item supports Service Principal authentication. +--- + +# fabric_gateway_role_assignment (Resource) + +Manage a Gateway Role Assignment. + +-> This item supports Service Principal authentication. + + +## Schema + +### Required + +- `gateway_id` (String) The Gateway ID. +- `principal_id` (String) The Principal ID. +- `principal_type` (String) The type of the principal. Accepted values: `Group`, `ServicePrincipal`, `ServicePrincipalProfile`, `User`. +- `role` (String) The Gateway Role of the principal. Accepted values: `Admin`, `ConnectionCreator`, `ConnectionCreatorWithResharing`. + +### Optional + +- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) + +### Read-Only + +- `id` (String) The Gateway Role Assignment ID. + + + +### Nested Schema for `timeouts` + +Optional: + +- `create` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). +- `delete` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Setting a timeout for a Delete operation is only applicable if changes are saved into state before the destroy operation occurs. +- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled. +- `update` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). From 36bd990a2db7fad8270ac098690ecdbd5790ebca Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Wed, 12 Feb 2025 20:03:51 +0000 Subject: [PATCH 07/14] add unit tests and accept reference by id and display name for data source gateway. --- docs/data-sources/gateway.md | 7 +- docs/data-sources/gateway_role_assignments.md | 13 - docs/data-sources/gateways.md | 4 +- internal/services/gateway/data_gateway.go | 91 ++++- .../gateway/data_gateway_role_assignments.go | 58 +-- .../data_gateway_role_assignments_test.go | 42 +-- .../services/gateway/data_gateway_test.go | 176 ++++++--- internal/services/gateway/data_gateways.go | 4 +- .../services/gateway/data_gateways_test.go | 69 ++-- internal/services/gateway/models.go | 2 +- .../services/gateway/models_data_gateway.go | 78 ++-- .../models_data_gateway_role_assignment.go | 48 +-- .../gateway/models_resource_gateway.go | 2 +- .../resource_gateway_role_assignment_test.go | 231 ++++++++++++ .../services/gateway/resource_gateway_test.go | 347 ++++++------------ tools/scripts/Set-WellKnown.ps1 | 269 +++++++++++++- 16 files changed, 960 insertions(+), 481 deletions(-) create mode 100644 internal/services/gateway/resource_gateway_role_assignment_test.go mode change 100644 => 100755 tools/scripts/Set-WellKnown.ps1 diff --git a/docs/data-sources/gateway.md b/docs/data-sources/gateway.md index 137e7b08..215d9639 100644 --- a/docs/data-sources/gateway.md +++ b/docs/data-sources/gateway.md @@ -19,12 +19,10 @@ Use this data source to get [Gateway](https://learn.microsoft.com/power-bi/guida ## Schema -### Required - -- `id` (String) The Gateway ID. - ### Optional +- `display_name` (String) The Gateway display name. +- `id` (String) The Gateway ID. - `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) ### Read-Only @@ -32,7 +30,6 @@ Use this data source to get [Gateway](https://learn.microsoft.com/power-bi/guida - `allow_cloud_connection_refresh` (Boolean) Allow cloud connection refresh. - `allow_custom_connectors` (Boolean) Allow custom connectors. - `capacity_id` (String) The Gateway capacity ID. -- `display_name` (String) The Gateway display name. - `inactivity_minutes_before_sleep` (Number) The Gateway inactivity minutes before sleep. Possible values: `120`, `1440`, `150`, `240`, `30`, `360`, `480`, `60`, `720`, `90` - `load_balancing_setting` (String) The load balancing setting. Possible values: `DistributeEvenly`, `Failover` - `number_of_member_gateways` (Number) The number of member gateways. Possible values: 1 to 7. diff --git a/docs/data-sources/gateway_role_assignments.md b/docs/data-sources/gateway_role_assignments.md index 2b50fbcf..83d79982 100644 --- a/docs/data-sources/gateway_role_assignments.md +++ b/docs/data-sources/gateway_role_assignments.md @@ -45,19 +45,6 @@ Optional: Read-Only: -- `details` (Attributes) The principal details. (see [below for nested schema](#nestedatt--values--details)) -- `display_name` (String) The principal's display name. - `id` (String) The Principal ID. - `role` (String) The gateway role of the principal. Possible values: `Admin`, `ConnectionCreator`, `ConnectionCreatorWithResharing`. - `type` (String) The type of the principal. Possible values: `Group`, `ServicePrincipal`, `ServicePrincipalProfile`, `User`. - - - -### Nested Schema for `values.details` - -Read-Only: - -- `app_id` (String) The service principal's Microsoft Entra App ID. -- `group_type` (String) The type of the group. Possible values: `DistributionList`, `SecurityGroup`, `Unknown`. -- `parent_principal_id` (String) The parent principal ID of Service Principal Profile. -- `user_principal_name` (String) The user principal name. diff --git a/docs/data-sources/gateways.md b/docs/data-sources/gateways.md index 87d86e53..9f909848 100644 --- a/docs/data-sources/gateways.md +++ b/docs/data-sources/gateways.md @@ -39,8 +39,9 @@ Optional: ### Nested Schema for `values` -Required: +Optional: +- `display_name` (String) The Gateway display name. - `id` (String) The Gateway ID. Read-Only: @@ -48,7 +49,6 @@ Read-Only: - `allow_cloud_connection_refresh` (Boolean) Allow cloud connection refresh. - `allow_custom_connectors` (Boolean) Allow custom connectors. - `capacity_id` (String) The Gateway capacity ID. -- `display_name` (String) The Gateway display name. - `inactivity_minutes_before_sleep` (Number) The Gateway inactivity minutes before sleep. Possible values: `120`, `1440`, `150`, `240`, `30`, `360`, `480`, `60`, `720`, `90` - `load_balancing_setting` (String) The load balancing setting. Possible values: `DistributeEvenly`, `Failover` - `number_of_member_gateways` (Number) The number of member gateways. Possible values: 1 to 7. diff --git a/internal/services/gateway/data_gateway.go b/internal/services/gateway/data_gateway.go index bbccf23c..6cf0cbfc 100644 --- a/internal/services/gateway/data_gateway.go +++ b/internal/services/gateway/data_gateway.go @@ -8,9 +8,11 @@ import ( "fmt" "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" + "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" fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" @@ -21,8 +23,10 @@ import ( pconfig "github.com/microsoft/terraform-provider-fabric/internal/provider/config" ) -// _ datasource.DataSourceWithConfigValidators = (*dataSourceGateway)(nil) -var _ datasource.DataSourceWithConfigure = (*dataSourceGateway)(nil) +var ( + _ datasource.DataSourceWithConfigValidators = (*dataSourceGateway)(nil) + _ datasource.DataSourceWithConfigure = (*dataSourceGateway)(nil) +) type dataSourceGateway struct { pConfigData *pconfig.ProviderData @@ -45,11 +49,13 @@ func (d *dataSourceGateway) Schema(ctx context.Context, _ datasource.SchemaReque Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ MarkdownDescription: "The " + ItemName + " ID.", - Required: true, + Optional: true, + Computed: true, CustomType: customtypes.UUIDType{}, }, "display_name": schema.StringAttribute{ MarkdownDescription: "The " + ItemName + " display name.", + Optional: true, Computed: true, }, "type": schema.StringAttribute{ @@ -129,18 +135,18 @@ func (d *dataSourceGateway) Schema(ctx context.Context, _ datasource.SchemaReque } } -// func (d *dataSourceGateway) 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 *dataSourceGateway) 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 *dataSourceGateway) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { if req.ProviderData == nil { @@ -183,7 +189,13 @@ func (d *dataSourceGateway) Read(ctx context.Context, req datasource.ReadRequest ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() - if resp.Diagnostics.Append(d.get(ctx, &data)...); resp.Diagnostics.HasError() { + if data.ID.ValueString() != "" { + diags = d.getByID(ctx, &data) + } else { + diags = d.getByDisplayName(ctx, &data) + } + + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { return } @@ -198,9 +210,9 @@ func (d *dataSourceGateway) Read(ctx context.Context, req datasource.ReadRequest } } -func (d *dataSourceGateway) get(ctx context.Context, model *dataSourceGatewayModel) diag.Diagnostics { +func (d *dataSourceGateway) getByID(ctx context.Context, model *dataSourceGatewayModel) diag.Diagnostics { tflog.Trace(ctx, "GET "+ItemName+" BY ID", map[string]any{ - "id": model.DisplayName.ValueString(), + "id": model.ID.ValueString(), }) respGet, err := d.client.GetGateway(ctx, model.ID.ValueString(), nil) @@ -214,3 +226,46 @@ func (d *dataSourceGateway) get(ctx context.Context, model *dataSourceGatewayMod return nil } + +func (d *dataSourceGateway) getByDisplayName(ctx context.Context, model *dataSourceGatewayModel) diag.Diagnostics { + tflog.Trace(ctx, "GET "+ItemName+" BY DISPLAY NAME", map[string]any{ + "display_name": model.DisplayName.ValueString(), + }) + + var diags diag.Diagnostics + + pager := d.client.NewListGatewaysPager(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 { + gateway := entity.GetGateway() + var entityDisplayName string + + switch *gateway.Type { + case fabcore.GatewayTypeVirtualNetwork: + entityDisplayName = *(entity.(*fabcore.VirtualNetworkGateway).DisplayName) + case fabcore.GatewayTypeOnPremises: + entityDisplayName = *(entity.(*fabcore.OnPremisesGateway).DisplayName) + default: + continue + } + + if entityDisplayName == model.DisplayName.ValueString() { + model.ID = customtypes.NewUUIDPointerValue(entity.GetGateway().ID) + + return d.getByID(ctx, model) + } + } + } + + diags.AddError( + common.ErrorReadHeader, + "Unable to find Gateway with 'display_name': "+model.DisplayName.ValueString(), + ) + + return diags +} diff --git a/internal/services/gateway/data_gateway_role_assignments.go b/internal/services/gateway/data_gateway_role_assignments.go index 1f69c251..43f95ca4 100644 --- a/internal/services/gateway/data_gateway_role_assignments.go +++ b/internal/services/gateway/data_gateway_role_assignments.go @@ -62,39 +62,39 @@ func (d *dataSourceGatewayRoleAssignments) Schema(ctx context.Context, _ datasou MarkdownDescription: "The gateway role of the principal. Possible values: " + utils.ConvertStringSlicesToString(fabcore.PossibleGatewayRoleValues(), true, true) + ".", Computed: true, }, - "display_name": schema.StringAttribute{ - MarkdownDescription: "The principal's display name.", - Computed: true, - }, + // "display_name": schema.StringAttribute{ + // MarkdownDescription: "The principal's display name.", + // Computed: true, + // }, "type": schema.StringAttribute{ MarkdownDescription: "The type of the principal. Possible values: " + utils.ConvertStringSlicesToString(fabcore.PossiblePrincipalTypeValues(), true, true) + ".", Computed: true, }, - "details": schema.SingleNestedAttribute{ - MarkdownDescription: "The principal details.", - Computed: true, - CustomType: supertypes.NewSingleNestedObjectTypeOf[principalDetailsModel](ctx), - Attributes: map[string]schema.Attribute{ - "user_principal_name": schema.StringAttribute{ - MarkdownDescription: "The user principal name.", - Computed: true, - }, - "group_type": schema.StringAttribute{ - MarkdownDescription: "The type of the group. Possible values: " + utils.ConvertStringSlicesToString(fabcore.PossibleGroupTypeValues(), true, true) + ".", - Computed: true, - }, - "app_id": schema.StringAttribute{ - MarkdownDescription: "The service principal's Microsoft Entra App ID.", - Computed: true, - CustomType: customtypes.UUIDType{}, - }, - "parent_principal_id": schema.StringAttribute{ - MarkdownDescription: "The parent principal ID of Service Principal Profile.", - Computed: true, - CustomType: customtypes.UUIDType{}, - }, - }, - }, + // "details": schema.SingleNestedAttribute{ + // MarkdownDescription: "The principal details.", + // Computed: true, + // CustomType: supertypes.NewSingleNestedObjectTypeOf[principalDetailsModel](ctx), + // Attributes: map[string]schema.Attribute{ + // "user_principal_name": schema.StringAttribute{ + // MarkdownDescription: "The user principal name.", + // Computed: true, + // }, + // "group_type": schema.StringAttribute{ + // MarkdownDescription: "The type of the group. Possible values: " + utils.ConvertStringSlicesToString(fabcore.PossibleGroupTypeValues(), true, true) + ".", + // Computed: true, + // }, + // "app_id": schema.StringAttribute{ + // MarkdownDescription: "The service principal's Microsoft Entra App ID.", + // Computed: true, + // CustomType: customtypes.UUIDType{}, + // }, + // "parent_principal_id": schema.StringAttribute{ + // MarkdownDescription: "The parent principal ID of Service Principal Profile.", + // Computed: true, + // CustomType: customtypes.UUIDType{}, + // }, + // }, + // }, }, }, }, diff --git a/internal/services/gateway/data_gateway_role_assignments_test.go b/internal/services/gateway/data_gateway_role_assignments_test.go index 31e85b13..9ed01688 100644 --- a/internal/services/gateway/data_gateway_role_assignments_test.go +++ b/internal/services/gateway/data_gateway_role_assignments_test.go @@ -50,31 +50,31 @@ func TestUnit_GatewayRoleAssignmentsDataSource(t *testing.T) { resource.TestCheckResourceAttr(testDataSourceGatewayRoleAssignments, "gateway_id", gatewayID), resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.id", entity.ID), resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.role", (*string)(entity.Role)), - resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.display_name", entity.Principal.DisplayName), + // resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.display_name", entity.Principal.DisplayName), resource.TestCheckResourceAttrPtr(testDataSourceGatewayRoleAssignments, "values.1.type", (*string)(entity.Principal.Type)), ), }, })) } -// func TestAcc_GatewayRoleAssignmentsDataSource(t *testing.T) { -// gateway := testhelp.WellKnown()["GatewayDS"].(map[string]any) -// gatewayID := gateway["id"].(string) +func TestAcc_GatewayRoleAssignmentsDataSource(t *testing.T) { + gateway := testhelp.WellKnown()["GatewayVirtualNetwork"].(map[string]any) + gatewayID := gateway["id"].(string) -// resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ -// // read -// { -// Config: at.CompileConfig( -// testDataSourceGatewayRoleAssignmentsHeader, -// map[string]any{ -// "gateway_id": gatewayID, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testDataSourceGatewayRoleAssignments, "gateway_id", gatewayID), -// resource.TestCheckResourceAttrSet(testDataSourceGatewayRoleAssignments, "values.0.id"), -// ), -// }, -// }, -// )) -// } + resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ + // read + { + Config: at.CompileConfig( + testDataSourceGatewayRoleAssignmentsHeader, + map[string]any{ + "gateway_id": gatewayID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testDataSourceGatewayRoleAssignments, "gateway_id", gatewayID), + resource.TestCheckResourceAttrSet(testDataSourceGatewayRoleAssignments, "values.0.id"), + ), + }, + }, + )) +} diff --git a/internal/services/gateway/data_gateway_test.go b/internal/services/gateway/data_gateway_test.go index 002cab11..3e929398 100644 --- a/internal/services/gateway/data_gateway_test.go +++ b/internal/services/gateway/data_gateway_test.go @@ -40,7 +40,7 @@ func TestUnit_GatewayDataSource(t *testing.T) { testDataSourceItemHeader, map[string]any{}, ), - ExpectError: regexp.MustCompile(`Missing required argument`), + ExpectError: regexp.MustCompile(`Missing Attribute Configuration`), }, // error - id - invalid UUID { @@ -63,7 +63,7 @@ func TestUnit_GatewayDataSource(t *testing.T) { ), ExpectError: regexp.MustCompile(`An argument named "unexpected_attr" is not expected here`), }, - // read by id + // read by id - virtual network { Config: at.CompileConfig( testDataSourceItemHeader, @@ -84,6 +84,28 @@ func TestUnit_GatewayDataSource(t *testing.T) { resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.virtual_network_name", virtualNetworkGateway.VirtualNetworkAzureResource.VirtualNetworkName), ), }, + // read by name - virtual network + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "display_name": *virtualNetworkGateway.DisplayName, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "id", virtualNetworkGateway.ID), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "type", (*string)(virtualNetworkGateway.Type)), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "display_name", virtualNetworkGateway.DisplayName), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "capacity_id", virtualNetworkGateway.CapacityID), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(*virtualNetworkGateway.InactivityMinutesBeforeSleep))), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(*virtualNetworkGateway.NumberOfMemberGateways))), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.resource_group_name", virtualNetworkGateway.VirtualNetworkAzureResource.ResourceGroupName), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.subnet_name", virtualNetworkGateway.VirtualNetworkAzureResource.SubnetName), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.subscription_id", virtualNetworkGateway.VirtualNetworkAzureResource.SubscriptionID), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.virtual_network_name", virtualNetworkGateway.VirtualNetworkAzureResource.VirtualNetworkName), + ), + }, + // read by id - on premises { Config: at.CompileConfig( testDataSourceItemHeader, @@ -104,6 +126,28 @@ func TestUnit_GatewayDataSource(t *testing.T) { resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "public_key.modulus", onPremisesGateway.PublicKey.Modulus), ), }, + // read by name - on premises + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "display_name": *onPremisesGateway.DisplayName, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "id", onPremisesGateway.ID), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "type", (*string)(onPremisesGateway.Type)), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "display_name", onPremisesGateway.DisplayName), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "allow_cloud_connection_refresh", strconv.FormatBool(*onPremisesGateway.AllowCloudConnectionRefresh)), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "allow_custom_connectors", strconv.FormatBool(*onPremisesGateway.AllowCustomConnectors)), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(*onPremisesGateway.NumberOfMemberGateways))), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "load_balancing_setting", string(*onPremisesGateway.LoadBalancingSetting)), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "version", onPremisesGateway.Version), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "public_key.exponent", onPremisesGateway.PublicKey.Exponent), + resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "public_key.modulus", onPremisesGateway.PublicKey.Modulus), + ), + }, + // read by id - on premises personal { Config: at.CompileConfig( testDataSourceItemHeader, @@ -119,26 +163,41 @@ func TestUnit_GatewayDataSource(t *testing.T) { resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "public_key.modulus", onPremisesGatewayPersonalGateway.PublicKey.Modulus), ), }, + // read by id - not found { Config: at.CompileConfig( testDataSourceItemHeader, map[string]any{ - "id": *virtualNetworkGateway.ID, + "id": testhelp.RandomUUID(), }, ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "id", virtualNetworkGateway.ID), - resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "type", (*string)(virtualNetworkGateway.Type)), - resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "display_name", virtualNetworkGateway.DisplayName), - resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "capacity_id", virtualNetworkGateway.CapacityID), - resource.TestCheckResourceAttr(testDataSourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(*virtualNetworkGateway.InactivityMinutesBeforeSleep))), - resource.TestCheckResourceAttr(testDataSourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(*virtualNetworkGateway.NumberOfMemberGateways))), - resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.resource_group_name", virtualNetworkGateway.VirtualNetworkAzureResource.ResourceGroupName), - resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.subnet_name", virtualNetworkGateway.VirtualNetworkAzureResource.SubnetName), - resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.subscription_id", virtualNetworkGateway.VirtualNetworkAzureResource.SubscriptionID), - resource.TestCheckResourceAttrPtr(testDataSourceItemFQN, "virtual_network_azure_resource.virtual_network_name", virtualNetworkGateway.VirtualNetworkAzureResource.VirtualNetworkName), + ExpectError: regexp.MustCompile(common.ErrorReadHeader), + }, + // read by name - not found + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "display_name": testhelp.RandomUUID(), + }, ), + ExpectError: regexp.MustCompile(common.ErrorReadHeader), }, + })) +} + +func TestAcc_GatewayDataSource(t *testing.T) { + entityVirtualNetwork := testhelp.WellKnown()["GatewayVirtualNetwork"].(map[string]any) + entityVirtualNetworkID := entityVirtualNetwork["id"].(string) + entityVirtualNetworkDisplayName := entityVirtualNetwork["displayName"].(string) + entityVirtualNetworkType := entityVirtualNetwork["type"].(string) + + entityOnPremises := testhelp.WellKnown()["GatewayOnPremises"].(map[string]any) + entityOnPremisesID := entityOnPremises["id"].(string) + entityOnPremisesDisplayName := entityOnPremises["displayName"].(string) + entityOnPremisesType := entityOnPremises["type"].(string) + + resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ // read by id - not found { Config: at.CompileConfig( @@ -149,38 +208,61 @@ func TestUnit_GatewayDataSource(t *testing.T) { ), ExpectError: regexp.MustCompile(common.ErrorReadHeader), }, + // read by id - virtual network + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "id": entityVirtualNetworkID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityVirtualNetworkID), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityVirtualNetworkDisplayName), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "type", entityVirtualNetworkType), + ), + }, + // read by name- virtual network + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "display_name": entityVirtualNetworkDisplayName, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityVirtualNetworkID), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityVirtualNetworkDisplayName), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "type", entityVirtualNetworkType), + ), + }, + // read by id - on premises + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "id": entityOnPremisesID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityOnPremisesID), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityOnPremisesDisplayName), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "type", entityOnPremisesType), + ), + }, + // read by name - on premises + { + Config: at.CompileConfig( + testDataSourceItemHeader, + map[string]any{ + "display_name": entityOnPremisesDisplayName, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityOnPremisesID), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityOnPremisesDisplayName), + resource.TestCheckResourceAttr(testDataSourceItemFQN, "type", entityOnPremisesType), + ), + }, })) } - -// func TestAcc_GatewayDataSource(t *testing.T) { -// entity := testhelp.WellKnown()["GatewayParent"].(map[string]any) -// entityID := entity["id"].(string) -// entityDisplayName := entity["displayName"].(string) -// entityDescription := entity["description"].(string) - -// resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ -// { -// Config: at.CompileConfig( -// testDataSourceItemHeader, -// map[string]any{ -// "id": entityID, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityID), -// resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityDisplayName), -// resource.TestCheckResourceAttr(testDataSourceItemFQN, "description", entityDescription), -// ), -// }, -// // read by id - not found -// { -// Config: at.CompileConfig( -// testDataSourceItemHeader, -// map[string]any{ -// "id": testhelp.RandomUUID(), -// }, -// ), -// ExpectError: regexp.MustCompile(common.ErrorReadHeader), -// }, -// })) -// } diff --git a/internal/services/gateway/data_gateways.go b/internal/services/gateway/data_gateways.go index 04e2136b..52301749 100644 --- a/internal/services/gateway/data_gateways.go +++ b/internal/services/gateway/data_gateways.go @@ -50,11 +50,13 @@ func (d *dataSourceGateways) Schema(ctx context.Context, _ datasource.SchemaRequ Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ MarkdownDescription: "The " + ItemName + " ID.", - Required: true, + Optional: true, + Computed: true, CustomType: customtypes.UUIDType{}, }, "display_name": schema.StringAttribute{ MarkdownDescription: "The " + ItemName + " display name.", + Optional: true, Computed: true, }, "type": schema.StringAttribute{ diff --git a/internal/services/gateway/data_gateways_test.go b/internal/services/gateway/data_gateways_test.go index 49d3a24f..a6b15556 100644 --- a/internal/services/gateway/data_gateways_test.go +++ b/internal/services/gateway/data_gateways_test.go @@ -20,15 +20,15 @@ var ( ) func TestUnit_GatewaysDataSource(t *testing.T) { - // virtualNetworkGateway := fakes.NewRandomVirtualNetworkGateway() - // onPremisesGateway := fakes.NewRandomOnPremisesGateway() - // onPremisesGatewayPersonalGateway := fakes.NewRandomOnPremisesGatewayPersonal() + virtualNetworkGateway := fakes.NewRandomVirtualNetworkGateway() + onPremisesGateway := fakes.NewRandomOnPremisesGateway() + onPremisesGatewayPersonalGateway := fakes.NewRandomOnPremisesGatewayPersonal() - // fakes.FakeServer.Upsert(fakes.NewRandomGateway()) - // fakes.FakeServer.Upsert(virtualNetworkGateway) - // fakes.FakeServer.Upsert(onPremisesGateway) - // fakes.FakeServer.Upsert(onPremisesGatewayPersonalGateway) - // fakes.FakeServer.Upsert(fakes.NewRandomGateway()) + fakes.FakeServer.Upsert(fakes.NewRandomGateway()) + fakes.FakeServer.Upsert(virtualNetworkGateway) + fakes.FakeServer.Upsert(onPremisesGateway) + fakes.FakeServer.Upsert(onPremisesGatewayPersonalGateway) + fakes.FakeServer.Upsert(fakes.NewRandomGateway()) resource.ParallelTest(t, testhelp.NewTestUnitCase(t, nil, fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ // error - unexpected_attr @@ -42,32 +42,33 @@ func TestUnit_GatewaysDataSource(t *testing.T) { ExpectError: regexp.MustCompile(`An argument named "unexpected_attr" is not expected here`), }, // read - // { - // Config: at.CompileConfig( - // testDataSourceItemsHeader, - // map[string]any{}, - // ), - // Check: resource.ComposeAggregateTestCheckFunc( - // resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.0.id"), - // resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.2.id"), - // resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.3.id"), - // ), - // }, + { + Config: at.CompileConfig( + testDataSourceItemsHeader, + map[string]any{}, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.1.id"), + resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.2.id"), + resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.3.id"), + ), + }, })) } -// func TestAcc_GatewaysDataSource(t *testing.T) { -// resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ -// // read -// { -// Config: at.CompileConfig( -// testDataSourceItemsHeader, -// map[string]any{}, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.0.id"), -// ), -// }, -// }, -// )) -// } +func TestAcc_GatewaysDataSource(t *testing.T) { + resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ + // read + { + Config: at.CompileConfig( + testDataSourceItemsHeader, + map[string]any{}, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.0.id"), + resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.1.id"), + ), + }, + }, + )) +} diff --git a/internal/services/gateway/models.go b/internal/services/gateway/models.go index e19d6518..2c405361 100644 --- a/internal/services/gateway/models.go +++ b/internal/services/gateway/models.go @@ -17,7 +17,7 @@ type virtualNetworkAzureResourceModel struct { VirtualNetworkName types.String `tfsdk:"virtual_network_name"` // Rename to just 'name' or 'display_name'? } -func (to *virtualNetworkAzureResourceModel) set(from *fabcore.VirtualNetworkAzureResource) { +func (to *virtualNetworkAzureResourceModel) set(from fabcore.VirtualNetworkAzureResource) { to.ResourceGroupName = types.StringPointerValue(from.ResourceGroupName) to.SubnetName = types.StringPointerValue(from.SubnetName) to.SubscriptionID = customtypes.NewUUIDPointerValue(from.SubscriptionID) diff --git a/internal/services/gateway/models_data_gateway.go b/internal/services/gateway/models_data_gateway.go index ac9f9884..f5b26013 100644 --- a/internal/services/gateway/models_data_gateway.go +++ b/internal/services/gateway/models_data_gateway.go @@ -40,19 +40,25 @@ type baseDataSourceGatewayModel struct { func (to *baseDataSourceGatewayModel) set(ctx context.Context, from fabcore.GatewayClassification) diag.Diagnostics { var diags diag.Diagnostics - switch gateway := from.(type) { - case *fabcore.VirtualNetworkGateway: - to.ID = customtypes.NewUUIDPointerValue(gateway.ID) - to.Type = types.StringPointerValue((*string)(gateway.Type)) - to.DisplayName = types.StringPointerValue(gateway.DisplayName) - to.CapacityID = customtypes.NewUUIDPointerValue(gateway.CapacityID) - to.InactivityMinutesBeforeSleep = types.Int32PointerValue(gateway.InactivityMinutesBeforeSleep) - to.NumberOfMemberGateways = types.Int32PointerValue(gateway.NumberOfMemberGateways) - - virtualNetworkAzureResource := supertypes.NewSingleNestedObjectValueOfNull[virtualNetworkAzureResourceModel](ctx) - if gateway.VirtualNetworkAzureResource != nil { + virtualNetworkAzureResource := supertypes.NewSingleNestedObjectValueOfNull[virtualNetworkAzureResourceModel](ctx) + publicKey := supertypes.NewSingleNestedObjectValueOfNull[publicKeyModel](ctx) + + gw := from.GetGateway() + to.ID = customtypes.NewUUIDPointerValue(gw.ID) + to.Type = types.StringPointerValue((*string)(gw.Type)) + + switch *gw.Type { + case fabcore.GatewayTypeVirtualNetwork: + entity := from.(*fabcore.VirtualNetworkGateway) + + to.DisplayName = types.StringPointerValue(entity.DisplayName) + to.CapacityID = customtypes.NewUUIDPointerValue(entity.CapacityID) + to.InactivityMinutesBeforeSleep = types.Int32PointerValue(entity.InactivityMinutesBeforeSleep) + to.NumberOfMemberGateways = types.Int32PointerValue(entity.NumberOfMemberGateways) + + if entity.VirtualNetworkAzureResource != nil { virtualNetworkAzureResourceModel := &virtualNetworkAzureResourceModel{} - virtualNetworkAzureResourceModel.set(gateway.VirtualNetworkAzureResource) + virtualNetworkAzureResourceModel.set(*entity.VirtualNetworkAzureResource) if diags := virtualNetworkAzureResource.Set(ctx, virtualNetworkAzureResourceModel); diags.HasError() { return diags @@ -61,20 +67,21 @@ func (to *baseDataSourceGatewayModel) set(ctx context.Context, from fabcore.Gate to.VirtualNetworkAzureResource = virtualNetworkAzureResource - case *fabcore.OnPremisesGateway: - to.ID = customtypes.NewUUIDPointerValue(gateway.ID) - to.Type = types.StringPointerValue((*string)(gateway.Type)) - to.DisplayName = types.StringPointerValue(gateway.DisplayName) - to.NumberOfMemberGateways = types.Int32PointerValue(gateway.NumberOfMemberGateways) - to.AllowCloudConnectionRefresh = types.BoolPointerValue(gateway.AllowCloudConnectionRefresh) - to.AllowCustomConnectors = types.BoolPointerValue(gateway.AllowCustomConnectors) - to.LoadBalancingSetting = types.StringPointerValue((*string)(gateway.LoadBalancingSetting)) - to.Version = types.StringPointerValue(gateway.Version) - - publicKey := supertypes.NewSingleNestedObjectValueOfNull[publicKeyModel](ctx) - if gateway.PublicKey != nil { + to.PublicKey = publicKey + + case fabcore.GatewayTypeOnPremises: + entity := from.(*fabcore.OnPremisesGateway) + + to.DisplayName = types.StringPointerValue(entity.DisplayName) + to.NumberOfMemberGateways = types.Int32PointerValue(entity.NumberOfMemberGateways) + to.AllowCloudConnectionRefresh = types.BoolPointerValue(entity.AllowCloudConnectionRefresh) + to.AllowCustomConnectors = types.BoolPointerValue(entity.AllowCustomConnectors) + to.LoadBalancingSetting = types.StringPointerValue((*string)(entity.LoadBalancingSetting)) + to.Version = types.StringPointerValue(entity.Version) + + if entity.PublicKey != nil { publicKeyModel := &publicKeyModel{} - publicKeyModel.set(gateway.PublicKey) + publicKeyModel.set(*entity.PublicKey) if diags := publicKey.Set(ctx, publicKeyModel); diags.HasError() { return diags @@ -83,15 +90,16 @@ func (to *baseDataSourceGatewayModel) set(ctx context.Context, from fabcore.Gate to.PublicKey = publicKey - case *fabcore.OnPremisesGatewayPersonal: - to.ID = customtypes.NewUUIDPointerValue(gateway.ID) - to.Type = types.StringPointerValue((*string)(gateway.Type)) - to.Version = types.StringPointerValue(gateway.Version) + to.VirtualNetworkAzureResource = virtualNetworkAzureResource + + case fabcore.GatewayTypeOnPremisesPersonal: + entity := from.(*fabcore.OnPremisesGatewayPersonal) - publicKey := supertypes.NewSingleNestedObjectValueOfNull[publicKeyModel](ctx) - if gateway.PublicKey != nil { + to.Version = types.StringPointerValue(entity.Version) + + if entity.PublicKey != nil { publicKeyModel := &publicKeyModel{} - publicKeyModel.set(gateway.PublicKey) + publicKeyModel.set(*entity.PublicKey) if diags := publicKey.Set(ctx, publicKeyModel); diags.HasError() { return diags @@ -100,8 +108,10 @@ func (to *baseDataSourceGatewayModel) set(ctx context.Context, from fabcore.Gate to.PublicKey = publicKey + to.VirtualNetworkAzureResource = virtualNetworkAzureResource + default: - diags.AddError("Unsupported Gateway type", fmt.Sprintf("The Gateway type '%T' is not supported.", gateway)) + diags.AddError("Unsupported Gateway type", fmt.Sprintf("The Gateway type '%s' is not supported.", (string)(*gw.Type))) return diags } @@ -114,7 +124,7 @@ type publicKeyModel struct { Modulus types.String `tfsdk:"modulus"` } -func (to *publicKeyModel) set(from *fabcore.PublicKey) { +func (to *publicKeyModel) set(from fabcore.PublicKey) { to.Exponent = types.StringPointerValue(from.Exponent) to.Modulus = types.StringPointerValue(from.Modulus) } diff --git a/internal/services/gateway/models_data_gateway_role_assignment.go b/internal/services/gateway/models_data_gateway_role_assignment.go index 8b5e4f95..b717030b 100644 --- a/internal/services/gateway/models_data_gateway_role_assignment.go +++ b/internal/services/gateway/models_data_gateway_role_assignment.go @@ -27,7 +27,7 @@ func (to *dataSourceGatewayRoleAssignmentsModel) setValues(ctx context.Context, for _, entity := range from { var entityModel gatewayRoleAssignmentModel - if diags := entityModel.set(ctx, entity); diags.HasError() { + if diags := entityModel.set(entity); diags.HasError() { return diags } @@ -38,51 +38,15 @@ func (to *dataSourceGatewayRoleAssignmentsModel) setValues(ctx context.Context, } type gatewayRoleAssignmentModel struct { - ID customtypes.UUID `tfsdk:"id"` - Role types.String `tfsdk:"role"` - DisplayName types.String `tfsdk:"display_name"` - Type types.String `tfsdk:"type"` - Details supertypes.SingleNestedObjectValueOf[principalDetailsModel] `tfsdk:"details"` + ID customtypes.UUID `tfsdk:"id"` + Role types.String `tfsdk:"role"` + Type types.String `tfsdk:"type"` } -func (to *gatewayRoleAssignmentModel) set(ctx context.Context, from fabcore.GatewayRoleAssignment) diag.Diagnostics { +func (to *gatewayRoleAssignmentModel) set(from fabcore.GatewayRoleAssignment) diag.Diagnostics { to.ID = customtypes.NewUUIDPointerValue(from.ID) to.Role = types.StringPointerValue((*string)(from.Role)) - - detailsModel := &principalDetailsModel{} - detailsModel.set(from.Principal, to) - - if diags := to.Details.Set(ctx, detailsModel); diags.HasError() { - return diags - } + to.Type = types.StringPointerValue((*string)(from.Principal.Type)) return nil } - -type principalDetailsModel struct { - UserPrincipalName types.String `tfsdk:"user_principal_name"` - GroupType types.String `tfsdk:"group_type"` - AppID customtypes.UUID `tfsdk:"app_id"` - ParentPrincipalID customtypes.UUID `tfsdk:"parent_principal_id"` -} - -func (to *principalDetailsModel) set(from *fabcore.Principal, roleAssignment *gatewayRoleAssignmentModel) { - to.UserPrincipalName = types.StringNull() - to.GroupType = types.StringNull() - to.AppID = customtypes.NewUUIDNull() - to.ParentPrincipalID = customtypes.NewUUIDNull() - - roleAssignment.DisplayName = types.StringPointerValue(from.DisplayName) - roleAssignment.Type = types.StringPointerValue((*string)(from.Type)) - - switch *from.Type { - case fabcore.PrincipalTypeUser: - to.UserPrincipalName = types.StringPointerValue(from.UserDetails.UserPrincipalName) - case fabcore.PrincipalTypeGroup: - to.GroupType = types.StringPointerValue((*string)(from.GroupDetails.GroupType)) - case fabcore.PrincipalTypeServicePrincipal: - to.AppID = customtypes.NewUUIDPointerValue(from.ServicePrincipalDetails.AADAppID) - case fabcore.PrincipalTypeServicePrincipalProfile: - to.ParentPrincipalID = customtypes.NewUUIDPointerValue(from.ServicePrincipalProfileDetails.ParentPrincipal.ID) - } -} diff --git a/internal/services/gateway/models_resource_gateway.go b/internal/services/gateway/models_resource_gateway.go index ccf5d4b8..0f584720 100644 --- a/internal/services/gateway/models_resource_gateway.go +++ b/internal/services/gateway/models_resource_gateway.go @@ -47,7 +47,7 @@ func (to *baseResourceGatewayModel) set(ctx context.Context, from fabcore.Gatewa virtualNetworkAzureResource := supertypes.NewSingleNestedObjectValueOfNull[virtualNetworkAzureResourceModel](ctx) if gateway.VirtualNetworkAzureResource != nil { virtualNetworkAzureResourceModel := &virtualNetworkAzureResourceModel{} - virtualNetworkAzureResourceModel.set(gateway.VirtualNetworkAzureResource) + virtualNetworkAzureResourceModel.set(*gateway.VirtualNetworkAzureResource) if diags := virtualNetworkAzureResource.Set(ctx, virtualNetworkAzureResourceModel); diags.HasError() { return diags diff --git a/internal/services/gateway/resource_gateway_role_assignment_test.go b/internal/services/gateway/resource_gateway_role_assignment_test.go new file mode 100644 index 00000000..2332cb5c --- /dev/null +++ b/internal/services/gateway/resource_gateway_role_assignment_test.go @@ -0,0 +1,231 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MPL-2.0 + +package gateway_test + +import ( + "fmt" + "regexp" + "testing" + + at "github.com/dcarbone/terraform-plugin-framework-utils/v3/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" + + "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" + "github.com/microsoft/terraform-provider-fabric/internal/services/gateway" + "github.com/microsoft/terraform-provider-fabric/internal/testhelp" + "github.com/microsoft/terraform-provider-fabric/internal/testhelp/fakes" +) + +var ( + testResourceGatewayRoleAssignment = testhelp.ResourceFQN("fabric", gatewayRoleAssignmentTFName, "test") + testResourceGatewayRoleAssignmentHeader = at.ResourceHeader(testhelp.TypeName("fabric", gatewayRoleAssignmentTFName), "test") +) + +func TestUnit_GatewayRoleAssignmentResource_Attributes(t *testing.T) { + resource.ParallelTest(t, testhelp.NewTestUnitCase(t, &testResourceGatewayRoleAssignment, fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ + // error - no required attributes - gateway_id + { + ResourceName: testResourceGatewayRoleAssignment, + Config: at.CompileConfig( + testResourceGatewayRoleAssignmentHeader, + map[string]any{ + "principal_id": "00000000-0000-0000-0000-000000000000", + "principal_type": "User", + "role": "ConnectionCreatorWithResharing", + }, + ), + ExpectError: regexp.MustCompile(`The argument "gateway_id" is required, but no definition was found.`), + }, + // error - no required attributes - principal_id + { + ResourceName: testResourceGatewayRoleAssignment, + Config: at.CompileConfig( + testResourceGatewayRoleAssignmentHeader, + map[string]any{ + "gateway_id": "00000000-0000-0000-0000-000000000000", + "principal_type": "User", + "role": "ConnectionCreatorWithResharing", + }, + ), + ExpectError: regexp.MustCompile(`The argument "principal_id" is required, but no definition was found.`), + }, + // error - no required attributes - principal_type + { + ResourceName: testResourceGatewayRoleAssignment, + Config: at.CompileConfig( + testResourceGatewayRoleAssignmentHeader, + map[string]any{ + "gateway_id": "00000000-0000-0000-0000-000000000000", + "principal_id": "00000000-0000-0000-0000-000000000000", + "role": "ConnectionCreatorWithResharing", + }, + ), + ExpectError: regexp.MustCompile(`The argument "principal_type" is required, but no definition was found.`), + }, + // error - no required attributes - role + { + ResourceName: testResourceGatewayRoleAssignment, + Config: at.CompileConfig( + testResourceGatewayRoleAssignmentHeader, + map[string]any{ + "gateway_id": "00000000-0000-0000-0000-000000000000", + "principal_id": "00000000-0000-0000-0000-000000000000", + "principal_type": "User", + }, + ), + ExpectError: regexp.MustCompile(`The argument "role" is required, but no definition was found.`), + }, + // error - invalid UUID - gateway_id + { + ResourceName: testResourceGatewayRoleAssignment, + Config: at.CompileConfig( + testResourceGatewayRoleAssignmentHeader, + map[string]any{ + "gateway_id": "invalid uuid", + "principal_id": "00000000-0000-0000-0000-000000000000", + "principal_type": "User", + "role": "ConnectionCreatorWithResharing", + }, + ), + ExpectError: regexp.MustCompile(customtypes.UUIDTypeErrorInvalidStringHeader), + }, + // error - invalid UUID - principal_id + { + ResourceName: testResourceGatewayRoleAssignment, + Config: at.CompileConfig( + testResourceGatewayRoleAssignmentHeader, + map[string]any{ + "gateway_id": "00000000-0000-0000-0000-000000000000", + "principal_id": "invalid uuid", + "principal_type": "User", + "role": "ConnectionCreatorWithResharing", + }, + ), + ExpectError: regexp.MustCompile(customtypes.UUIDTypeErrorInvalidStringHeader), + }, + })) +} + +func TestUnit_GatewayRoleAssignmentResource_ImportState(t *testing.T) { + testCase := at.CompileConfig( + testResourceGatewayRoleAssignmentHeader, + map[string]any{}, + ) + + resource.ParallelTest(t, testhelp.NewTestUnitCase(t, &testResourceGatewayRoleAssignment, fakes.FakeServer.ServerFactory, nil, []resource.TestStep{ + { + ResourceName: testResourceGatewayRoleAssignment, + Config: testCase, + ImportStateId: "not-valid", + ImportState: true, + ExpectError: regexp.MustCompile("GatewayID/GatewayRoleAssignmentID"), + }, + { + ResourceName: testResourceGatewayRoleAssignment, + Config: testCase, + ImportStateId: "test/id", + ImportState: true, + ExpectError: regexp.MustCompile(customtypes.UUIDTypeErrorInvalidStringHeader), + }, + { + ResourceName: testResourceGatewayRoleAssignment, + Config: testCase, + ImportStateId: fmt.Sprintf("%s/%s", "test", "00000000-0000-0000-0000-000000000000"), + ImportState: true, + ExpectError: regexp.MustCompile(customtypes.UUIDTypeErrorInvalidStringHeader), + }, + { + ResourceName: testResourceGatewayRoleAssignment, + Config: testCase, + ImportStateId: fmt.Sprintf("%s/%s", "00000000-0000-0000-0000-000000000000", "test"), + ImportState: true, + ExpectError: regexp.MustCompile(customtypes.UUIDTypeErrorInvalidStringHeader), + }, + })) +} + +func TestAcc_GatewayRoleAssignmentResource_CRUD(t *testing.T) { + gatewayType := string(fabcore.GatewayTypeVirtualNetwork) + gatewayCreateDisplayName := testhelp.RandomName() + gatewayCreateInactivityMinutesBeforeSleep := int(testhelp.RandomElement(gateway.PossibleInactivityMinutesBeforeSleepValues)) + gatewayCreateNumberOfMemberGateways := testhelp.RandomInt(int(gateway.MinNumberOfMemberGatewaysValues), int(gateway.MaxNumberOfMemberGatewaysValues)) + + capacity := testhelp.WellKnown()["Capacity"].(map[string]any) + capacityID := capacity["id"].(string) + + virtualNetworkAzureResource := testhelp.WellKnown()["VirtualNetwork01"].(map[string]any) + VirtualNetworkName := virtualNetworkAzureResource["name"].(string) + ResourceGroupName := virtualNetworkAzureResource["resourceGroupName"].(string) + SubnetName := virtualNetworkAzureResource["subnetName"].(string) + SubscriptionID := virtualNetworkAzureResource["subscriptionId"].(string) + + gatewayResourceHCL := at.CompileConfig( + at.ResourceHeader(testhelp.TypeName("fabric", itemTFName), "test"), + map[string]any{ + "type": gatewayType, + "display_name": gatewayCreateDisplayName, + "inactivity_minutes_before_sleep": gatewayCreateInactivityMinutesBeforeSleep, + "number_of_member_gateways": gatewayCreateNumberOfMemberGateways, + "virtual_network_azure_resource": map[string]any{ + "virtual_network_name": VirtualNetworkName, + "resource_group_name": ResourceGroupName, + "subnet_name": SubnetName, + "subscription_id": SubscriptionID, + }, + "capacity_id": capacityID, + }, + ) + gatewayResourceFQN := testhelp.ResourceFQN("fabric", itemTFName, "test") + + entity := testhelp.WellKnown()["Principal"].(map[string]any) + entityID := entity["id"].(string) + entityType := entity["type"].(string) + + resource.Test(t, testhelp.NewTestAccCase(t, &testResourceGatewayRoleAssignment, nil, []resource.TestStep{ + // Create and Read + { + ResourceName: testResourceGatewayRoleAssignment, + Config: at.JoinConfigs( + gatewayResourceHCL, + at.CompileConfig( + testResourceGatewayRoleAssignmentHeader, + map[string]any{ + "gateway_id": testhelp.RefByFQN(gatewayResourceFQN, "id"), + "principal_id": entityID, + "principal_type": entityType, + "role": "ConnectionCreatorWithResharing", + }, + ), + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "principal_id", entityID), + resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "principal_type", entityType), + resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "role", "ConnectionCreatorWithResharing"), + ), + }, + // Update and Read + { + ResourceName: testResourceGatewayRoleAssignment, + Config: at.JoinConfigs( + gatewayResourceHCL, + at.CompileConfig( + testResourceGatewayRoleAssignmentHeader, + map[string]any{ + "gateway_id": testhelp.RefByFQN(gatewayResourceFQN, "id"), + "principal_id": entityID, + "principal_type": entityType, + "role": "ConnectionCreator", + }, + ), + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "principal_id", entityID), + resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "principal_type", entityType), + resource.TestCheckResourceAttr(testResourceGatewayRoleAssignment, "role", "ConnectionCreator"), + ), + }, + })) +} diff --git a/internal/services/gateway/resource_gateway_test.go b/internal/services/gateway/resource_gateway_test.go index 4689b18e..67214157 100644 --- a/internal/services/gateway/resource_gateway_test.go +++ b/internal/services/gateway/resource_gateway_test.go @@ -426,238 +426,125 @@ func TestUnit_GatewayResource_CRUD(t *testing.T) { })) } -// func TestAcc_GatewayResource_CRUD(t *testing.T) { -// capacity := testhelp.WellKnown()["Capacity"].(map[string]any) -// capacityID := capacity["id"].(string) +func TestAcc_GatewayResource_CRUD(t *testing.T) { + entityType := string(fabcore.GatewayTypeVirtualNetwork) + entityCreateDisplayName := testhelp.RandomName() + entityCreateInactivityMinutesBeforeSleep := int(testhelp.RandomElement(gateway.PossibleInactivityMinutesBeforeSleepValues)) + entityCreateNumberOfMemberGateways := testhelp.RandomInt(int(gateway.MinNumberOfMemberGatewaysValues), int(gateway.MaxNumberOfMemberGatewaysValues)) -// entityCreateDisplayName := testhelp.RandomName() -// entityUpdateDisplayName := testhelp.RandomName() -// entityUpdateDescription := testhelp.RandomName() + capacity := testhelp.WellKnown()["Capacity"].(map[string]any) + capacityID := capacity["id"].(string) -// resource.Test(t, testhelp.NewTestAccCase(t, &testResourceItemFQN, nil, []resource.TestStep{ -// // Create and Read -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityCreateDisplayName, -// "capacity_id": capacityID, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityCreateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", ""), -// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), -// ), -// }, -// // Update and Read -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityUpdateDisplayName, -// "description": entityUpdateDescription, -// "capacity_id": capacityID, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), -// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), -// ), -// }, -// // Update - unassign capacity -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityUpdateDisplayName, -// "description": entityUpdateDescription, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), -// resource.TestCheckNoResourceAttr(testResourceItemFQN, "capacity_id"), -// ), -// }, -// // Update - assign capacity -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityUpdateDisplayName, -// "description": entityUpdateDescription, -// "capacity_id": capacityID, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), -// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), -// ), -// }, -// // Update - assign identity -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityUpdateDisplayName, -// "description": entityUpdateDescription, -// "capacity_id": capacityID, -// "identity": map[string]any{ -// "type": "SystemAssigned", -// }, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), -// resource.TestCheckResourceAttrSet(testResourceItemFQN, "identity.application_id"), -// ), -// }, -// // Update - unassign identity -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityUpdateDisplayName, -// "description": entityUpdateDescription, -// "capacity_id": capacityID, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), -// resource.TestCheckNoResourceAttr(testResourceItemFQN, "identity"), -// ), -// }, -// }, -// )) -// } + virtualNetworkAzureResource01 := testhelp.WellKnown()["VirtualNetwork01"].(map[string]any) + vNET01VirtualNetworkName := virtualNetworkAzureResource01["name"].(string) + vNET01ResourceGroupName := virtualNetworkAzureResource01["resourceGroupName"].(string) + vNET01SubnetName := virtualNetworkAzureResource01["subnetName"].(string) + vNET01SubscriptionID := virtualNetworkAzureResource01["subscriptionId"].(string) -// func TestAcc_GatewayResource_Identity_CRUD(t *testing.T) { -// capacity := testhelp.WellKnown()["Capacity"].(map[string]any) -// capacityID := capacity["id"].(string) + entityUpdateDisplayName := testhelp.RandomName() + entityUpdateInactivityMinutesBeforeSleep := int(testhelp.RandomElement(gateway.PossibleInactivityMinutesBeforeSleepValues)) + entityUpdateNumberOfMemberGateways := testhelp.RandomInt(int(gateway.MinNumberOfMemberGatewaysValues), int(gateway.MaxNumberOfMemberGatewaysValues)) -// entityCreateDisplayName := testhelp.RandomName() -// entityUpdateDisplayName := testhelp.RandomName() -// entityUpdateDescription := testhelp.RandomName() + virtualNetworkAzureResource02 := testhelp.WellKnown()["VirtualNetwork02"].(map[string]any) + vNET02VirtualNetworkName := virtualNetworkAzureResource02["name"].(string) + vNET02ResourceGroupName := virtualNetworkAzureResource02["resourceGroupName"].(string) + vNET02SubnetName := virtualNetworkAzureResource02["subnetName"].(string) + vNET02SubscriptionID := virtualNetworkAzureResource02["subscriptionId"].(string) -// resource.Test(t, testhelp.NewTestAccCase(t, &testResourceItemFQN, nil, []resource.TestStep{ -// // Create and Read -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityCreateDisplayName, -// "capacity_id": capacityID, -// "identity": map[string]any{ -// "type": "SystemAssigned", -// }, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityCreateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", ""), -// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), -// resource.TestCheckResourceAttrSet(testResourceItemFQN, "identity.application_id"), -// ), -// }, -// // Update and Read -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityUpdateDisplayName, -// "description": entityUpdateDescription, -// "capacity_id": capacityID, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), -// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), -// ), -// }, -// // Update - unassign capacity -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityUpdateDisplayName, -// "description": entityUpdateDescription, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), -// resource.TestCheckNoResourceAttr(testResourceItemFQN, "capacity_id"), -// ), -// }, -// // Update - assign capacity -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityUpdateDisplayName, -// "description": entityUpdateDescription, -// "capacity_id": capacityID, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), -// resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), -// ), -// }, -// // Update - unassign identity -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityUpdateDisplayName, -// "description": entityUpdateDescription, -// "capacity_id": capacityID, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), -// resource.TestCheckNoResourceAttr(testResourceItemFQN, "identity"), -// ), -// }, -// // Update - assign identity -// { -// ResourceName: testResourceItemFQN, -// Config: at.CompileConfig( -// testResourceItemHeader, -// map[string]any{ -// "display_name": entityUpdateDisplayName, -// "description": entityUpdateDescription, -// "capacity_id": capacityID, -// "identity": map[string]any{ -// "type": "SystemAssigned", -// }, -// }, -// ), -// Check: resource.ComposeAggregateTestCheckFunc( -// resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), -// resource.TestCheckResourceAttr(testResourceItemFQN, "description", entityUpdateDescription), -// resource.TestCheckResourceAttrSet(testResourceItemFQN, "identity.application_id"), -// ), -// }, -// }, -// )) -// } + resource.Test(t, testhelp.NewTestAccCase(t, &testResourceItemFQN, nil, []resource.TestStep{ + // Create and Read + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": entityType, + "display_name": entityCreateDisplayName, + "inactivity_minutes_before_sleep": entityCreateInactivityMinutesBeforeSleep, + "number_of_member_gateways": entityCreateNumberOfMemberGateways, + "virtual_network_azure_resource": map[string]any{ + "virtual_network_name": vNET01VirtualNetworkName, + "resource_group_name": vNET01ResourceGroupName, + "subnet_name": vNET01SubnetName, + "subscription_id": vNET01SubscriptionID, + }, + "capacity_id": capacityID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceItemFQN, "type", entityType), + resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityCreateDisplayName), + resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(entityCreateInactivityMinutesBeforeSleep))), + resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(entityCreateNumberOfMemberGateways))), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.virtual_network_name", vNET01VirtualNetworkName), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.resource_group_name", vNET01ResourceGroupName), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.subnet_name", vNET01SubnetName), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.subscription_id", vNET01SubscriptionID), + resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), + ), + }, + // Update and Read - no replacement + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": entityType, + "display_name": entityUpdateDisplayName, + "inactivity_minutes_before_sleep": entityUpdateInactivityMinutesBeforeSleep, + "number_of_member_gateways": entityUpdateNumberOfMemberGateways, + "virtual_network_azure_resource": map[string]any{ + "virtual_network_name": vNET01VirtualNetworkName, + "resource_group_name": vNET01ResourceGroupName, + "subnet_name": vNET01SubnetName, + "subscription_id": vNET01SubscriptionID, + }, + "capacity_id": capacityID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceItemFQN, "type", entityType), + resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), + resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(entityUpdateInactivityMinutesBeforeSleep))), + resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(entityUpdateNumberOfMemberGateways))), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.virtual_network_name", vNET01VirtualNetworkName), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.resource_group_name", vNET01ResourceGroupName), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.subnet_name", vNET01SubnetName), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.subscription_id", vNET01SubscriptionID), + resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), + ), + }, + // Update and Read - with replacement + { + ResourceName: testResourceItemFQN, + Config: at.CompileConfig( + testResourceItemHeader, + map[string]any{ + "type": entityType, + "display_name": entityUpdateDisplayName, + "inactivity_minutes_before_sleep": entityUpdateInactivityMinutesBeforeSleep, + "number_of_member_gateways": entityUpdateNumberOfMemberGateways, + "virtual_network_azure_resource": map[string]any{ + "virtual_network_name": vNET02VirtualNetworkName, + "resource_group_name": vNET02ResourceGroupName, + "subnet_name": vNET02SubnetName, + "subscription_id": vNET02SubscriptionID, + }, + "capacity_id": capacityID, + }, + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(testResourceItemFQN, "type", entityType), + resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), + resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(entityUpdateInactivityMinutesBeforeSleep))), + resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(entityUpdateNumberOfMemberGateways))), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.virtual_network_name", vNET02VirtualNetworkName), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.resource_group_name", vNET02ResourceGroupName), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.subnet_name", vNET02SubnetName), + resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.subscription_id", vNET02SubscriptionID), + resource.TestCheckResourceAttr(testResourceItemFQN, "capacity_id", capacityID), + ), + }, + }, + )) +} diff --git a/tools/scripts/Set-WellKnown.ps1 b/tools/scripts/Set-WellKnown.ps1 old mode 100644 new mode 100755 index 45f4819b..63cacdf9 --- a/tools/scripts/Set-WellKnown.ps1 +++ b/tools/scripts/Set-WellKnown.ps1 @@ -474,8 +474,187 @@ function Set-FabricWorkspaceRoleAssignment { } } +function Set-FabricDomain { + param ( + [Parameter(Mandatory = $true)] + [string]$DisplayName, + + [Parameter(Mandatory = $false)] + [string]$ParentDomainId + ) + + $results = Invoke-FabricRest -Method 'GET' -Endpoint "admin/domains" + $result = $results.Response.domains | Where-Object { $_.displayName -eq $DisplayName } + if (!$result) { + Write-Log -Message "Creating Domain: $DisplayName" -Level 'WARN' + $payload = @{ + displayName = $DisplayName + description = $DisplayName + } + + if ($ParentDomainId) { + $payload['parentDomainId'] = $ParentDomainId + } + + $result = (Invoke-FabricRest -Method 'POST' -Endpoint "admin/domains" -Payload $payload).Response + } + + if ($ParentDomainId) { + Write-Log -Message "Child Domain - Name: $($result.displayName) / ID: $($result.id)" + } + else { + Write-Log -Message "Parent Domain - Name: $($result.displayName) / ID: $($result.id)" + } + + return $result +} + +function Set-FabricGatewayVirtualNetwork { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$DisplayName, + + [Parameter(Mandatory = $true)] + [string]$CapacityId, + + # Inactivity time (in minutes) before the gateway goes to auto-sleep. + # Allowed values: 30, 60, 90, 120, 150, 240, 360, 480, 720, 1440. + [Parameter(Mandatory = $true)] + [int]$InactivityMinutesBeforeSleep, + + # Number of member gateways (between 1 and 7). + [Parameter(Mandatory = $true)] + [int]$NumberOfMemberGateways, + + # Azure virtual network details: + [Parameter(Mandatory = $true)] + [string]$SubscriptionId, + + [Parameter(Mandatory = $true)] + [string]$ResourceGroupName, + + [Parameter(Mandatory = $true)] + [string]$VirtualNetworkName, + + [Parameter(Mandatory = $true)] + [string]$SubnetName + ) + + # Attempt to check for an existing gateway with the same display name. + $existingGateways = Invoke-FabricRest -Method 'GET' -Endpoint "gateways" + $result = $existingGateways.Response.value | Where-Object { $_.displayName -eq $DisplayName } + if (!$result) { + # Construct the payload for creating a Virtual Network gateway. + # Refer to the API documentation for details on the request format :contentReference[oaicite:1]{index=1} and the Virtual Network Azure Resource :contentReference[oaicite:2]{index=2}. + $payload = @{ + type = "VirtualNetwork" + displayName = $DisplayName + capacityId = $CapacityId + inactivityMinutesBeforeSleep = $InactivityMinutesBeforeSleep + numberOfMemberGateways = $NumberOfMemberGateways + virtualNetworkAzureResource = @{ + subscriptionId = $SubscriptionId + resourceGroupName = $ResourceGroupName + virtualNetworkName = $VirtualNetworkName + subnetName = $SubnetName + } + } + + Write-Log -Message "Creating Virtual Network Gateway: $DisplayName" -Level 'WARN' + $result = Invoke-FabricRest -Method 'POST' -Endpoint "gateways" -Payload $payload + } + + Write-Log -Message "Gateway Virtual Network - Name: $($result.displayName) / ID: $($result.id)" + return $result +} + + +function Set-AzureVirtualNetwork { + param( + [Parameter(Mandatory = $true)] + [string]$ResourceGroupName, + + [Parameter(Mandatory = $true)] + [string]$VNetName, + + [Parameter(Mandatory = $true)] + [string]$Location, + + [Parameter(Mandatory = $true)] + [string[]]$AddressPrefixes, + + [Parameter(Mandatory = $true)] + [string]$SubnetName, + + [Parameter(Mandatory = $true)] + [string[]]$SubnetAddressPrefixes + ) + + # Attempt to get the existing Virtual Network + $vnet = Get-AzVirtualNetwork -Name $VNetName -ResourceGroupName $ResourceGroupName -ErrorAction Stop + if (!$vnet) { + # VNet does not exist, so create it + Write-Log -Message "Creating VNet: $VNetName in Resource Group: $ResourceGroupName" -Level 'WARN' + $subnetConfig = New-AzVirtualNetworkSubnetConfig ` + -Name $SubnetName ` + -AddressPrefix $SubnetAddressPrefixes ` + + $subnetConfig = Add-AzDelegation ` + -Name 'PowerPlatformVnetAccess' ` + -ServiceName 'Microsoft.PowerPlatform/vnetaccesslinks' ` + -Subnet $subnetConfig + + $vnet = New-AzVirtualNetwork ` + -Name $VNetName ` + -ResourceGroupName $ResourceGroupName ` + -Location $Location ` + -AddressPrefix $AddressPrefixes ` + -Subnet $subnetConfig + + # Commit creation + $vnet = $vnet | Set-AzVirtualNetwork + Write-Log -Message "Created VNet: $VNetName" -Level 'INFO' + return $vnet + } + + # If the VNet already exists, check for the subnet + $subnet = $vnet.Subnets | Where-Object { $_.Name -eq $SubnetName } + if (-not $subnet) { + # Subnet does not exist; add one with the delegation + Write-Log -Message "Adding subnet '$SubnetName' with delegation 'Microsoft.PowerPlatform/vnetaccesslinks' to VNet '$VNetName'." -Level 'WARN' + $subnetConfig = New-AzVirtualNetworkSubnetConfig ` + -Name $SubnetName ` + -AddressPrefix $SubnetAddressPrefixes ` + + $subnetConfig = Add-AzDelegation ` + -Name 'PowerPlatformVnetAccess' ` + -ServiceName 'Microsoft.PowerPlatform/vnetaccesslinks' ` + -Subnet $subnetConfig + + $vnet = $vnet | Set-AzVirtualNetwork + } + else { + # Subnet exists; ensure it has the correct delegation + $existingDelegation = $subnet.Delegations | Where-Object { $_.ServiceName -eq 'Microsoft.PowerPlatform/vnetaccesslinks' } + if (-not $existingDelegation) { + Write-Log -Message "Subnet '$SubnetName' found but missing delegation to 'Microsoft.PowerPlatform/vnetaccesslinks'. Adding Microsoft.PowerPlatform/vnetaccesslinks delegation..." -Level 'WARN' + + $subnetConfig = Add-AzDelegation ` + -Name 'PowerPlatformVnetAccess' ` + -ServiceName 'Microsoft.PowerPlatform/vnetaccesslinks' ` + -Subnet $subnet + + $vnet = $vnet | Set-AzVirtualNetwork + Write-Log -Message "Added missing delegation to subnet '$SubnetName'." -Level 'INFO' + } + } + Write-Log -Message "Az Virtual Network - Name: $($vnet.Name)" + return $vnet +} + # Define an array of modules to install -$modules = @('Az.Accounts', 'Az.Resources', 'Az.Fabric', 'pwsh-dotenv', 'ADOPS') +$modules = @('Az.Accounts', 'Az.Resources', 'Az.Fabric', 'pwsh-dotenv', 'ADOPS', 'Az.Network') # Loop through each module and install if not installed foreach ($module in $modules) { @@ -488,8 +667,8 @@ if (Test-Path -Path './wellknown.env') { Import-Dotenv -Path ./wellknown.env -AllowClobber } -if (!$Env:FABRIC_TESTACC_WELLKNOWN_ENTRA_TENANT_ID -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID -or !$Env:FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZDO_ORGANIZATION_NAME -or !$Env:FABRIC_TESTACC_WELLKNOWN_NAME_PREFIX) { - Write-Log -Message 'FABRIC_TESTACC_WELLKNOWN_ENTRA_TENANT_ID, FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID, FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME, FABRIC_TESTACC_WELLKNOWN_AZDO_ORGANIZATION_NAME and FABRIC_TESTACC_WELLKNOWN_NAME_PREFIX are required environment variables.' -Level 'ERROR' +if (!$Env:FABRIC_TESTACC_WELLKNOWN_ENTRA_TENANT_ID -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID -or !$Env:FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZDO_ORGANIZATION_NAME -or !$Env:FABRIC_TESTACC_WELLKNOWN_NAME_PREFIX -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME -or !$Env:FABRIC_TESTACC_WELLKNOWN_AZURE_LOCATION) { + Write-Log -Message 'FABRIC_TESTACC_WELLKNOWN_ENTRA_TENANT_ID, FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID, FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME, FABRIC_TESTACC_WELLKNOWN_AZDO_ORGANIZATION_NAME and FABRIC_TESTACC_WELLKNOWN_NAME_PREFIX and FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME and FABRIC_TESTACC_WELLKNOWN_AZURE_LOCATION are required environment variables.' -Level 'ERROR' } # Check if already logged in to Azure, if not then login @@ -559,6 +738,11 @@ $itemNaming = @{ 'EntraServicePrincipal' = 'sp' 'EntraGroup' = 'grp' 'AzDOProject' = 'proj' + 'VirtualNetwork01' = 'vnet01' + 'VirtualNetwork02' = 'vnet02' + 'VirtualNetworkSubnet' = 'subnet' + 'GatewayVirtualNetwork' = 'gvnet' + 'GatewayOnPremises' = 'gop' } $baseName = Get-BaseName @@ -568,6 +752,8 @@ $Env:FABRIC_TESTACC_WELLKNOWN_NAME_BASE = $baseName $envVarNames = @( 'FABRIC_TESTACC_WELLKNOWN_ENTRA_TENANT_ID', 'FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID', + 'FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME' + 'FABRIC_TESTACC_WELLKNOWN_AZURE_LOCATION', 'FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME', 'FABRIC_TESTACC_WELLKNOWN_AZDO_ORGANIZATION_NAME', 'FABRIC_TESTACC_WELLKNOWN_NAME_PREFIX', @@ -840,6 +1026,83 @@ if ($SPN) { $result = Set-ADOPSGitPermission -ProjectId $azdoProject.id -RepositoryId $azdoRepo.id -Descriptor $azdoSPN.descriptor -Allow 'GenericContribute', 'PullRequestContribute', 'CreateBranch', 'CreateTag', 'GenericRead' } +# Create Azure Virtual Network 1 if not exists +$vnetName = "${displayName}_$($itemNaming['VirtualNetwork01'])" +$addrRange = "10.10.0.0/16" +$subName = "${displayName}_$($itemNaming['VirtualNetworkSubnet'])" +$subRange = "10.10.1.0/24" + +$vnet = Set-AzureVirtualNetwork ` + -ResourceGroupName $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME ` + -VNetName $vnetName ` + -Location $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_LOCATION ` + -AddressPrefixes $addrRange ` + -SubnetName $subName ` + -SubnetAddressPrefixes $subRange + +$wellKnown['VirtualNetwork01'] = @{ + name = $vnet.Name + resourceGroupName = $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME + subnetName = $subName + subscriptionId = $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID +} + + +# Create Azure Virtual Network 2 if not exists +$vnetName = "${displayName}_$($itemNaming['VirtualNetwork02'])" +$addrRange = "10.10.0.0/16" +$subName = "${displayName}_$($itemNaming['VirtualNetworkSubnet'])" +$subRange = "10.10.1.0/24" + +$vnet = Set-AzureVirtualNetwork ` + -ResourceGroupName $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME ` + -VNetName $vnetName ` + -Location $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_LOCATION ` + -AddressPrefixes $addrRange ` + -SubnetName $subName ` + -SubnetAddressPrefixes $subRange + +$wellKnown['VirtualNetwork02'] = @{ + name = $vnet.Name + resourceGroupName = $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME + subnetName = $subName + subscriptionId = $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID +} + +# Create Fabric Gateway Virtual Network if not exists +$displayNameTemp = "${displayName}_$($itemNaming['GatewayVirtualNetwork'])" +$inactivityMinutesBeforeSleep = 30 +$numberOfMemberGateways = 1 + +$gateway = Set-FabricGatewayVirtualNetwork ` + -DisplayName $displayNameTemp ` + -CapacityId $capacity.id ` + -InactivityMinutesBeforeSleep $inactivityMinutesBeforeSleep ` + -NumberOfMemberGateways $numberOfMemberGateways ` + -SubscriptionId $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID ` + -ResourceGroupName $Env:FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME ` + -VirtualNetworkName $wellKnown['VirtualNetwork01'].name ` + -SubnetName $wellKnown['VirtualNetwork01'].subnetName + +$wellKnown['GatewayVirtualNetwork'] = @{ + id = $gateway.id + displayName = $gateway.displayName + type = $gateway.type +} + +# Check if GatewayOnPremises +$displayNameTemp = "${displayName}_$($itemNaming['GatewayOnPremises'])" +$results = Invoke-FabricRest -Method 'GET' -Endpoint "gateways" +$result = $results.Response.value | Where-Object { $_.displayName -eq $displayNameTemp } +if (!$result) { + Write-Log -Message "!!! Please create an On-Premises Gateway manually (with Display Name: ${displayNameTemp}), and update details in the well-known file !!!" -Level 'ERROR' -Stop $false +} +$wellKnown['GatewayOnPremises'] = @{ + id = if ($result) { $result.id } else { '00000000-0000-0000-0000-000000000000' } + displayName = if ($result) { $result.displayName } else { $displayNameTemp } + type = 'OnPremises' +} + # Save wellknown.json file $wellKnownJson = $wellKnown | ConvertTo-Json $wellKnownJson From e556a7e2ace86d8dc5123e525c79ec9c70c72532 Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Thu, 13 Feb 2025 09:09:34 +0000 Subject: [PATCH 08/14] fix linting issues --- internal/services/gateway/data_gateway.go | 14 ++++---- internal/services/gateway/data_gateways.go | 3 +- .../services/gateway/models_data_gateway.go | 14 +++----- .../services/gateway/models_data_gateways.go | 1 + .../gateway/models_resource_gateway.go | 2 ++ internal/services/gateway/resource_gateway.go | 5 ++- .../resource_gateway_role_assignment_test.go | 19 +++++------ .../services/gateway/resource_gateway_test.go | 17 +++++----- internal/testhelp/fakes/fabric_gateway.go | 33 +++++++++++-------- internal/testhelp/utils.go | 16 ++++++--- tools/scripts/Set-WellKnown.ps1 | 2 +- 11 files changed, 68 insertions(+), 58 deletions(-) diff --git a/internal/services/gateway/data_gateway.go b/internal/services/gateway/data_gateway.go index 6cf0cbfc..d48b50a0 100644 --- a/internal/services/gateway/data_gateway.go +++ b/internal/services/gateway/data_gateway.go @@ -6,6 +6,7 @@ package gateway import ( "context" "fmt" + "strconv" "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" "github.com/hashicorp/terraform-plugin-framework-validators/datasourcevalidator" @@ -72,7 +73,7 @@ func (d *dataSourceGateway) Schema(ctx context.Context, _ datasource.SchemaReque Computed: true, }, "number_of_member_gateways": schema.Int32Attribute{ - MarkdownDescription: "The number of member gateways. Possible values: " + fmt.Sprint(MinNumberOfMemberGatewaysValues) + " to " + fmt.Sprint(MaxNumberOfMemberGatewaysValues) + ".", + MarkdownDescription: "The number of member gateways. Possible values: " + strconv.Itoa(int(MinNumberOfMemberGatewaysValues)) + " to " + strconv.Itoa(int(MaxNumberOfMemberGatewaysValues)) + ".", Computed: true, }, "virtual_network_azure_resource": schema.SingleNestedAttribute{ @@ -242,14 +243,13 @@ func (d *dataSourceGateway) getByDisplayName(ctx context.Context, model *dataSou } for _, entity := range page.Value { - gateway := entity.GetGateway() var entityDisplayName string - switch *gateway.Type { - case fabcore.GatewayTypeVirtualNetwork: - entityDisplayName = *(entity.(*fabcore.VirtualNetworkGateway).DisplayName) - case fabcore.GatewayTypeOnPremises: - entityDisplayName = *(entity.(*fabcore.OnPremisesGateway).DisplayName) + switch gateway := entity.(type) { + case *fabcore.VirtualNetworkGateway: + entityDisplayName = *(gateway.DisplayName) + case *fabcore.OnPremisesGateway: + entityDisplayName = *(gateway.DisplayName) default: continue } diff --git a/internal/services/gateway/data_gateways.go b/internal/services/gateway/data_gateways.go index 52301749..a56706a2 100644 --- a/internal/services/gateway/data_gateways.go +++ b/internal/services/gateway/data_gateways.go @@ -6,6 +6,7 @@ package gateway import ( "context" "fmt" + "strconv" "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts" "github.com/hashicorp/terraform-plugin-framework/datasource" @@ -73,7 +74,7 @@ func (d *dataSourceGateways) Schema(ctx context.Context, _ datasource.SchemaRequ Computed: true, }, "number_of_member_gateways": schema.Int32Attribute{ - MarkdownDescription: "The number of member gateways. Possible values: " + fmt.Sprint(MinNumberOfMemberGatewaysValues) + " to " + fmt.Sprint(MaxNumberOfMemberGatewaysValues) + ".", + MarkdownDescription: "The number of member gateways. Possible values: " + strconv.Itoa(int(MinNumberOfMemberGatewaysValues)) + " to " + strconv.Itoa(int(MaxNumberOfMemberGatewaysValues)) + ".", Computed: true, }, "virtual_network_azure_resource": schema.SingleNestedAttribute{ diff --git a/internal/services/gateway/models_data_gateway.go b/internal/services/gateway/models_data_gateway.go index f5b26013..e5eb9b4f 100644 --- a/internal/services/gateway/models_data_gateway.go +++ b/internal/services/gateway/models_data_gateway.go @@ -47,10 +47,8 @@ func (to *baseDataSourceGatewayModel) set(ctx context.Context, from fabcore.Gate to.ID = customtypes.NewUUIDPointerValue(gw.ID) to.Type = types.StringPointerValue((*string)(gw.Type)) - switch *gw.Type { - case fabcore.GatewayTypeVirtualNetwork: - entity := from.(*fabcore.VirtualNetworkGateway) - + switch entity := from.(type) { + case *fabcore.VirtualNetworkGateway: to.DisplayName = types.StringPointerValue(entity.DisplayName) to.CapacityID = customtypes.NewUUIDPointerValue(entity.CapacityID) to.InactivityMinutesBeforeSleep = types.Int32PointerValue(entity.InactivityMinutesBeforeSleep) @@ -69,9 +67,7 @@ func (to *baseDataSourceGatewayModel) set(ctx context.Context, from fabcore.Gate to.PublicKey = publicKey - case fabcore.GatewayTypeOnPremises: - entity := from.(*fabcore.OnPremisesGateway) - + case *fabcore.OnPremisesGateway: to.DisplayName = types.StringPointerValue(entity.DisplayName) to.NumberOfMemberGateways = types.Int32PointerValue(entity.NumberOfMemberGateways) to.AllowCloudConnectionRefresh = types.BoolPointerValue(entity.AllowCloudConnectionRefresh) @@ -92,9 +88,7 @@ func (to *baseDataSourceGatewayModel) set(ctx context.Context, from fabcore.Gate to.VirtualNetworkAzureResource = virtualNetworkAzureResource - case fabcore.GatewayTypeOnPremisesPersonal: - entity := from.(*fabcore.OnPremisesGatewayPersonal) - + case *fabcore.OnPremisesGatewayPersonal: to.Version = types.StringPointerValue(entity.Version) if entity.PublicKey != nil { diff --git a/internal/services/gateway/models_data_gateways.go b/internal/services/gateway/models_data_gateways.go index 6357a7fc..02189034 100644 --- a/internal/services/gateway/models_data_gateways.go +++ b/internal/services/gateway/models_data_gateways.go @@ -25,6 +25,7 @@ func (to *dataSourceGatewaysModel) setValues(ctx context.Context, from []fabcore if diags := entityModel.set(ctx, entity); diags.HasError() { return diags } + slice = append(slice, &entityModel) } diff --git a/internal/services/gateway/models_resource_gateway.go b/internal/services/gateway/models_resource_gateway.go index 0f584720..9826ec2c 100644 --- a/internal/services/gateway/models_resource_gateway.go +++ b/internal/services/gateway/models_resource_gateway.go @@ -45,6 +45,7 @@ func (to *baseResourceGatewayModel) set(ctx context.Context, from fabcore.Gatewa to.NumberOfMemberGateways = types.Int32PointerValue(gateway.NumberOfMemberGateways) virtualNetworkAzureResource := supertypes.NewSingleNestedObjectValueOfNull[virtualNetworkAzureResourceModel](ctx) + if gateway.VirtualNetworkAzureResource != nil { virtualNetworkAzureResourceModel := &virtualNetworkAzureResourceModel{} virtualNetworkAzureResourceModel.set(*gateway.VirtualNetworkAzureResource) @@ -57,6 +58,7 @@ func (to *baseResourceGatewayModel) set(ctx context.Context, from fabcore.Gatewa to.VirtualNetworkAzureResource = virtualNetworkAzureResource default: diags.AddError("Unsupported Gateway type", fmt.Sprintf("The Gateway type '%T' is not supported.", gateway)) + return diags } diff --git a/internal/services/gateway/resource_gateway.go b/internal/services/gateway/resource_gateway.go index 016df317..50b49828 100644 --- a/internal/services/gateway/resource_gateway.go +++ b/internal/services/gateway/resource_gateway.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" supertypes "github.com/orange-cloudavenue/terraform-plugin-framework-supertypes" @@ -26,8 +27,6 @@ import ( superobjectvalidator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/objectvalidator" superstringvalidator "github.com/orange-cloudavenue/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/types" - "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" @@ -37,7 +36,7 @@ import ( // Ensure the implementation satisfies the expected interfaces. var ( _ resource.ResourceWithConfigure = (*resourceGateway)(nil) - // _ resource.ResourceWithImportState = (*resourceGateway)(nil) + // _ resource.ResourceWithImportState = (*resourceGateway)(nil). ) type resourceGateway struct { diff --git a/internal/services/gateway/resource_gateway_role_assignment_test.go b/internal/services/gateway/resource_gateway_role_assignment_test.go index 2332cb5c..1daf83f4 100644 --- a/internal/services/gateway/resource_gateway_role_assignment_test.go +++ b/internal/services/gateway/resource_gateway_role_assignment_test.go @@ -10,7 +10,6 @@ import ( at "github.com/dcarbone/terraform-plugin-framework-utils/v3/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" - fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" "github.com/microsoft/terraform-provider-fabric/internal/framework/customtypes" @@ -151,16 +150,16 @@ func TestAcc_GatewayRoleAssignmentResource_CRUD(t *testing.T) { gatewayType := string(fabcore.GatewayTypeVirtualNetwork) gatewayCreateDisplayName := testhelp.RandomName() gatewayCreateInactivityMinutesBeforeSleep := int(testhelp.RandomElement(gateway.PossibleInactivityMinutesBeforeSleepValues)) - gatewayCreateNumberOfMemberGateways := testhelp.RandomInt(int(gateway.MinNumberOfMemberGatewaysValues), int(gateway.MaxNumberOfMemberGatewaysValues)) + gatewayCreateNumberOfMemberGateways := int(testhelp.RandomInt(gateway.MinNumberOfMemberGatewaysValues, gateway.MaxNumberOfMemberGatewaysValues)) capacity := testhelp.WellKnown()["Capacity"].(map[string]any) capacityID := capacity["id"].(string) virtualNetworkAzureResource := testhelp.WellKnown()["VirtualNetwork01"].(map[string]any) - VirtualNetworkName := virtualNetworkAzureResource["name"].(string) - ResourceGroupName := virtualNetworkAzureResource["resourceGroupName"].(string) - SubnetName := virtualNetworkAzureResource["subnetName"].(string) - SubscriptionID := virtualNetworkAzureResource["subscriptionId"].(string) + virtualNetworkName := virtualNetworkAzureResource["name"].(string) + resourceGroupName := virtualNetworkAzureResource["resourceGroupName"].(string) + subnetName := virtualNetworkAzureResource["subnetName"].(string) + subscriptionID := virtualNetworkAzureResource["subscriptionId"].(string) gatewayResourceHCL := at.CompileConfig( at.ResourceHeader(testhelp.TypeName("fabric", itemTFName), "test"), @@ -170,10 +169,10 @@ func TestAcc_GatewayRoleAssignmentResource_CRUD(t *testing.T) { "inactivity_minutes_before_sleep": gatewayCreateInactivityMinutesBeforeSleep, "number_of_member_gateways": gatewayCreateNumberOfMemberGateways, "virtual_network_azure_resource": map[string]any{ - "virtual_network_name": VirtualNetworkName, - "resource_group_name": ResourceGroupName, - "subnet_name": SubnetName, - "subscription_id": SubscriptionID, + "virtual_network_name": virtualNetworkName, + "resource_group_name": resourceGroupName, + "subnet_name": subnetName, + "subscription_id": subscriptionID, }, "capacity_id": capacityID, }, diff --git a/internal/services/gateway/resource_gateway_test.go b/internal/services/gateway/resource_gateway_test.go index 67214157..32a13921 100644 --- a/internal/services/gateway/resource_gateway_test.go +++ b/internal/services/gateway/resource_gateway_test.go @@ -13,7 +13,6 @@ import ( at "github.com/dcarbone/terraform-plugin-framework-utils/v3/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" - fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" "github.com/microsoft/terraform-provider-fabric/internal/common" @@ -430,7 +429,7 @@ func TestAcc_GatewayResource_CRUD(t *testing.T) { entityType := string(fabcore.GatewayTypeVirtualNetwork) entityCreateDisplayName := testhelp.RandomName() entityCreateInactivityMinutesBeforeSleep := int(testhelp.RandomElement(gateway.PossibleInactivityMinutesBeforeSleepValues)) - entityCreateNumberOfMemberGateways := testhelp.RandomInt(int(gateway.MinNumberOfMemberGatewaysValues), int(gateway.MaxNumberOfMemberGatewaysValues)) + entityCreateNumberOfMemberGateways := int(testhelp.RandomInt(gateway.MinNumberOfMemberGatewaysValues, gateway.MaxNumberOfMemberGatewaysValues)) capacity := testhelp.WellKnown()["Capacity"].(map[string]any) capacityID := capacity["id"].(string) @@ -443,7 +442,7 @@ func TestAcc_GatewayResource_CRUD(t *testing.T) { entityUpdateDisplayName := testhelp.RandomName() entityUpdateInactivityMinutesBeforeSleep := int(testhelp.RandomElement(gateway.PossibleInactivityMinutesBeforeSleepValues)) - entityUpdateNumberOfMemberGateways := testhelp.RandomInt(int(gateway.MinNumberOfMemberGatewaysValues), int(gateway.MaxNumberOfMemberGatewaysValues)) + entityUpdateNumberOfMemberGateways := int(testhelp.RandomInt(gateway.MinNumberOfMemberGatewaysValues, gateway.MaxNumberOfMemberGatewaysValues)) virtualNetworkAzureResource02 := testhelp.WellKnown()["VirtualNetwork02"].(map[string]any) vNET02VirtualNetworkName := virtualNetworkAzureResource02["name"].(string) @@ -474,8 +473,8 @@ func TestAcc_GatewayResource_CRUD(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(testResourceItemFQN, "type", entityType), resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityCreateDisplayName), - resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(entityCreateInactivityMinutesBeforeSleep))), - resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(entityCreateNumberOfMemberGateways))), + resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(entityCreateInactivityMinutesBeforeSleep)), + resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(entityCreateNumberOfMemberGateways)), resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.virtual_network_name", vNET01VirtualNetworkName), resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.resource_group_name", vNET01ResourceGroupName), resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.subnet_name", vNET01SubnetName), @@ -505,8 +504,8 @@ func TestAcc_GatewayResource_CRUD(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(testResourceItemFQN, "type", entityType), resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), - resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(entityUpdateInactivityMinutesBeforeSleep))), - resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(entityUpdateNumberOfMemberGateways))), + resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(entityUpdateInactivityMinutesBeforeSleep)), + resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(entityUpdateNumberOfMemberGateways)), resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.virtual_network_name", vNET01VirtualNetworkName), resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.resource_group_name", vNET01ResourceGroupName), resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.subnet_name", vNET01SubnetName), @@ -536,8 +535,8 @@ func TestAcc_GatewayResource_CRUD(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(testResourceItemFQN, "type", entityType), resource.TestCheckResourceAttr(testResourceItemFQN, "display_name", entityUpdateDisplayName), - resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(int(entityUpdateInactivityMinutesBeforeSleep))), - resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(int(entityUpdateNumberOfMemberGateways))), + resource.TestCheckResourceAttr(testResourceItemFQN, "inactivity_minutes_before_sleep", strconv.Itoa(entityUpdateInactivityMinutesBeforeSleep)), + resource.TestCheckResourceAttr(testResourceItemFQN, "number_of_member_gateways", strconv.Itoa(entityUpdateNumberOfMemberGateways)), resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.virtual_network_name", vNET02VirtualNetworkName), resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.resource_group_name", vNET02ResourceGroupName), resource.TestCheckResourceAttr(testResourceItemFQN, "virtual_network_azure_resource.subnet_name", vNET02SubnetName), diff --git a/internal/testhelp/fakes/fabric_gateway.go b/internal/testhelp/fakes/fabric_gateway.go index 19da1757..d6f3359c 100644 --- a/internal/testhelp/fakes/fabric_gateway.go +++ b/internal/testhelp/fakes/fabric_gateway.go @@ -10,7 +10,7 @@ import ( fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" fabfake "github.com/microsoft/fabric-sdk-go/fabric/fake" - "github.com/microsoft/terraform-provider-fabric/internal/services/gateway" + gw "github.com/microsoft/terraform-provider-fabric/internal/services/gateway" "github.com/microsoft/terraform-provider-fabric/internal/testhelp" ) @@ -27,6 +27,7 @@ func (o *operationsGateway) Create(data fabcore.CreateGatewayRequestClassificati entity.InactivityMinutesBeforeSleep = gateway.InactivityMinutesBeforeSleep entity.NumberOfMemberGateways = gateway.NumberOfMemberGateways entity.VirtualNetworkAzureResource = gateway.VirtualNetworkAzureResource + return entity default: panic("Unsupported Gateway type") @@ -60,13 +61,18 @@ func (o *operationsGateway) TransformUpdate(entity fabcore.GatewayClassification } func (o *operationsGateway) Update(base fabcore.GatewayClassification, data fabcore.UpdateGatewayRequestClassification) fabcore.GatewayClassification { - switch request := data.(type) { - case *fabcore.UpdateVirtualNetworkGatewayRequest: - gateway, _ := base.(*fabcore.VirtualNetworkGateway) + switch gateway := base.(type) { + case *fabcore.VirtualNetworkGateway: + request, ok := data.(*fabcore.UpdateVirtualNetworkGatewayRequest) + if !ok { + panic("Invalid update data for VirtualNetworkGateway") + } + gateway.CapacityID = request.CapacityID gateway.DisplayName = request.DisplayName gateway.InactivityMinutesBeforeSleep = request.InactivityMinutesBeforeSleep gateway.NumberOfMemberGateways = request.NumberOfMemberGateways + return gateway default: panic("Unsupported Gateway type") @@ -78,12 +84,10 @@ func (o *operationsGateway) Validate(newEntity fabcore.GatewayClassification, ex if *(existingGateway.GetGateway().Type) != *(newEntity.GetGateway().Type) { continue } - switch gateway := newEntity.(type) { - case *fabcore.VirtualNetworkGateway: - vng := existingGateway.(*fabcore.VirtualNetworkGateway) - if *vng.DisplayName == *gateway.DisplayName { - // TODO(badeamarjieh): add error code to GO SDK - return http.StatusConflict, fabfake.SetResponseError(http.StatusConflict, "DuplicateGatewayName", fabcore.ErrWorkspace.WorkspaceNameAlreadyExists.Error()) + + if newVNG, ok := newEntity.(*fabcore.VirtualNetworkGateway); ok { + if existingVNG, ok := existingGateway.(*fabcore.VirtualNetworkGateway); ok && *existingVNG.DisplayName == *newVNG.DisplayName { + return http.StatusConflict, fabfake.SetResponseError(http.StatusConflict, fabcore.ErrGateway.DuplicateGatewayName.Error(), fabcore.ErrGateway.DuplicateGatewayName.Error()) } } } @@ -97,16 +101,19 @@ func (o *operationsGateway) GetID(entity fabcore.GatewayClassification) string { func configureVirtualNetworkGateway(server *fakeServer) fabcore.VirtualNetworkGateway { configureGateway(server) + return fabcore.VirtualNetworkGateway{} } func configureOnPremisesGateway(server *fakeServer) fabcore.OnPremisesGateway { configureGateway(server) + return fabcore.OnPremisesGateway{} } func configureOnPremisesGatewayPersonal(server *fakeServer) fabcore.OnPremisesGatewayPersonal { configureGateway(server) + return fabcore.OnPremisesGatewayPersonal{} } @@ -158,7 +165,7 @@ func NewRandomOnPremisesGateway() *fabcore.OnPremisesGateway { DisplayName: to.Ptr(testhelp.RandomName()), AllowCloudConnectionRefresh: to.Ptr(testhelp.RandomBool()), AllowCustomConnectors: to.Ptr(testhelp.RandomBool()), - NumberOfMemberGateways: to.Ptr(int32(testhelp.RandomInt(int(gateway.MinNumberOfMemberGatewaysValues), int(gateway.MaxNumberOfMemberGatewaysValues)))), + NumberOfMemberGateways: to.Ptr(testhelp.RandomInt(gw.MinNumberOfMemberGatewaysValues, gw.MaxNumberOfMemberGatewaysValues)), LoadBalancingSetting: to.Ptr(testhelp.RandomElement(fabcore.PossibleLoadBalancingSettingValues())), Version: to.Ptr(testhelp.RandomName()), PublicKey: NewRadomPublicKey(), @@ -180,8 +187,8 @@ func NewRandomVirtualNetworkGateway() *fabcore.VirtualNetworkGateway { Type: to.Ptr(fabcore.GatewayTypeVirtualNetwork), DisplayName: to.Ptr(testhelp.RandomName()), CapacityID: to.Ptr(testhelp.RandomUUID()), - InactivityMinutesBeforeSleep: to.Ptr(testhelp.RandomElement(gateway.PossibleInactivityMinutesBeforeSleepValues)), - NumberOfMemberGateways: to.Ptr(int32(testhelp.RandomInt(int(gateway.MinNumberOfMemberGatewaysValues), int(gateway.MaxNumberOfMemberGatewaysValues)))), + InactivityMinutesBeforeSleep: to.Ptr(testhelp.RandomElement(gw.PossibleInactivityMinutesBeforeSleepValues)), + NumberOfMemberGateways: to.Ptr(testhelp.RandomInt(gw.MinNumberOfMemberGatewaysValues, gw.MaxNumberOfMemberGatewaysValues)), VirtualNetworkAzureResource: NewRandomVirtualNetworkAzureResource(), } } diff --git a/internal/testhelp/utils.go b/internal/testhelp/utils.go index e1fc94e0..ba2e8433 100644 --- a/internal/testhelp/utils.go +++ b/internal/testhelp/utils.go @@ -9,6 +9,7 @@ import ( "encoding/pem" "errors" "fmt" + "math" "strings" "github.com/hashicorp/go-uuid" @@ -27,9 +28,10 @@ func RandomName(length ...int) string { return acctest.RandStringFromCharSet(size, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") } -// RandomInt returns a random integer between minInt (inclusive) and maxInt (exclusive) -func RandomInt(minInt int, maxInt int) int { - return acctest.RandIntRange(minInt, maxInt) +// RandomInt returns a random integer between minInt (inclusive) and maxInt (exclusive). +func RandomInt(minInt, maxInt int32) int32 { + // #nosec G115 -- safe cast because we know minInt/maxInt fit in int32 + return int32(acctest.RandIntRange(int(minInt), int(maxInt))) } func RandomBool() bool { @@ -43,7 +45,13 @@ func RandomUUID() string { } func RandomElement[T any](elements []T) T { - return elements[RandomInt(0, len(elements))] + elementsLen := len(elements) + + if elementsLen < math.MinInt32 || elementsLen > math.MaxInt32 { + panic(fmt.Sprintf("random number %d out of int32 bounds", elementsLen)) + } + + return elements[RandomInt(0, int32(elementsLen))] } func RandomURI() string { diff --git a/tools/scripts/Set-WellKnown.ps1 b/tools/scripts/Set-WellKnown.ps1 index 63cacdf9..a5e04677 100755 --- a/tools/scripts/Set-WellKnown.ps1 +++ b/tools/scripts/Set-WellKnown.ps1 @@ -443,7 +443,7 @@ function Set-FabricWorkspaceCapacity { $payload = @{ capacityId = $CapacityId } - $result = (Invoke-FabricRest -Method 'POST' -Endpoint "workspaces/$WorkspaceId/assignToCapacity" -Payload $payload).Response + _ = (Invoke-FabricRest -Method 'POST' -Endpoint "workspaces/$WorkspaceId/assignToCapacity" -Payload $payload).Response $workspace.Response.capacityId = $CapacityId } From 8e613218ee65764fdac99636299bee93a92b9b77 Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Thu, 13 Feb 2025 12:35:00 +0000 Subject: [PATCH 09/14] remove onprem acc tests and from wellknown --- .gitignore | 1 - .../services/gateway/data_gateway_test.go | 33 ------ .../services/gateway/data_gateways_test.go | 1 - .../fixtures/.wellknown.template.json | 110 ------------------ tools/scripts/Set-WellKnown.ps1 | 14 --- 5 files changed, 159 deletions(-) delete mode 100644 internal/testhelp/fixtures/.wellknown.template.json diff --git a/.gitignore b/.gitignore index 6dec83b0..fa5fb9f5 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,6 @@ testresults.xml golangci-report.xml .wellknown.json .wellknown.*.json -!.wellknown.template.json changie.md .lycheecache *.env diff --git a/internal/services/gateway/data_gateway_test.go b/internal/services/gateway/data_gateway_test.go index 3e929398..fcb83f3a 100644 --- a/internal/services/gateway/data_gateway_test.go +++ b/internal/services/gateway/data_gateway_test.go @@ -192,11 +192,6 @@ func TestAcc_GatewayDataSource(t *testing.T) { entityVirtualNetworkDisplayName := entityVirtualNetwork["displayName"].(string) entityVirtualNetworkType := entityVirtualNetwork["type"].(string) - entityOnPremises := testhelp.WellKnown()["GatewayOnPremises"].(map[string]any) - entityOnPremisesID := entityOnPremises["id"].(string) - entityOnPremisesDisplayName := entityOnPremises["displayName"].(string) - entityOnPremisesType := entityOnPremises["type"].(string) - resource.ParallelTest(t, testhelp.NewTestAccCase(t, nil, nil, []resource.TestStep{ // read by id - not found { @@ -236,33 +231,5 @@ func TestAcc_GatewayDataSource(t *testing.T) { resource.TestCheckResourceAttr(testDataSourceItemFQN, "type", entityVirtualNetworkType), ), }, - // read by id - on premises - { - Config: at.CompileConfig( - testDataSourceItemHeader, - map[string]any{ - "id": entityOnPremisesID, - }, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityOnPremisesID), - resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityOnPremisesDisplayName), - resource.TestCheckResourceAttr(testDataSourceItemFQN, "type", entityOnPremisesType), - ), - }, - // read by name - on premises - { - Config: at.CompileConfig( - testDataSourceItemHeader, - map[string]any{ - "display_name": entityOnPremisesDisplayName, - }, - ), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(testDataSourceItemFQN, "id", entityOnPremisesID), - resource.TestCheckResourceAttr(testDataSourceItemFQN, "display_name", entityOnPremisesDisplayName), - resource.TestCheckResourceAttr(testDataSourceItemFQN, "type", entityOnPremisesType), - ), - }, })) } diff --git a/internal/services/gateway/data_gateways_test.go b/internal/services/gateway/data_gateways_test.go index a6b15556..705189ce 100644 --- a/internal/services/gateway/data_gateways_test.go +++ b/internal/services/gateway/data_gateways_test.go @@ -66,7 +66,6 @@ func TestAcc_GatewaysDataSource(t *testing.T) { ), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.0.id"), - resource.TestCheckResourceAttrSet(testDataSourceItemsFQN, "values.1.id"), ), }, }, diff --git a/internal/testhelp/fixtures/.wellknown.template.json b/internal/testhelp/fixtures/.wellknown.template.json deleted file mode 100644 index c4986090..00000000 --- a/internal/testhelp/fixtures/.wellknown.template.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "workspace": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_workspace", - "description": "testacc_fabric_workspace" - }, - "dashboard": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_dashboard", - "description": "testacc_fabric_dashboard" - }, - "datamart": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_datamart", - "description": "testacc_fabric_datamart" - }, - "dataPipeline": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_data_pipeline", - "description": "testacc_fabric_data_pipeline" - }, - "environment": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_environment", - "description": "testacc_fabric_environment" - }, - "eventhouse": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_eventhouse", - "description": "testacc_fabric_eventhouse" - }, - "eventstream": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_eventstream", - "description": "testacc_fabric_eventstream" - }, - "kqlDatabase": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_kql_database", - "description": "testacc_fabric_kql_database" - }, - "kqlQueryset": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_kql_queryset", - "description": "testacc_fabric_kql_queryset" - }, - "lakehouse": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_lakehouse", - "description": "testacc_fabric_lakehouse" - }, - "mlExperiment": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_ml_experiment", - "description": "testacc_fabric_ml_experiment" - }, - "mlModel": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_ml_model", - "description": "testacc_fabric_ml_model" - }, - "notebook": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_notebook", - "description": "testacc_fabric_notebook" - }, - "report": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_report", - "description": "testacc_fabric_report" - }, - "semanticModel": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_semantic_model", - "description": "testacc_fabric_semantic_model" - }, - "warehouse": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_warehouse", - "description": "testacc_fabric_warehouse" - }, - "sparkJobDefinition": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_spark_job_definition", - "description": "testacc_fabric_spark_job_definition" - }, - "capacity": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_fabric_capacity", - "description": "testacc_fabric_capacity" - }, - "domainParent": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_domain_parent", - "description": "testacc_domain_parent" - }, - "domainChild": { - "id": "00000000-0000-0000-0000-000000000000", - "displayName": "testacc_domain_child", - "description": "testacc_domain_child" - }, - "principal": { - "id": "00000000-0000-0000-0000-000000000000", - "type": "ServicePrincipal" - }, - "group": { - "id": "00000000-0000-0000-0000-000000000000", - "type": "Group" - } -} diff --git a/tools/scripts/Set-WellKnown.ps1 b/tools/scripts/Set-WellKnown.ps1 index fdcd6481..056e804d 100755 --- a/tools/scripts/Set-WellKnown.ps1 +++ b/tools/scripts/Set-WellKnown.ps1 @@ -742,7 +742,6 @@ $itemNaming = @{ 'VirtualNetwork02' = 'vnet02' 'VirtualNetworkSubnet' = 'subnet' 'GatewayVirtualNetwork' = 'gvnet' - 'GatewayOnPremises' = 'gop' } $baseName = Get-BaseName @@ -1095,19 +1094,6 @@ $wellKnown['GatewayVirtualNetwork'] = @{ type = $gateway.type } -# Check if GatewayOnPremises -$displayNameTemp = "${displayName}_$($itemNaming['GatewayOnPremises'])" -$results = Invoke-FabricRest -Method 'GET' -Endpoint "gateways" -$result = $results.Response.value | Where-Object { $_.displayName -eq $displayNameTemp } -if (!$result) { - Write-Log -Message "!!! Please create an On-Premises Gateway manually (with Display Name: ${displayNameTemp}), and update details in the well-known file !!!" -Level 'ERROR' -Stop $false -} -$wellKnown['GatewayOnPremises'] = @{ - id = if ($result) { $result.id } else { '00000000-0000-0000-0000-000000000000' } - displayName = if ($result) { $result.displayName } else { $displayNameTemp } - type = 'OnPremises' -} - # Save wellknown.json file $wellKnownJson = $wellKnown | ConvertTo-Json $wellKnownJson From c544a10213a73c32220788fa8d5b25fbd43677dc Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Thu, 13 Feb 2025 15:58:40 +0000 Subject: [PATCH 10/14] fix well known ps script --- DEVELOPER.md | 2 ++ tools/scripts/Set-WellKnown.ps1 | 27 +++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/DEVELOPER.md b/DEVELOPER.md index 1d295099..f2adeb23 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -298,6 +298,8 @@ FABRIC_TESTACC_WELLKNOWN_AZURE_SUBSCRIPTION_ID="" FABRIC_TESTACC_WELLKNOWN_FABRIC_CAPACITY_NAME="" FABRIC_TESTACC_WELLKNOWN_AZDO_ORGANIZATION_NAME="" FABRIC_TESTACC_WELLKNOWN_NAME_PREFIX="" +FABRIC_TESTACC_WELLKNOWN_AZURE_RESOURCE_GROUP_NAME="" +FABRIC_TESTACC_WELLKNOWN_AZURE_LOCATION"" # Optional FABRIC_TESTACC_WELLKNOWN_NAME_SUFFIX="" diff --git a/tools/scripts/Set-WellKnown.ps1 b/tools/scripts/Set-WellKnown.ps1 index 056e804d..14e44047 100755 --- a/tools/scripts/Set-WellKnown.ps1 +++ b/tools/scripts/Set-WellKnown.ps1 @@ -562,7 +562,8 @@ function Set-FabricGatewayVirtualNetwork { } Write-Log -Message "Creating Virtual Network Gateway: $DisplayName" -Level 'WARN' - $result = Invoke-FabricRest -Method 'POST' -Endpoint "gateways" -Payload $payload + $newGateway = Invoke-FabricRest -Method 'POST' -Endpoint "gateways" -Payload $payload + $result = $newGateway.Response } Write-Log -Message "Gateway Virtual Network - Name: $($result.displayName) / ID: $($result.id)" @@ -592,8 +593,10 @@ function Set-AzureVirtualNetwork { ) # Attempt to get the existing Virtual Network - $vnet = Get-AzVirtualNetwork -Name $VNetName -ResourceGroupName $ResourceGroupName -ErrorAction Stop - if (!$vnet) { + try { + $vnet = Get-AzVirtualNetwork -Name $VNetName -ResourceGroupName $ResourceGroupName -ErrorAction Stop + } + catch { # VNet does not exist, so create it Write-Log -Message "Creating VNet: $VNetName in Resource Group: $ResourceGroupName" -Level 'WARN' $subnetConfig = New-AzVirtualNetworkSubnetConfig ` @@ -615,7 +618,6 @@ function Set-AzureVirtualNetwork { # Commit creation $vnet = $vnet | Set-AzVirtualNetwork Write-Log -Message "Created VNet: $VNetName" -Level 'INFO' - return $vnet } # If the VNet already exists, check for the subnet @@ -650,6 +652,19 @@ function Set-AzureVirtualNetwork { } } Write-Log -Message "Az Virtual Network - Name: $($vnet.Name)" + + $userPrincipalName = $azContext.Account.Id + $principal = Get-AzADUser -UserPrincipalName $userPrincipalName + + $existingAssignment = Get-AzRoleAssignment -Scope $vnet.Id -ObjectId $principal.Id -ErrorAction SilentlyContinue | Where-Object { + $_.RoleDefinitionName -eq "Network Contributor" + } + + Write-Log "Assigning Network Contributor role to the principal on the virtual network $($VNetName)" + if (!$existingAssignment) { + New-AzRoleAssignment -ObjectId $principal.Id -RoleDefinitionName "Network Contributor" -Scope $vnet.Id + } + return $vnet } @@ -1030,6 +1045,10 @@ if ($SPN) { $result = Set-ADOPSGitPermission -ProjectId $azdoProject.id -RepositoryId $azdoRepo.id -Descriptor $azdoSPN.descriptor -Allow 'GenericContribute', 'PullRequestContribute', 'CreateBranch', 'CreateTag', 'GenericRead' } +# Register the Microsoft.PowerPlatform resource provider +Write-Log -Message "Registering Microsoft.PowerPlatform resource provider" -Level 'WARN' +Register-AzResourceProvider -ProviderNamespace "Microsoft.PowerPlatform" + # Create Azure Virtual Network 1 if not exists $vnetName = "${displayName}_$($itemNaming['VirtualNetwork01'])" $addrRange = "10.10.0.0/16" From 45079bb2b99097e5a723670c038e5a27d972002f Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Tue, 25 Feb 2025 08:07:42 +0000 Subject: [PATCH 11/14] fix lint and remove redundant code in Set-WellKno --- internal/testhelp/fakes/fake_server.go | 1 + tools/scripts/Set-WellKnown.ps1 | 35 -------------------------- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/internal/testhelp/fakes/fake_server.go b/internal/testhelp/fakes/fake_server.go index 58259ba9..e848bc85 100644 --- a/internal/testhelp/fakes/fake_server.go +++ b/internal/testhelp/fakes/fake_server.go @@ -81,6 +81,7 @@ func (s *fakeServer) Upsert(element any) { if elementType.Kind() == reflect.Ptr { elementType = elementType.Elem() } + if !s.isSupportedType(elementType) { panic("Unsupported type: " + reflect.TypeOf(element).String() + ". Did you forget to call HandleEntity in NewFakeServer?") // lintignore:R009 } diff --git a/tools/scripts/Set-WellKnown.ps1 b/tools/scripts/Set-WellKnown.ps1 index 14e44047..386d1603 100755 --- a/tools/scripts/Set-WellKnown.ps1 +++ b/tools/scripts/Set-WellKnown.ps1 @@ -474,41 +474,6 @@ function Set-FabricWorkspaceRoleAssignment { } } -function Set-FabricDomain { - param ( - [Parameter(Mandatory = $true)] - [string]$DisplayName, - - [Parameter(Mandatory = $false)] - [string]$ParentDomainId - ) - - $results = Invoke-FabricRest -Method 'GET' -Endpoint "admin/domains" - $result = $results.Response.domains | Where-Object { $_.displayName -eq $DisplayName } - if (!$result) { - Write-Log -Message "Creating Domain: $DisplayName" -Level 'WARN' - $payload = @{ - displayName = $DisplayName - description = $DisplayName - } - - if ($ParentDomainId) { - $payload['parentDomainId'] = $ParentDomainId - } - - $result = (Invoke-FabricRest -Method 'POST' -Endpoint "admin/domains" -Payload $payload).Response - } - - if ($ParentDomainId) { - Write-Log -Message "Child Domain - Name: $($result.displayName) / ID: $($result.id)" - } - else { - Write-Log -Message "Parent Domain - Name: $($result.displayName) / ID: $($result.id)" - } - - return $result -} - function Set-FabricGatewayVirtualNetwork { [CmdletBinding()] param( From 545019b44600d7007c72fe1d1f8a8f729b75f7b0 Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Thu, 6 Mar 2025 08:24:43 +0000 Subject: [PATCH 12/14] update docs --- docs/data-sources/graphql_api.md | 67 ------------------------------- docs/data-sources/graphql_apis.md | 62 ---------------------------- 2 files changed, 129 deletions(-) delete mode 100644 docs/data-sources/graphql_api.md delete mode 100644 docs/data-sources/graphql_apis.md diff --git a/docs/data-sources/graphql_api.md b/docs/data-sources/graphql_api.md deleted file mode 100644 index 7d5a0b00..00000000 --- a/docs/data-sources/graphql_api.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "fabric_graphql_api Data Source - terraform-provider-fabric" -subcategory: "" -description: |- - Get a Fabric GraphQL API. - Use this data source to fetch a GraphQL API https://learn.microsoft.com/fabric/data-engineering/api-graphql-overview. - -> This item supports Service Principal authentication. - ~> This data-source is in preview. To access it, you must explicitly enable the preview mode in the provider level configuration. ---- - -# fabric_graphql_api (Data Source) - -Get a Fabric GraphQL API. - -Use this data source to fetch a [GraphQL API](https://learn.microsoft.com/fabric/data-engineering/api-graphql-overview). - --> This item supports Service Principal authentication. - -~> This data-source is in **preview**. To access it, you must explicitly enable the `preview` mode in the provider level configuration. - -## Example Usage - -```terraform -data "fabric_graphql_api" "example_by_id" { - id = "11111111-1111-1111-1111-111111111111" - workspace_id = "00000000-0000-0000-0000-000000000000" -} - -data "fabric_graphql_api" "example_by_name" { - display_name = "example" - workspace_id = "00000000-0000-0000-0000-000000000000" -} - -# This is an invalid data source -# Do not specify `id` and `display_name` in the same data source block -# data "fabric_graphql_api" "example" { -# display_name = "example" -# id = "11111111-1111-1111-1111-111111111111" -# workspace_id = "00000000-0000-0000-0000-000000000000" -# } -``` - - -## Schema - -### Required - -- `workspace_id` (String) The Workspace ID. - -### Optional - -- `display_name` (String) The GraphQL API display name. -- `id` (String) The GraphQL API ID. -- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) - -### Read-Only - -- `description` (String) The GraphQL API description. - - - -### Nested Schema for `timeouts` - -Optional: - -- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). diff --git a/docs/data-sources/graphql_apis.md b/docs/data-sources/graphql_apis.md deleted file mode 100644 index 3ecf1190..00000000 --- a/docs/data-sources/graphql_apis.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "fabric_graphql_apis Data Source - terraform-provider-fabric" -subcategory: "" -description: |- - List a Fabric GraphQL APIs. - Use this data source to list GraphQL APIs https://learn.microsoft.com/fabric/data-engineering/api-graphql-overview. - -> This item supports Service Principal authentication. - ~> This data-source is in preview. To access it, you must explicitly enable the preview mode in the provider level configuration. ---- - -# fabric_graphql_apis (Data Source) - -List a Fabric GraphQL APIs. - -Use this data source to list [GraphQL APIs](https://learn.microsoft.com/fabric/data-engineering/api-graphql-overview). - --> This item supports Service Principal authentication. - -~> This data-source is in **preview**. To access it, you must explicitly enable the `preview` mode in the provider level configuration. - -## Example Usage - -```terraform -data "fabric_graphql_apis" "example" { - workspace_id = "00000000-0000-0000-0000-000000000000" -} -``` - - -## Schema - -### Required - -- `workspace_id` (String) The Workspace ID. - -### Optional - -- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) - -### Read-Only - -- `values` (Attributes List) The list of GraphQL APIs. (see [below for nested schema](#nestedatt--values)) - - - -### Nested Schema for `timeouts` - -Optional: - -- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). - - - -### Nested Schema for `values` - -Read-Only: - -- `description` (String) The GraphQL API description. -- `display_name` (String) The GraphQL API display name. -- `id` (String) The GraphQL API ID. -- `workspace_id` (String) The Workspace ID. From f1d729bef300b535b1284196d7cbfbcc63d9f07c Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Thu, 6 Mar 2025 10:00:58 +0000 Subject: [PATCH 13/14] fix removal of graphql from merge fix --- docs/data-sources/graphql_api.md | 67 +++++++++++++++++++++++++++++++ docs/data-sources/graphql_apis.md | 62 ++++++++++++++++++++++++++++ internal/provider/provider.go | 2 + 3 files changed, 131 insertions(+) create mode 100644 docs/data-sources/graphql_api.md create mode 100644 docs/data-sources/graphql_apis.md diff --git a/docs/data-sources/graphql_api.md b/docs/data-sources/graphql_api.md new file mode 100644 index 00000000..7d5a0b00 --- /dev/null +++ b/docs/data-sources/graphql_api.md @@ -0,0 +1,67 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "fabric_graphql_api Data Source - terraform-provider-fabric" +subcategory: "" +description: |- + Get a Fabric GraphQL API. + Use this data source to fetch a GraphQL API https://learn.microsoft.com/fabric/data-engineering/api-graphql-overview. + -> This item supports Service Principal authentication. + ~> This data-source is in preview. To access it, you must explicitly enable the preview mode in the provider level configuration. +--- + +# fabric_graphql_api (Data Source) + +Get a Fabric GraphQL API. + +Use this data source to fetch a [GraphQL API](https://learn.microsoft.com/fabric/data-engineering/api-graphql-overview). + +-> This item supports Service Principal authentication. + +~> This data-source is in **preview**. To access it, you must explicitly enable the `preview` mode in the provider level configuration. + +## Example Usage + +```terraform +data "fabric_graphql_api" "example_by_id" { + id = "11111111-1111-1111-1111-111111111111" + workspace_id = "00000000-0000-0000-0000-000000000000" +} + +data "fabric_graphql_api" "example_by_name" { + display_name = "example" + workspace_id = "00000000-0000-0000-0000-000000000000" +} + +# This is an invalid data source +# Do not specify `id` and `display_name` in the same data source block +# data "fabric_graphql_api" "example" { +# display_name = "example" +# id = "11111111-1111-1111-1111-111111111111" +# workspace_id = "00000000-0000-0000-0000-000000000000" +# } +``` + + +## Schema + +### Required + +- `workspace_id` (String) The Workspace ID. + +### Optional + +- `display_name` (String) The GraphQL API display name. +- `id` (String) The GraphQL API ID. +- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) + +### Read-Only + +- `description` (String) The GraphQL API description. + + + +### Nested Schema for `timeouts` + +Optional: + +- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). diff --git a/docs/data-sources/graphql_apis.md b/docs/data-sources/graphql_apis.md new file mode 100644 index 00000000..3ecf1190 --- /dev/null +++ b/docs/data-sources/graphql_apis.md @@ -0,0 +1,62 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "fabric_graphql_apis Data Source - terraform-provider-fabric" +subcategory: "" +description: |- + List a Fabric GraphQL APIs. + Use this data source to list GraphQL APIs https://learn.microsoft.com/fabric/data-engineering/api-graphql-overview. + -> This item supports Service Principal authentication. + ~> This data-source is in preview. To access it, you must explicitly enable the preview mode in the provider level configuration. +--- + +# fabric_graphql_apis (Data Source) + +List a Fabric GraphQL APIs. + +Use this data source to list [GraphQL APIs](https://learn.microsoft.com/fabric/data-engineering/api-graphql-overview). + +-> This item supports Service Principal authentication. + +~> This data-source is in **preview**. To access it, you must explicitly enable the `preview` mode in the provider level configuration. + +## Example Usage + +```terraform +data "fabric_graphql_apis" "example" { + workspace_id = "00000000-0000-0000-0000-000000000000" +} +``` + + +## Schema + +### Required + +- `workspace_id` (String) The Workspace ID. + +### Optional + +- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) + +### Read-Only + +- `values` (Attributes List) The list of GraphQL APIs. (see [below for nested schema](#nestedatt--values)) + + + +### Nested Schema for `timeouts` + +Optional: + +- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). + + + +### Nested Schema for `values` + +Read-Only: + +- `description` (String) The GraphQL API description. +- `display_name` (String) The GraphQL API display name. +- `id` (String) The GraphQL API ID. +- `workspace_id` (String) The Workspace ID. diff --git a/internal/provider/provider.go b/internal/provider/provider.go index d72682e3..97496711 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -436,6 +436,8 @@ func (p *FabricProvider) DataSources(ctx context.Context) []func() datasource.Da gateway.NewDataSourceGateway, gateway.NewDataSourceGateways, gateway.NewDataSourceGatewayRoleAssignments, + graphqlapi.NewDataSourceGraphQLApi, + graphqlapi.NewDataSourceGraphQLApis, kqldashboard.NewDataSourceKQLDashboard, kqldashboard.NewDataSourceKQLDashboards, kqldatabase.NewDataSourceKQLDatabase, From 5e278f51bf10b961e668ffd6842ea2a3427e3629 Mon Sep 17 00:00:00 2001 From: Badea Marjieh Date: Fri, 7 Mar 2025 02:45:08 +0000 Subject: [PATCH 14/14] add changelog and skip testacc for gateway role assignments CRUD for SPN --- .changes/unreleased/added-20250307-024333.yaml | 5 +++++ .../gateway/resource_gateway_role_assignment_test.go | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 .changes/unreleased/added-20250307-024333.yaml diff --git a/.changes/unreleased/added-20250307-024333.yaml b/.changes/unreleased/added-20250307-024333.yaml new file mode 100644 index 00000000..0f181e4a --- /dev/null +++ b/.changes/unreleased/added-20250307-024333.yaml @@ -0,0 +1,5 @@ +kind: added +body: Onboard new resource/data-source `fabric_gateway`. +time: 2025-03-07T02:43:33.229242334Z +custom: + Issue: "170" diff --git a/internal/services/gateway/resource_gateway_role_assignment_test.go b/internal/services/gateway/resource_gateway_role_assignment_test.go index 1daf83f4..ed895a28 100644 --- a/internal/services/gateway/resource_gateway_role_assignment_test.go +++ b/internal/services/gateway/resource_gateway_role_assignment_test.go @@ -147,6 +147,10 @@ func TestUnit_GatewayRoleAssignmentResource_ImportState(t *testing.T) { } func TestAcc_GatewayRoleAssignmentResource_CRUD(t *testing.T) { + if testhelp.ShouldSkipTest(t) { + t.Skip("No SPN support") + } + gatewayType := string(fabcore.GatewayTypeVirtualNetwork) gatewayCreateDisplayName := testhelp.RandomName() gatewayCreateInactivityMinutesBeforeSleep := int(testhelp.RandomElement(gateway.PossibleInactivityMinutesBeforeSleepValues))