From 11603af4675cdf71069d2793e6c33cfeb46dbcbf Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Tue, 16 Jan 2024 14:58:43 +0100 Subject: [PATCH 01/15] wip adding new ip addresses attribute --- go.mod | 2 + go.sum | 2 - .../service/project/data_source_project.go | 81 ++++++++++++---- .../service/project/data_source_projects.go | 2 +- internal/service/project/model_project.go | 26 ++++- .../service/project/model_project_test.go | 96 ++++++++++++++++--- 6 files changed, 178 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 84a44ac9fd..44336f7bd8 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.21 toolchain go1.21.3 +replace go.mongodb.org/atlas-sdk/v20231115003 => ../atlas-sdk-go + require ( github.com/aws/aws-sdk-go v1.49.17 github.com/go-test/deep v1.1.0 diff --git a/go.sum b/go.sum index 0bbcb169fc..aadd6b16e4 100644 --- a/go.sum +++ b/go.sum @@ -761,8 +761,6 @@ go.mongodb.org/atlas v0.36.0 h1:m05S3AO7zkl+bcG1qaNsEKBnAqnKx2FDwLooHpIG3j4= go.mongodb.org/atlas v0.36.0/go.mod h1:nfPldE9dSama6G2IbIzmEza02Ly7yFZjMMVscaM0uEc= go.mongodb.org/atlas-sdk/v20231001002 v20231001002.0.0 h1:h1X2CGKyN1UFvNs69vp7xpufbbreq6p7bbrg5uJ1sxw= go.mongodb.org/atlas-sdk/v20231001002 v20231001002.0.0/go.mod h1:4TAUPaWPFNSbi8c1hbQLr1wAdkmqi48O7zvyXjBM+a8= -go.mongodb.org/atlas-sdk/v20231115003 v20231115003.1.0 h1:31Li8Xb1THAzYfAVDR9hhAn4z9IhmFs/+AbGqADsyt8= -go.mongodb.org/atlas-sdk/v20231115003 v20231115003.1.0/go.mod h1:tXE5JorXFSauhnw9Xu+/tNrRh90rTX8rYs9y0i2Jy+c= go.mongodb.org/realm v0.1.0 h1:zJiXyLaZrznQ+Pz947ziSrDKUep39DO4SfA0Fzx8M4M= go.mongodb.org/realm v0.1.0/go.mod h1:4Vj6iy+Puo1TDERcoh4XZ+pjtwbOzPpzqy3Cwe8ZmDM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/service/project/data_source_project.go b/internal/service/project/data_source_project.go index b64436dd25..1784941aa5 100644 --- a/internal/service/project/data_source_project.go +++ b/internal/service/project/data_source_project.go @@ -31,21 +31,22 @@ type projectDS struct { } type TfProjectDSModel struct { - RegionUsageRestrictions types.String `tfsdk:"region_usage_restrictions"` - ProjectID types.String `tfsdk:"project_id"` - Name types.String `tfsdk:"name"` - OrgID types.String `tfsdk:"org_id"` - Created types.String `tfsdk:"created"` - ID types.String `tfsdk:"id"` - Limits []*TfLimitModel `tfsdk:"limits"` - Teams []*TfTeamDSModel `tfsdk:"teams"` - ClusterCount types.Int64 `tfsdk:"cluster_count"` - IsCollectDatabaseSpecificsStatisticsEnabled types.Bool `tfsdk:"is_collect_database_specifics_statistics_enabled"` - IsRealtimePerformancePanelEnabled types.Bool `tfsdk:"is_realtime_performance_panel_enabled"` - IsSchemaAdvisorEnabled types.Bool `tfsdk:"is_schema_advisor_enabled"` - IsPerformanceAdvisorEnabled types.Bool `tfsdk:"is_performance_advisor_enabled"` - IsExtendedStorageSizesEnabled types.Bool `tfsdk:"is_extended_storage_sizes_enabled"` - IsDataExplorerEnabled types.Bool `tfsdk:"is_data_explorer_enabled"` + IPAddresses *TFIPAddressesModel `tfsdk:"ip_addresses"` + Created types.String `tfsdk:"created"` + OrgID types.String `tfsdk:"org_id"` + RegionUsageRestrictions types.String `tfsdk:"region_usage_restrictions"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + ProjectID types.String `tfsdk:"project_id"` + Teams []*TfTeamDSModel `tfsdk:"teams"` + Limits []*TfLimitModel `tfsdk:"limits"` + ClusterCount types.Int64 `tfsdk:"cluster_count"` + IsCollectDatabaseSpecificsStatisticsEnabled types.Bool `tfsdk:"is_collect_database_specifics_statistics_enabled"` + IsRealtimePerformancePanelEnabled types.Bool `tfsdk:"is_realtime_performance_panel_enabled"` + IsSchemaAdvisorEnabled types.Bool `tfsdk:"is_schema_advisor_enabled"` + IsPerformanceAdvisorEnabled types.Bool `tfsdk:"is_performance_advisor_enabled"` + IsExtendedStorageSizesEnabled types.Bool `tfsdk:"is_extended_storage_sizes_enabled"` + IsDataExplorerEnabled types.Bool `tfsdk:"is_data_explorer_enabled"` } type TfTeamDSModel struct { @@ -53,6 +54,20 @@ type TfTeamDSModel struct { RoleNames types.List `tfsdk:"role_names"` } +type TFIPAddressesModel struct { + Services TFServicesModel `tfsdk:"services"` +} + +type TFServicesModel struct { + Clusters []TFClusterIPsModel `tfsdk:"clusters"` +} + +type TFClusterIPsModel struct { + ClusterName types.String `tfsdk:"cluster_name"` + Inbound types.List `tfsdk:"inbound"` + Outbound types.List `tfsdk:"outbound"` +} + func (d *projectDS) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ @@ -137,6 +152,34 @@ func (d *projectDS) Schema(ctx context.Context, req datasource.SchemaRequest, re }, }, }, + "ip_addresses": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "services": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "clusters": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "cluster_name": schema.StringAttribute{ + Computed: true, + }, + "inbound": schema.ListAttribute{ + ElementType: types.StringType, + Computed: true, + }, + "outbound": schema.ListAttribute{ + ElementType: types.StringType, + Computed: true, + }, + }, + }, + Computed: true, + }, + }, + Computed: true, + }, + }, + Computed: true, + }, }, } } @@ -179,7 +222,13 @@ func (d *projectDS) Read(ctx context.Context, req datasource.ReadRequest, resp * return } - projectState = NewTFProjectDataSourceModel(ctx, project, atlasTeams, atlasProjectSettings, atlasLimits) + ipAddresses, _, err := connV2.ProjectsApi.ReturnAllIPAddresses(ctx, project.GetId()).Execute() + if err != nil { + resp.Diagnostics.AddError("error when getting project IP addresses", fmt.Sprintf(ErrorProjectRead, project.GetId(), err.Error())) + return + } + + projectState = NewTFProjectDataSourceModel(ctx, project, atlasTeams, atlasProjectSettings, atlasLimits, ipAddresses) resp.Diagnostics.Append(resp.State.Set(ctx, &projectState)...) if resp.Diagnostics.HasError() { diff --git a/internal/service/project/data_source_projects.go b/internal/service/project/data_source_projects.go index 0681bef193..9fb5a3fc22 100644 --- a/internal/service/project/data_source_projects.go +++ b/internal/service/project/data_source_projects.go @@ -176,7 +176,7 @@ func populateProjectsDataSourceModel(ctx context.Context, connV2 *admin.APIClien project := input[i] atlasTeams, atlasLimits, atlasProjectSettings, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), project.GetId()) if err == nil { // if the project is still valid, e.g. could have just been deleted - projectModel := NewTFProjectDataSourceModel(ctx, &project, atlasTeams, atlasProjectSettings, atlasLimits) + projectModel := NewTFProjectDataSourceModel(ctx, &project, atlasTeams, atlasProjectSettings, atlasLimits, nil) // TODO adjust here results = append(results, &projectModel) } } diff --git a/internal/service/project/model_project.go b/internal/service/project/model_project.go index f8e57e13ff..85324c1e68 100644 --- a/internal/service/project/model_project.go +++ b/internal/service/project/model_project.go @@ -9,7 +9,8 @@ import ( ) func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, - teams *admin.PaginatedTeamRole, projectSettings *admin.GroupSettings, limits []admin.DataFederationLimit) TfProjectDSModel { + teams *admin.PaginatedTeamRole, projectSettings *admin.GroupSettings, limits []admin.DataFederationLimit, ipAddresses *admin.GroupIPAddresses) TfProjectDSModel { + ipAddressesModel := NewTFIPAddressesModel(ctx, ipAddresses) return TfProjectDSModel{ ID: types.StringValue(project.GetId()), ProjectID: types.StringValue(project.GetId()), @@ -25,6 +26,7 @@ func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, IsSchemaAdvisorEnabled: types.BoolValue(*projectSettings.IsSchemaAdvisorEnabled), Teams: NewTFTeamsDataSourceModel(ctx, teams), Limits: NewTFLimitsDataSourceModel(ctx, limits), + IPAddresses: &ipAddressesModel, } } @@ -60,6 +62,28 @@ func NewTFLimitsDataSourceModel(ctx context.Context, dataFederationLimits []admi return limits } +func NewTFIPAddressesModel(ctx context.Context, ipAddresses *admin.GroupIPAddresses) TFIPAddressesModel { + clusterIPs := []TFClusterIPsModel{} + if ipAddresses != nil && ipAddresses.Services != nil { + clusterIPAddresses := ipAddresses.Services.GetClusters() + clusterIPs = make([]TFClusterIPsModel, len(clusterIPAddresses)) + for i := range clusterIPAddresses { + inbound, _ := types.ListValueFrom(ctx, types.StringType, clusterIPAddresses[i].GetInbound()) + outbound, _ := types.ListValueFrom(ctx, types.StringType, clusterIPAddresses[i].GetOutbound()) + clusterIPs[i] = TFClusterIPsModel{ + ClusterName: types.StringPointerValue(clusterIPAddresses[i].ClusterName), + Inbound: inbound, + Outbound: outbound, + } + } + } + return TFIPAddressesModel{ + Services: TFServicesModel{ + Clusters: clusterIPs, + }, + } +} + func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, teams *admin.PaginatedTeamRole, projectSettings *admin.GroupSettings, limits []admin.DataFederationLimit) *TfProjectRSModel { projectPlan := TfProjectRSModel{ diff --git a/internal/service/project/model_project_test.go b/internal/service/project/model_project_test.go index d756ea00fe..f5c7670781 100644 --- a/internal/service/project/model_project_test.go +++ b/internal/service/project/model_project_test.go @@ -2,12 +2,12 @@ package project_test import ( "context" - "reflect" "testing" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion" "github.com/mongodb/terraform-provider-mongodbatlas/internal/service/project" + "github.com/stretchr/testify/assert" "go.mongodb.org/atlas-sdk/v20231115003/admin" ) @@ -25,9 +25,11 @@ const ( ) var ( - roles = []string{"GROUP_DATA_ACCESS_READ_ONLY", "GROUP_CLUSTER_MANAGER"} - roleList, _ = types.ListValueFrom(context.Background(), types.StringType, roles) - teamRolesSDK = []admin.TeamRole{ + roles = []string{"GROUP_DATA_ACCESS_READ_ONLY", "GROUP_CLUSTER_MANAGER"} + roleList, _ = types.ListValueFrom(context.Background(), types.StringType, roles) + ipAddresses = []string{"13.13.13.13"} + ipAddressesList, _ = types.ListValueFrom(context.Background(), types.StringType, ipAddresses) + teamRolesSDK = []admin.TeamRole{ { TeamId: conversion.StringPtr("teamId"), RoleNames: conversion.NonEmptyToPtr(roles), @@ -57,6 +59,17 @@ var ( MaximumLimit: types.Int64Value(limitMaximumLimit), }, } + ipAddressesTF = project.TFIPAddressesModel{ + Services: project.TFServicesModel{ + Clusters: []project.TFClusterIPsModel{ + { + Inbound: ipAddressesList, + Outbound: ipAddressesList, + ClusterName: types.StringValue("Cluster0"), + }, + }, + }, + } projectSDK = admin.Group{ Id: admin.PtrString(projectID), Name: projectName, @@ -71,6 +84,18 @@ var ( IsRealtimePerformancePanelEnabled: admin.PtrBool(true), IsSchemaAdvisorEnabled: admin.PtrBool(true), } + projectIPAddressesSDK = admin.GroupIPAddresses{ + GroupId: admin.PtrString(projectID), + Services: &admin.GroupService{ + Clusters: &[]admin.ClusterIPAddresses{ + { + Inbound: &[]string{"13.13.13.13"}, + Outbound: &[]string{"13.13.13.13"}, + ClusterName: admin.PtrString("Cluster0"), + }, + }, + }, + } ) func TestTeamsDataSourceSDKToTFModel(t *testing.T) { @@ -98,7 +123,7 @@ func TestTeamsDataSourceSDKToTFModel(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { resultModel := project.NewTFTeamsDataSourceModel(context.Background(), tc.paginatedTeamRole) - if !reflect.DeepEqual(resultModel, tc.expectedTFModel) { + if !assert.Equal(t, tc.expectedTFModel, resultModel) { t.Errorf("created terraform model did not match expected output") } }) @@ -121,7 +146,7 @@ func TestLimitsDataSourceSDKToTFModel(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { resultModel := project.NewTFLimitsDataSourceModel(context.Background(), tc.dataFederationLimits) - if !reflect.DeepEqual(resultModel, tc.expectedTFModel) { + if !assert.Equal(t, tc.expectedTFModel, resultModel) { t.Errorf("created terraform model did not match expected output") } }) @@ -134,6 +159,7 @@ func TestProjectDataSourceSDKToTFModel(t *testing.T) { project *admin.Group teams *admin.PaginatedTeamRole projectSettings *admin.GroupSettings + ipAddresses *admin.GroupIPAddresses dataFederationLimits []admin.DataFederationLimit expectedTFModel project.TfProjectDSModel }{ @@ -145,6 +171,7 @@ func TestProjectDataSourceSDKToTFModel(t *testing.T) { TotalCount: conversion.IntPtr(1), }, projectSettings: &projectSettingsSDK, + ipAddresses: &projectIPAddressesSDK, dataFederationLimits: limitsSDK, expectedTFModel: project.TfProjectDSModel{ @@ -161,6 +188,7 @@ func TestProjectDataSourceSDKToTFModel(t *testing.T) { IsSchemaAdvisorEnabled: types.BoolValue(true), Teams: teamsDSTF, Limits: limitsTF, + IPAddresses: &ipAddressesTF, Created: types.StringValue("0001-01-01T00:00:00Z"), }, }, @@ -168,8 +196,8 @@ func TestProjectDataSourceSDKToTFModel(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - resultModel := project.NewTFProjectDataSourceModel(context.Background(), tc.project, tc.teams, tc.projectSettings, tc.dataFederationLimits) - if !reflect.DeepEqual(resultModel, tc.expectedTFModel) { + resultModel := project.NewTFProjectDataSourceModel(context.Background(), tc.project, tc.teams, tc.projectSettings, tc.dataFederationLimits, tc.ipAddresses) + if !assert.Equal(t, tc.expectedTFModel, resultModel) { t.Errorf("created terraform model did not match expected output") } }) @@ -199,7 +227,7 @@ func TestTeamRoleListTFtoSDK(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { resultModel := project.NewTeamRoleList(context.Background(), tc.teamRolesTF) - if !reflect.DeepEqual(resultModel, tc.expectedResult) { + if !assert.Equal(t, tc.expectedResult, resultModel) { t.Errorf("created terraform model did not match expected output") } }) @@ -233,7 +261,7 @@ func TestTeamModelMapTF(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { resultModel := project.NewTfTeamModelMap(tc.teamRolesTF) - if !reflect.DeepEqual(resultModel, tc.expectedResult) { + if !assert.Equal(t, tc.expectedResult, resultModel) { t.Errorf("created terraform model did not match expected output") } }) @@ -267,7 +295,53 @@ func TestLimitModelMapTF(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { resultModel := project.NewTfLimitModelMap(tc.limitsTF) - if !reflect.DeepEqual(resultModel, tc.expectedResult) { + if !assert.Equal(t, tc.expectedResult, resultModel) { + t.Errorf("created terraform model did not match expected output") + } + }) + } +} + +func TestIPAddressesModelToTF(t *testing.T) { + testCases := []struct { + name string + sdkModel *admin.GroupIPAddresses + expectedResult project.TFIPAddressesModel + }{ + { + name: "No response", + sdkModel: nil, + expectedResult: project.TFIPAddressesModel{ + Services: project.TFServicesModel{ + Clusters: []project.TFClusterIPsModel{}, + }, + }, + }, + { + name: "Empty response when no clusters are created", + sdkModel: &admin.GroupIPAddresses{ + GroupId: admin.PtrString(projectID), + Services: &admin.GroupService{ + Clusters: &[]admin.ClusterIPAddresses{}, + }, + }, + expectedResult: project.TFIPAddressesModel{ + Services: project.TFServicesModel{ + Clusters: []project.TFClusterIPsModel{}, + }, + }, + }, + { + name: "Full response", + sdkModel: &projectIPAddressesSDK, + expectedResult: ipAddressesTF, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + resultModel := project.NewTFIPAddressesModel(context.Background(), tc.sdkModel) + if !assert.Equal(t, tc.expectedResult, resultModel) { t.Errorf("created terraform model did not match expected output") } }) From 374446e82bbb014349377f7c3ec19917ff7443fc Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Wed, 17 Jan 2024 10:50:07 +0100 Subject: [PATCH 02/15] support plural data source --- .github/workflows/code-health.yml | 2 +- .mockery.yaml | 16 +++++--- GNUmakefile | 4 ++ .../service/project/data_source_project.go | 8 +--- .../service/project/data_source_projects.go | 32 ++++++++++++++- internal/service/project/resource_project.go | 23 +++++++---- .../service/project/resource_project_test.go | 32 ++++++++++++--- internal/service/project/service_project.go | 5 +++ .../testutil/mocksvc/group_project_service.go | 39 +++++++++++++++++++ 9 files changed, 132 insertions(+), 29 deletions(-) diff --git a/.github/workflows/code-health.yml b/.github/workflows/code-health.yml index 372f73c4b1..fcd96a246d 100644 --- a/.github/workflows/code-health.yml +++ b/.github/workflows/code-health.yml @@ -21,7 +21,7 @@ jobs: with: go-version-file: 'go.mod' - name: Mock generation - run: make tools && mockery + run: make tools && make generate-mocks - name: Check for uncommited files run: | export FILES=$(git ls-files -o -m --directory --exclude-standard --no-empty-directory) diff --git a/.mockery.yaml b/.mockery.yaml index de719f7fb9..220097ec86 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -6,12 +6,18 @@ filename: "{{ .InterfaceName | snakecase }}.go" mockname: "{{.InterfaceName}}" packages: - ? github.com/mongodb/terraform-provider-mongodbatlas/internal/service/searchdeployment - ? github.com/mongodb/terraform-provider-mongodbatlas/internal/service/encryptionatrest - ? github.com/mongodb/terraform-provider-mongodbatlas/internal/service/project - ? github.com/mongodb/terraform-provider-mongodbatlas/internal/service/advancedcluster - : interfaces: + github.com/mongodb/terraform-provider-mongodbatlas/internal/service/searchdeployment: + interfaces: DeploymentService: + + github.com/mongodb/terraform-provider-mongodbatlas/internal/service/encryptionatrest: + interfaces: EarService: + + github.com/mongodb/terraform-provider-mongodbatlas/internal/service/project: + interfaces: GroupProjectService: + + github.com/mongodb/terraform-provider-mongodbatlas/internal/service/advancedcluster: + interfaces: ClusterService: diff --git a/GNUmakefile b/GNUmakefile index a68b4d3f7f..0bbd63adba 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -115,6 +115,10 @@ link-git-hooks: ## Install git hooks update-atlas-sdk: ## Update the atlas-sdk dependency ./scripts/update-sdk.sh +.PHONY: generate-mocks +generate-mocks: # uses mockery to generate mocks in folder `internal/testutil/mocksvc` + mockery + # e.g. run: make scaffold resource_name=streamInstance type=resource # - type argument can have the values: `resource`, `data-source`, `plural-data-source`. # details on usage can be found in CONTRIBUTING.md under "Scaffolding initial Code and File Structure" diff --git a/internal/service/project/data_source_project.go b/internal/service/project/data_source_project.go index 1784941aa5..9473a4339b 100644 --- a/internal/service/project/data_source_project.go +++ b/internal/service/project/data_source_project.go @@ -216,18 +216,12 @@ func (d *projectDS) Read(ctx context.Context, req datasource.ReadRequest, resp * } } - atlasTeams, atlasLimits, atlasProjectSettings, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), project.GetId()) + atlasTeams, atlasLimits, atlasProjectSettings, ipAddresses, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), project.GetId()) if err != nil { resp.Diagnostics.AddError("error when getting project properties", fmt.Sprintf(ErrorProjectRead, project.GetId(), err.Error())) return } - ipAddresses, _, err := connV2.ProjectsApi.ReturnAllIPAddresses(ctx, project.GetId()).Execute() - if err != nil { - resp.Diagnostics.AddError("error when getting project IP addresses", fmt.Sprintf(ErrorProjectRead, project.GetId(), err.Error())) - return - } - projectState = NewTFProjectDataSourceModel(ctx, project, atlasTeams, atlasProjectSettings, atlasLimits, ipAddresses) resp.Diagnostics.Append(resp.State.Set(ctx, &projectState)...) diff --git a/internal/service/project/data_source_projects.go b/internal/service/project/data_source_projects.go index 9fb5a3fc22..2f349188b7 100644 --- a/internal/service/project/data_source_projects.go +++ b/internal/service/project/data_source_projects.go @@ -134,6 +134,34 @@ func (d *ProjectsDS) Schema(ctx context.Context, req datasource.SchemaRequest, r }, }, }, + "ip_addresses": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "services": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "clusters": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "cluster_name": schema.StringAttribute{ + Computed: true, + }, + "inbound": schema.ListAttribute{ + ElementType: types.StringType, + Computed: true, + }, + "outbound": schema.ListAttribute{ + ElementType: types.StringType, + Computed: true, + }, + }, + }, + Computed: true, + }, + }, + Computed: true, + }, + }, + Computed: true, + }, }, }, }, @@ -174,9 +202,9 @@ func populateProjectsDataSourceModel(ctx context.Context, connV2 *admin.APIClien results := make([]*TfProjectDSModel, 0, len(input)) for i := range input { project := input[i] - atlasTeams, atlasLimits, atlasProjectSettings, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), project.GetId()) + atlasTeams, atlasLimits, atlasProjectSettings, ipAddresses, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), project.GetId()) if err == nil { // if the project is still valid, e.g. could have just been deleted - projectModel := NewTFProjectDataSourceModel(ctx, &project, atlasTeams, atlasProjectSettings, atlasLimits, nil) // TODO adjust here + projectModel := NewTFProjectDataSourceModel(ctx, &project, atlasTeams, atlasProjectSettings, atlasLimits, ipAddresses) results = append(results, &projectModel) } } diff --git a/internal/service/project/resource_project.go b/internal/service/project/resource_project.go index 584699ddd4..19eeb8fd9f 100644 --- a/internal/service/project/resource_project.go +++ b/internal/service/project/resource_project.go @@ -354,7 +354,8 @@ func (r *projectRS) Create(ctx context.Context, req resource.CreateRequest, resp } // get project props - atlasTeams, atlasLimits, atlasProjectSettings, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) + // TODO: implement resource + atlasTeams, atlasLimits, atlasProjectSettings, _, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) if err != nil { resp.Diagnostics.AddError("error when getting project properties after create", fmt.Sprintf(ErrorProjectRead, projectID, err.Error())) return @@ -400,7 +401,7 @@ func (r *projectRS) Read(ctx context.Context, req resource.ReadRequest, resp *re } // get project props - atlasTeams, atlasLimits, atlasProjectSettings, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) + atlasTeams, atlasLimits, atlasProjectSettings, _, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) if err != nil { resp.Diagnostics.AddError("error when getting project properties after create", fmt.Sprintf(ErrorProjectRead, projectID, err.Error())) return @@ -469,7 +470,7 @@ func (r *projectRS) Update(ctx context.Context, req resource.UpdateRequest, resp } // get project props - atlasTeams, atlasLimits, atlasProjectSettings, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) + atlasTeams, atlasLimits, atlasProjectSettings, _, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) if err != nil { resp.Diagnostics.AddError("error when getting project properties after create", fmt.Sprintf(ErrorProjectRead, projectID, err.Error())) return @@ -530,23 +531,29 @@ func FilterUserDefinedLimits(allAtlasLimits []admin.DataFederationLimit, tflimit return filteredLimits } -func GetProjectPropsFromAPI(ctx context.Context, client GroupProjectService, projectID string) (*admin.PaginatedTeamRole, []admin.DataFederationLimit, *admin.GroupSettings, error) { +// TODO refactor resp into struct +func GetProjectPropsFromAPI(ctx context.Context, client GroupProjectService, projectID string) (*admin.PaginatedTeamRole, []admin.DataFederationLimit, *admin.GroupSettings, *admin.GroupIPAddresses, error) { teams, _, err := client.ListProjectTeams(ctx, projectID) if err != nil { - return nil, nil, nil, fmt.Errorf("error getting project's teams assigned (%s): %v", projectID, err.Error()) + return nil, nil, nil, nil, fmt.Errorf("error getting project's teams assigned (%s): %v", projectID, err.Error()) } limits, _, err := client.ListProjectLimits(ctx, projectID) if err != nil { - return nil, nil, nil, fmt.Errorf("error getting project's limits (%s): %s", projectID, err.Error()) + return nil, nil, nil, nil, fmt.Errorf("error getting project's limits (%s): %s", projectID, err.Error()) } projectSettings, _, err := client.GetProjectSettings(ctx, projectID) if err != nil { - return nil, nil, nil, fmt.Errorf("error getting project's settings assigned (%s): %v", projectID, err.Error()) + return nil, nil, nil, nil, fmt.Errorf("error getting project's settings assigned (%s): %v", projectID, err.Error()) } - return teams, limits, projectSettings, nil + ipAddresses, _, err := client.ReturnAllIPAddresses(ctx, projectID) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("error getting project's IP addresses (%s): %v", projectID, err.Error()) + } + + return teams, limits, projectSettings, ipAddresses, nil } func updateProjectSettings(ctx context.Context, connV2 *admin.APIClient, projectState, projectPlan *TfProjectRSModel) error { diff --git a/internal/service/project/resource_project_test.go b/internal/service/project/resource_project_test.go index 4864953d4d..7033b381a9 100644 --- a/internal/service/project/resource_project_test.go +++ b/internal/service/project/resource_project_test.go @@ -47,11 +47,12 @@ func TestGetProjectPropsFromAPI(t *testing.T) { Err: nil, } testCases := []struct { - name string - teamRoleReponse TeamRoleResponse - groupResponse GroupSettingsResponse - limitResponse LimitsResponse - expectedError bool + teamRoleReponse TeamRoleResponse + groupResponse GroupSettingsResponse + ipAddressesResponse IPAddressesResponse + name string + limitResponse LimitsResponse + expectedError bool }{ { name: "Successful", @@ -90,6 +91,18 @@ func TestGetProjectPropsFromAPI(t *testing.T) { }, expectedError: true, }, + { + name: "Fail to get project's ip addresses", + teamRoleReponse: successfulTeamRoleResponse, + limitResponse: successfulLimitsResponse, + groupResponse: successfulGroupSettingsResponse, + ipAddressesResponse: IPAddressesResponse{ + IPAddresses: nil, + HTTPResponse: &http.Response{StatusCode: 503}, + Err: errors.New("Service Unavailable"), + }, + expectedError: true, + }, } for _, tc := range testCases { @@ -98,8 +111,9 @@ func TestGetProjectPropsFromAPI(t *testing.T) { svc.On("ListProjectTeams", mock.Anything, mock.Anything).Return(tc.teamRoleReponse.TeamRole, tc.teamRoleReponse.HTTPResponse, tc.teamRoleReponse.Err) svc.On("ListProjectLimits", mock.Anything, mock.Anything).Return(tc.limitResponse.Limits, tc.limitResponse.HTTPResponse, tc.limitResponse.Err).Maybe() svc.On("GetProjectSettings", mock.Anything, mock.Anything).Return(tc.groupResponse.GroupSettings, tc.groupResponse.HTTPResponse, tc.groupResponse.Err).Maybe() + svc.On("ReturnAllIPAddresses", mock.Anything, mock.Anything).Return(tc.groupResponse.GroupSettings, tc.groupResponse.HTTPResponse, tc.groupResponse.Err).Maybe() - _, _, _, err := project.GetProjectPropsFromAPI(context.Background(), svc, dummyProjectID) + _, _, _, _, err := project.GetProjectPropsFromAPI(context.Background(), svc, dummyProjectID) if (err != nil) != tc.expectedError { t.Errorf("Case %s: Received unexpected error: %v", tc.name, err) @@ -898,6 +912,12 @@ type GroupSettingsResponse struct { HTTPResponse *http.Response Err error } + +type IPAddressesResponse struct { + IPAddresses *admin.GroupIPAddresses + HTTPResponse *http.Response + Err error +} type ProjectResponse struct { Project *admin.Group HTTPResponse *http.Response diff --git a/internal/service/project/service_project.go b/internal/service/project/service_project.go index 2feb333e55..d0b4d114ce 100644 --- a/internal/service/project/service_project.go +++ b/internal/service/project/service_project.go @@ -18,6 +18,7 @@ type GroupProjectService interface { UpdateTeamRoles(ctx context.Context, groupID, teamID string, teamRole *admin.TeamRole) (*admin.PaginatedTeamRole, *http.Response, error) AddAllTeamsToProject(ctx context.Context, groupID string, teamRole *[]admin.TeamRole) (*admin.PaginatedTeamRole, *http.Response, error) ListClusters(ctx context.Context, groupID string) (*admin.PaginatedAdvancedClusterDescription, *http.Response, error) + ReturnAllIPAddresses(ctx context.Context, groupID string) (*admin.GroupIPAddresses, *http.Response, error) } type GroupProjectServiceFromClient struct { @@ -65,6 +66,10 @@ func (a *GroupProjectServiceFromClient) ListClusters(ctx context.Context, groupI return a.client.ClustersApi.ListClusters(ctx, groupID).Execute() } +func (a *GroupProjectServiceFromClient) ReturnAllIPAddresses(ctx context.Context, groupID string) (*admin.GroupIPAddresses, *http.Response, error) { + return a.client.ProjectsApi.ReturnAllIPAddresses(ctx, groupID).Execute() +} + func ServiceFromClient(client *admin.APIClient) GroupProjectService { return &GroupProjectServiceFromClient{ client: client, diff --git a/internal/testutil/mocksvc/group_project_service.go b/internal/testutil/mocksvc/group_project_service.go index e9adc3f78d..2ff915eb2b 100644 --- a/internal/testutil/mocksvc/group_project_service.go +++ b/internal/testutil/mocksvc/group_project_service.go @@ -281,6 +281,45 @@ func (_m *GroupProjectService) RemoveProjectTeam(ctx context.Context, groupID st return r0, r1 } +// ReturnAllIPAddresses provides a mock function with given fields: ctx, groupID +func (_m *GroupProjectService) ReturnAllIPAddresses(ctx context.Context, groupID string) (*admin.GroupIPAddresses, *http.Response, error) { + ret := _m.Called(ctx, groupID) + + if len(ret) == 0 { + panic("no return value specified for ReturnAllIPAddresses") + } + + var r0 *admin.GroupIPAddresses + var r1 *http.Response + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*admin.GroupIPAddresses, *http.Response, error)); ok { + return rf(ctx, groupID) + } + if rf, ok := ret.Get(0).(func(context.Context, string) *admin.GroupIPAddresses); ok { + r0 = rf(ctx, groupID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*admin.GroupIPAddresses) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) *http.Response); ok { + r1 = rf(ctx, groupID) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*http.Response) + } + } + + if rf, ok := ret.Get(2).(func(context.Context, string) error); ok { + r2 = rf(ctx, groupID) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // SetProjectLimit provides a mock function with given fields: ctx, limitName, groupID, dataFederationLimit func (_m *GroupProjectService) SetProjectLimit(ctx context.Context, limitName string, groupID string, dataFederationLimit *admin.DataFederationLimit) (*admin.DataFederationLimit, *http.Response, error) { ret := _m.Called(ctx, limitName, groupID, dataFederationLimit) From 0d259a6cea7f5b44395e6b53f08e98a570bb0473 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Wed, 17 Jan 2024 11:29:32 +0100 Subject: [PATCH 03/15] update sdk version to dev-latest --- go.mod | 4 +--- go.sum | 2 ++ .../data_source_federated_settings_identity_providers.go | 2 +- .../resource_federated_settings_identity_provider.go | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 44336f7bd8..87b95048a8 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,6 @@ go 1.21 toolchain go1.21.3 -replace go.mongodb.org/atlas-sdk/v20231115003 => ../atlas-sdk-go - require ( github.com/aws/aws-sdk-go v1.49.17 github.com/go-test/deep v1.1.0 @@ -29,7 +27,7 @@ require ( github.com/zclconf/go-cty v1.14.1 go.mongodb.org/atlas v0.36.0 go.mongodb.org/atlas-sdk/v20231001002 v20231001002.0.0 - go.mongodb.org/atlas-sdk/v20231115003 v20231115003.1.0 + go.mongodb.org/atlas-sdk/v20231115003 v20231115003.1.1-0.20240117091756-3da8be8d2750 go.mongodb.org/realm v0.1.0 golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 ) diff --git a/go.sum b/go.sum index aadd6b16e4..7fcfbbfe6f 100644 --- a/go.sum +++ b/go.sum @@ -761,6 +761,8 @@ go.mongodb.org/atlas v0.36.0 h1:m05S3AO7zkl+bcG1qaNsEKBnAqnKx2FDwLooHpIG3j4= go.mongodb.org/atlas v0.36.0/go.mod h1:nfPldE9dSama6G2IbIzmEza02Ly7yFZjMMVscaM0uEc= go.mongodb.org/atlas-sdk/v20231001002 v20231001002.0.0 h1:h1X2CGKyN1UFvNs69vp7xpufbbreq6p7bbrg5uJ1sxw= go.mongodb.org/atlas-sdk/v20231001002 v20231001002.0.0/go.mod h1:4TAUPaWPFNSbi8c1hbQLr1wAdkmqi48O7zvyXjBM+a8= +go.mongodb.org/atlas-sdk/v20231115003 v20231115003.1.1-0.20240117091756-3da8be8d2750 h1:6YoLpLGBjtYT3T6Qn2rvCQTbFrW+mrePopJpkaw5qRk= +go.mongodb.org/atlas-sdk/v20231115003 v20231115003.1.1-0.20240117091756-3da8be8d2750/go.mod h1:tXE5JorXFSauhnw9Xu+/tNrRh90rTX8rYs9y0i2Jy+c= go.mongodb.org/realm v0.1.0 h1:zJiXyLaZrznQ+Pz947ziSrDKUep39DO4SfA0Fzx8M4M= go.mongodb.org/realm v0.1.0/go.mod h1:4Vj6iy+Puo1TDERcoh4XZ+pjtwbOzPpzqy3Cwe8ZmDM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_providers.go b/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_providers.go index 9bab4719a1..5f7137fe3e 100644 --- a/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_providers.go +++ b/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_providers.go @@ -227,7 +227,7 @@ func dataSourceMongoDBAtlasFederatedSettingsIdentityProvidersRead(ctx context.Co return diag.Errorf("error getting federatedSettings IdentityProviders assigned (%s): %s", federationSettingsID, err) } - if err := d.Set("results", FlattenFederatedSettingsIdentityProvider(federatedSettingsIdentityProviders)); err != nil { + if err := d.Set("results", FlattenFederatedSettingsIdentityProvider(federatedSettingsIdentityProviders.GetResults())); err != nil { return diag.FromErr(fmt.Errorf("error setting `result` for federatedSettings IdentityProviders: %s", err)) } diff --git a/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider.go b/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider.go index f566e7db02..eebe266cf8 100644 --- a/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider.go +++ b/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider.go @@ -224,7 +224,7 @@ func resourceMongoDBAtlasFederatedSettingsIdentityProviderUpdate(ctx context.Con return append(oldSDKUpdate(ctx, federationSettingsID, oktaIdpID, d, meta), getGracePeriodWarning()) } - var updateRequest *admin.IdentityProviderUpdate + var updateRequest *admin.FederationIdentityProviderUpdate _, _, err := connV2.FederatedAuthenticationApi.GetIdentityProvider(context.Background(), federationSettingsID, oktaIdpID).Execute() if err != nil { From 71971b1aac3dce5546789853048e122dec7662ea Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Wed, 17 Jan 2024 12:11:51 +0100 Subject: [PATCH 04/15] include unit test for resource terraform model --- .../service/project/data_source_project.go | 14 --- internal/service/project/model_project.go | 4 +- .../service/project/model_project_test.go | 62 ++++++++++++- internal/service/project/resource_project.go | 88 ++++++++++++++----- 4 files changed, 129 insertions(+), 39 deletions(-) diff --git a/internal/service/project/data_source_project.go b/internal/service/project/data_source_project.go index 9473a4339b..5d31b04bc0 100644 --- a/internal/service/project/data_source_project.go +++ b/internal/service/project/data_source_project.go @@ -54,20 +54,6 @@ type TfTeamDSModel struct { RoleNames types.List `tfsdk:"role_names"` } -type TFIPAddressesModel struct { - Services TFServicesModel `tfsdk:"services"` -} - -type TFServicesModel struct { - Clusters []TFClusterIPsModel `tfsdk:"clusters"` -} - -type TFClusterIPsModel struct { - ClusterName types.String `tfsdk:"cluster_name"` - Inbound types.List `tfsdk:"inbound"` - Outbound types.List `tfsdk:"outbound"` -} - func (d *projectDS) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ diff --git a/internal/service/project/model_project.go b/internal/service/project/model_project.go index 85324c1e68..5c28c3b221 100644 --- a/internal/service/project/model_project.go +++ b/internal/service/project/model_project.go @@ -85,7 +85,8 @@ func NewTFIPAddressesModel(ctx context.Context, ipAddresses *admin.GroupIPAddres } func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, - teams *admin.PaginatedTeamRole, projectSettings *admin.GroupSettings, limits []admin.DataFederationLimit) *TfProjectRSModel { + teams *admin.PaginatedTeamRole, projectSettings *admin.GroupSettings, limits []admin.DataFederationLimit, ipAddresses *admin.GroupIPAddresses) *TfProjectRSModel { + ipAddressesModel := NewTFIPAddressesModel(ctx, ipAddresses) projectPlan := TfProjectRSModel{ ID: types.StringValue(projectRes.GetId()), Name: types.StringValue(projectRes.Name), @@ -95,6 +96,7 @@ func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, WithDefaultAlertsSettings: types.BoolPointerValue(projectRes.WithDefaultAlertsSettings), Teams: newTFTeamsResourceModel(ctx, teams), Limits: newTFLimitsResourceModel(ctx, limits), + IPAddresses: &ipAddressesModel, } if projectSettings != nil { diff --git a/internal/service/project/model_project_test.go b/internal/service/project/model_project_test.go index f5c7670781..8c8297cbc6 100644 --- a/internal/service/project/model_project_test.go +++ b/internal/service/project/model_project_test.go @@ -27,6 +27,7 @@ const ( var ( roles = []string{"GROUP_DATA_ACCESS_READ_ONLY", "GROUP_CLUSTER_MANAGER"} roleList, _ = types.ListValueFrom(context.Background(), types.StringType, roles) + roleSet, _ = types.SetValueFrom(context.Background(), types.StringType, roles) ipAddresses = []string{"13.13.13.13"} ipAddressesList, _ = types.ListValueFrom(context.Background(), types.StringType, ipAddresses) teamRolesSDK = []admin.TeamRole{ @@ -41,6 +42,12 @@ var ( RoleNames: roleList, }, } + teamsTFSet, _ = types.SetValueFrom(context.Background(), project.TfTeamObjectType, []project.TfTeamModel{ + { + TeamID: types.StringValue("teamId"), + RoleNames: roleSet, + }, + }) limitsSDK = []admin.DataFederationLimit{ { Name: limitName, @@ -59,6 +66,9 @@ var ( MaximumLimit: types.Int64Value(limitMaximumLimit), }, } + limitsTFSet, _ = types.SetValueFrom(context.Background(), project.TfLimitObjectType, []project.TfLimitModel{ + *limitsTF[0], + }) ipAddressesTF = project.TFIPAddressesModel{ Services: project.TFServicesModel{ Clusters: []project.TFClusterIPsModel{ @@ -153,7 +163,7 @@ func TestLimitsDataSourceSDKToTFModel(t *testing.T) { } } -func TestProjectDataSourceSDKToTFModel(t *testing.T) { +func TestProjectDataSourceSDKToDataSourceTFModel(t *testing.T) { testCases := []struct { name string project *admin.Group @@ -204,6 +214,56 @@ func TestProjectDataSourceSDKToTFModel(t *testing.T) { } } +func TestProjectDataSourceSDKToResourceTFModel(t *testing.T) { + testCases := []struct { + name string + project *admin.Group + teams *admin.PaginatedTeamRole + projectSettings *admin.GroupSettings + ipAddresses *admin.GroupIPAddresses + dataFederationLimits []admin.DataFederationLimit + expectedTFModel project.TfProjectRSModel + }{ + { + name: "Project", + project: &projectSDK, + teams: &admin.PaginatedTeamRole{ + Results: conversion.NonEmptyToPtr(teamRolesSDK), + TotalCount: conversion.IntPtr(1), + }, + projectSettings: &projectSettingsSDK, + ipAddresses: &projectIPAddressesSDK, + dataFederationLimits: limitsSDK, + expectedTFModel: project.TfProjectRSModel{ + + ID: types.StringValue(projectID), + Name: types.StringValue(projectName), + OrgID: types.StringValue(projectOrgID), + ClusterCount: types.Int64Value(clusterCount), + IsCollectDatabaseSpecificsStatisticsEnabled: types.BoolValue(true), + IsDataExplorerEnabled: types.BoolValue(true), + IsExtendedStorageSizesEnabled: types.BoolValue(true), + IsPerformanceAdvisorEnabled: types.BoolValue(true), + IsRealtimePerformancePanelEnabled: types.BoolValue(true), + IsSchemaAdvisorEnabled: types.BoolValue(true), + Teams: teamsTFSet, + Limits: limitsTFSet, + IPAddresses: &ipAddressesTF, + Created: types.StringValue("0001-01-01T00:00:00Z"), + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + resultModel := project.NewTFProjectResourceModel(context.Background(), tc.project, tc.teams, tc.projectSettings, tc.dataFederationLimits, tc.ipAddresses) + if !assert.Equal(t, tc.expectedTFModel, *resultModel) { + t.Errorf("created terraform model did not match expected output") + } + }) + } +} + func TestTeamRoleListTFtoSDK(t *testing.T) { var rolesSet, _ = types.SetValueFrom(context.Background(), types.StringType, roles) teamsTF := []project.TfTeamModel{ diff --git a/internal/service/project/resource_project.go b/internal/service/project/resource_project.go index 19eeb8fd9f..1534ffbdb3 100644 --- a/internal/service/project/resource_project.go +++ b/internal/service/project/resource_project.go @@ -54,22 +54,23 @@ type projectRS struct { } type TfProjectRSModel struct { - Limits types.Set `tfsdk:"limits"` - Teams types.Set `tfsdk:"teams"` - RegionUsageRestrictions types.String `tfsdk:"region_usage_restrictions"` - Name types.String `tfsdk:"name"` - OrgID types.String `tfsdk:"org_id"` - Created types.String `tfsdk:"created"` - ProjectOwnerID types.String `tfsdk:"project_owner_id"` - ID types.String `tfsdk:"id"` - ClusterCount types.Int64 `tfsdk:"cluster_count"` - IsDataExplorerEnabled types.Bool `tfsdk:"is_data_explorer_enabled"` - IsPerformanceAdvisorEnabled types.Bool `tfsdk:"is_performance_advisor_enabled"` - IsRealtimePerformancePanelEnabled types.Bool `tfsdk:"is_realtime_performance_panel_enabled"` - IsSchemaAdvisorEnabled types.Bool `tfsdk:"is_schema_advisor_enabled"` - IsExtendedStorageSizesEnabled types.Bool `tfsdk:"is_extended_storage_sizes_enabled"` - IsCollectDatabaseSpecificsStatisticsEnabled types.Bool `tfsdk:"is_collect_database_specifics_statistics_enabled"` - WithDefaultAlertsSettings types.Bool `tfsdk:"with_default_alerts_settings"` + Limits types.Set `tfsdk:"limits"` + Teams types.Set `tfsdk:"teams"` + IPAddresses *TFIPAddressesModel `tfsdk:"ip_addresses"` + RegionUsageRestrictions types.String `tfsdk:"region_usage_restrictions"` + Name types.String `tfsdk:"name"` + OrgID types.String `tfsdk:"org_id"` + Created types.String `tfsdk:"created"` + ProjectOwnerID types.String `tfsdk:"project_owner_id"` + ID types.String `tfsdk:"id"` + ClusterCount types.Int64 `tfsdk:"cluster_count"` + IsDataExplorerEnabled types.Bool `tfsdk:"is_data_explorer_enabled"` + IsPerformanceAdvisorEnabled types.Bool `tfsdk:"is_performance_advisor_enabled"` + IsRealtimePerformancePanelEnabled types.Bool `tfsdk:"is_realtime_performance_panel_enabled"` + IsSchemaAdvisorEnabled types.Bool `tfsdk:"is_schema_advisor_enabled"` + IsExtendedStorageSizesEnabled types.Bool `tfsdk:"is_extended_storage_sizes_enabled"` + IsCollectDatabaseSpecificsStatisticsEnabled types.Bool `tfsdk:"is_collect_database_specifics_statistics_enabled"` + WithDefaultAlertsSettings types.Bool `tfsdk:"with_default_alerts_settings"` } type TfTeamModel struct { @@ -85,6 +86,20 @@ type TfLimitModel struct { MaximumLimit types.Int64 `tfsdk:"maximum_limit"` } +type TFIPAddressesModel struct { // TODO: consistency in Tf vs TF + Services TFServicesModel `tfsdk:"services"` +} + +type TFServicesModel struct { + Clusters []TFClusterIPsModel `tfsdk:"clusters"` +} + +type TFClusterIPsModel struct { + ClusterName types.String `tfsdk:"cluster_name"` + Inbound types.List `tfsdk:"inbound"` + Outbound types.List `tfsdk:"outbound"` +} + var TfTeamObjectType = types.ObjectType{AttrTypes: map[string]attr.Type{ "team_id": types.StringType, "role_names": types.SetType{ElemType: types.StringType}, @@ -187,6 +202,34 @@ func (r *projectRS) Schema(ctx context.Context, req resource.SchemaRequest, resp "region_usage_restrictions": schema.StringAttribute{ Optional: true, }, + "ip_addresses": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "services": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "clusters": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "cluster_name": schema.StringAttribute{ + Computed: true, + }, + "inbound": schema.ListAttribute{ + ElementType: types.StringType, + Computed: true, + }, + "outbound": schema.ListAttribute{ + ElementType: types.StringType, + Computed: true, + }, + }, + }, + Computed: true, + }, + }, + Computed: true, + }, + }, + Computed: true, + }, }, Blocks: map[string]schema.Block{ "teams": schema.SetNestedBlock{ @@ -354,15 +397,14 @@ func (r *projectRS) Create(ctx context.Context, req resource.CreateRequest, resp } // get project props - // TODO: implement resource - atlasTeams, atlasLimits, atlasProjectSettings, _, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) + atlasTeams, atlasLimits, atlasProjectSettings, ipAddresses, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) if err != nil { resp.Diagnostics.AddError("error when getting project properties after create", fmt.Sprintf(ErrorProjectRead, projectID, err.Error())) return } atlasLimits = FilterUserDefinedLimits(atlasLimits, limits) - projectPlanNew := NewTFProjectResourceModel(ctx, projectRes, atlasTeams, atlasProjectSettings, atlasLimits) + projectPlanNew := NewTFProjectResourceModel(ctx, projectRes, atlasTeams, atlasProjectSettings, atlasLimits, ipAddresses) updatePlanFromConfig(projectPlanNew, &projectPlan) // set state to fully populated data @@ -401,14 +443,14 @@ func (r *projectRS) Read(ctx context.Context, req resource.ReadRequest, resp *re } // get project props - atlasTeams, atlasLimits, atlasProjectSettings, _, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) + atlasTeams, atlasLimits, atlasProjectSettings, ipAddresses, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) if err != nil { resp.Diagnostics.AddError("error when getting project properties after create", fmt.Sprintf(ErrorProjectRead, projectID, err.Error())) return } atlasLimits = FilterUserDefinedLimits(atlasLimits, limits) - projectStateNew := NewTFProjectResourceModel(ctx, projectRes, atlasTeams, atlasProjectSettings, atlasLimits) + projectStateNew := NewTFProjectResourceModel(ctx, projectRes, atlasTeams, atlasProjectSettings, atlasLimits, ipAddresses) updatePlanFromConfig(projectStateNew, &projectState) // save read data into Terraform state @@ -470,7 +512,7 @@ func (r *projectRS) Update(ctx context.Context, req resource.UpdateRequest, resp } // get project props - atlasTeams, atlasLimits, atlasProjectSettings, _, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) + atlasTeams, atlasLimits, atlasProjectSettings, ipAddresses, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) if err != nil { resp.Diagnostics.AddError("error when getting project properties after create", fmt.Sprintf(ErrorProjectRead, projectID, err.Error())) return @@ -478,7 +520,7 @@ func (r *projectRS) Update(ctx context.Context, req resource.UpdateRequest, resp var planLimits []TfLimitModel _ = projectPlan.Limits.ElementsAs(ctx, &planLimits, false) atlasLimits = FilterUserDefinedLimits(atlasLimits, planLimits) - projectPlanNew := NewTFProjectResourceModel(ctx, projectRes, atlasTeams, atlasProjectSettings, atlasLimits) + projectPlanNew := NewTFProjectResourceModel(ctx, projectRes, atlasTeams, atlasProjectSettings, atlasLimits, ipAddresses) updatePlanFromConfig(projectPlanNew, &projectPlan) // save updated data into Terraform state From 9a3b82b7fce2b341873d724272176e2645172f71 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Wed, 17 Jan 2024 12:47:37 +0100 Subject: [PATCH 05/15] make use of object type --- .../service/project/data_source_project.go | 32 ++++++------- internal/service/project/model_project.go | 11 +++-- .../service/project/model_project_test.go | 31 ++++++------ internal/service/project/resource_project.go | 48 ++++++++++++------- 4 files changed, 67 insertions(+), 55 deletions(-) diff --git a/internal/service/project/data_source_project.go b/internal/service/project/data_source_project.go index 5d31b04bc0..c3ce921112 100644 --- a/internal/service/project/data_source_project.go +++ b/internal/service/project/data_source_project.go @@ -31,22 +31,22 @@ type projectDS struct { } type TfProjectDSModel struct { - IPAddresses *TFIPAddressesModel `tfsdk:"ip_addresses"` - Created types.String `tfsdk:"created"` - OrgID types.String `tfsdk:"org_id"` - RegionUsageRestrictions types.String `tfsdk:"region_usage_restrictions"` - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - ProjectID types.String `tfsdk:"project_id"` - Teams []*TfTeamDSModel `tfsdk:"teams"` - Limits []*TfLimitModel `tfsdk:"limits"` - ClusterCount types.Int64 `tfsdk:"cluster_count"` - IsCollectDatabaseSpecificsStatisticsEnabled types.Bool `tfsdk:"is_collect_database_specifics_statistics_enabled"` - IsRealtimePerformancePanelEnabled types.Bool `tfsdk:"is_realtime_performance_panel_enabled"` - IsSchemaAdvisorEnabled types.Bool `tfsdk:"is_schema_advisor_enabled"` - IsPerformanceAdvisorEnabled types.Bool `tfsdk:"is_performance_advisor_enabled"` - IsExtendedStorageSizesEnabled types.Bool `tfsdk:"is_extended_storage_sizes_enabled"` - IsDataExplorerEnabled types.Bool `tfsdk:"is_data_explorer_enabled"` + IPAddresses types.Object `tfsdk:"ip_addresses"` + Created types.String `tfsdk:"created"` + OrgID types.String `tfsdk:"org_id"` + RegionUsageRestrictions types.String `tfsdk:"region_usage_restrictions"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + ProjectID types.String `tfsdk:"project_id"` + Teams []*TfTeamDSModel `tfsdk:"teams"` + Limits []*TfLimitModel `tfsdk:"limits"` + ClusterCount types.Int64 `tfsdk:"cluster_count"` + IsCollectDatabaseSpecificsStatisticsEnabled types.Bool `tfsdk:"is_collect_database_specifics_statistics_enabled"` + IsRealtimePerformancePanelEnabled types.Bool `tfsdk:"is_realtime_performance_panel_enabled"` + IsSchemaAdvisorEnabled types.Bool `tfsdk:"is_schema_advisor_enabled"` + IsPerformanceAdvisorEnabled types.Bool `tfsdk:"is_performance_advisor_enabled"` + IsExtendedStorageSizesEnabled types.Bool `tfsdk:"is_extended_storage_sizes_enabled"` + IsDataExplorerEnabled types.Bool `tfsdk:"is_data_explorer_enabled"` } type TfTeamDSModel struct { diff --git a/internal/service/project/model_project.go b/internal/service/project/model_project.go index 5c28c3b221..415df36f17 100644 --- a/internal/service/project/model_project.go +++ b/internal/service/project/model_project.go @@ -26,7 +26,7 @@ func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, IsSchemaAdvisorEnabled: types.BoolValue(*projectSettings.IsSchemaAdvisorEnabled), Teams: NewTFTeamsDataSourceModel(ctx, teams), Limits: NewTFLimitsDataSourceModel(ctx, limits), - IPAddresses: &ipAddressesModel, + IPAddresses: ipAddressesModel, } } @@ -62,7 +62,7 @@ func NewTFLimitsDataSourceModel(ctx context.Context, dataFederationLimits []admi return limits } -func NewTFIPAddressesModel(ctx context.Context, ipAddresses *admin.GroupIPAddresses) TFIPAddressesModel { +func NewTFIPAddressesModel(ctx context.Context, ipAddresses *admin.GroupIPAddresses) types.Object { clusterIPs := []TFClusterIPsModel{} if ipAddresses != nil && ipAddresses.Services != nil { clusterIPAddresses := ipAddresses.Services.GetClusters() @@ -77,11 +77,12 @@ func NewTFIPAddressesModel(ctx context.Context, ipAddresses *admin.GroupIPAddres } } } - return TFIPAddressesModel{ + obj, _ := types.ObjectValueFrom(ctx, IPAddressesObjectType.AttrTypes, TFIPAddressesModel{ // TODO handle errors. Services: TFServicesModel{ Clusters: clusterIPs, }, - } + }) + return obj } func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, @@ -96,7 +97,7 @@ func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, WithDefaultAlertsSettings: types.BoolPointerValue(projectRes.WithDefaultAlertsSettings), Teams: newTFTeamsResourceModel(ctx, teams), Limits: newTFLimitsResourceModel(ctx, limits), - IPAddresses: &ipAddressesModel, + IPAddresses: ipAddressesModel, } if projectSettings != nil { diff --git a/internal/service/project/model_project_test.go b/internal/service/project/model_project_test.go index 8c8297cbc6..fc868c123c 100644 --- a/internal/service/project/model_project_test.go +++ b/internal/service/project/model_project_test.go @@ -69,7 +69,7 @@ var ( limitsTFSet, _ = types.SetValueFrom(context.Background(), project.TfLimitObjectType, []project.TfLimitModel{ *limitsTF[0], }) - ipAddressesTF = project.TFIPAddressesModel{ + ipAddressesTF, _ = types.ObjectValueFrom(context.Background(), project.IPAddressesObjectType.AttrTypes, project.TFIPAddressesModel{ Services: project.TFServicesModel{ Clusters: []project.TFClusterIPsModel{ { @@ -79,7 +79,12 @@ var ( }, }, }, - } + }) + emptyIPAddressesTF, _ = types.ObjectValueFrom(context.Background(), project.IPAddressesObjectType.AttrTypes, project.TFIPAddressesModel{ + Services: project.TFServicesModel{ + Clusters: []project.TFClusterIPsModel{}, + }, + }) projectSDK = admin.Group{ Id: admin.PtrString(projectID), Name: projectName, @@ -198,7 +203,7 @@ func TestProjectDataSourceSDKToDataSourceTFModel(t *testing.T) { IsSchemaAdvisorEnabled: types.BoolValue(true), Teams: teamsDSTF, Limits: limitsTF, - IPAddresses: &ipAddressesTF, + IPAddresses: ipAddressesTF, Created: types.StringValue("0001-01-01T00:00:00Z"), }, }, @@ -248,7 +253,7 @@ func TestProjectDataSourceSDKToResourceTFModel(t *testing.T) { IsSchemaAdvisorEnabled: types.BoolValue(true), Teams: teamsTFSet, Limits: limitsTFSet, - IPAddresses: &ipAddressesTF, + IPAddresses: ipAddressesTF, Created: types.StringValue("0001-01-01T00:00:00Z"), }, }, @@ -366,16 +371,12 @@ func TestIPAddressesModelToTF(t *testing.T) { testCases := []struct { name string sdkModel *admin.GroupIPAddresses - expectedResult project.TFIPAddressesModel + expectedResult types.Object }{ { - name: "No response", - sdkModel: nil, - expectedResult: project.TFIPAddressesModel{ - Services: project.TFServicesModel{ - Clusters: []project.TFClusterIPsModel{}, - }, - }, + name: "No response", + sdkModel: nil, + expectedResult: emptyIPAddressesTF, }, { name: "Empty response when no clusters are created", @@ -385,11 +386,7 @@ func TestIPAddressesModelToTF(t *testing.T) { Clusters: &[]admin.ClusterIPAddresses{}, }, }, - expectedResult: project.TFIPAddressesModel{ - Services: project.TFServicesModel{ - Clusters: []project.TFClusterIPsModel{}, - }, - }, + expectedResult: emptyIPAddressesTF, }, { name: "Full response", diff --git a/internal/service/project/resource_project.go b/internal/service/project/resource_project.go index 1534ffbdb3..548dea8c86 100644 --- a/internal/service/project/resource_project.go +++ b/internal/service/project/resource_project.go @@ -54,23 +54,23 @@ type projectRS struct { } type TfProjectRSModel struct { - Limits types.Set `tfsdk:"limits"` - Teams types.Set `tfsdk:"teams"` - IPAddresses *TFIPAddressesModel `tfsdk:"ip_addresses"` - RegionUsageRestrictions types.String `tfsdk:"region_usage_restrictions"` - Name types.String `tfsdk:"name"` - OrgID types.String `tfsdk:"org_id"` - Created types.String `tfsdk:"created"` - ProjectOwnerID types.String `tfsdk:"project_owner_id"` - ID types.String `tfsdk:"id"` - ClusterCount types.Int64 `tfsdk:"cluster_count"` - IsDataExplorerEnabled types.Bool `tfsdk:"is_data_explorer_enabled"` - IsPerformanceAdvisorEnabled types.Bool `tfsdk:"is_performance_advisor_enabled"` - IsRealtimePerformancePanelEnabled types.Bool `tfsdk:"is_realtime_performance_panel_enabled"` - IsSchemaAdvisorEnabled types.Bool `tfsdk:"is_schema_advisor_enabled"` - IsExtendedStorageSizesEnabled types.Bool `tfsdk:"is_extended_storage_sizes_enabled"` - IsCollectDatabaseSpecificsStatisticsEnabled types.Bool `tfsdk:"is_collect_database_specifics_statistics_enabled"` - WithDefaultAlertsSettings types.Bool `tfsdk:"with_default_alerts_settings"` + Limits types.Set `tfsdk:"limits"` + Teams types.Set `tfsdk:"teams"` + IPAddresses types.Object `tfsdk:"ip_addresses"` + RegionUsageRestrictions types.String `tfsdk:"region_usage_restrictions"` + Name types.String `tfsdk:"name"` + OrgID types.String `tfsdk:"org_id"` + Created types.String `tfsdk:"created"` + ProjectOwnerID types.String `tfsdk:"project_owner_id"` + ID types.String `tfsdk:"id"` + ClusterCount types.Int64 `tfsdk:"cluster_count"` + IsDataExplorerEnabled types.Bool `tfsdk:"is_data_explorer_enabled"` + IsPerformanceAdvisorEnabled types.Bool `tfsdk:"is_performance_advisor_enabled"` + IsRealtimePerformancePanelEnabled types.Bool `tfsdk:"is_realtime_performance_panel_enabled"` + IsSchemaAdvisorEnabled types.Bool `tfsdk:"is_schema_advisor_enabled"` + IsExtendedStorageSizesEnabled types.Bool `tfsdk:"is_extended_storage_sizes_enabled"` + IsCollectDatabaseSpecificsStatisticsEnabled types.Bool `tfsdk:"is_collect_database_specifics_statistics_enabled"` + WithDefaultAlertsSettings types.Bool `tfsdk:"with_default_alerts_settings"` } type TfTeamModel struct { @@ -100,6 +100,20 @@ type TFClusterIPsModel struct { Outbound types.List `tfsdk:"outbound"` } +var IPAddressesObjectType = types.ObjectType{AttrTypes: map[string]attr.Type{ + "services": ServicesObjectType, +}} + +var ServicesObjectType = types.ObjectType{AttrTypes: map[string]attr.Type{ + "clusters": types.ListType{ElemType: ClusterIPsObjectType}, +}} + +var ClusterIPsObjectType = types.ObjectType{AttrTypes: map[string]attr.Type{ + "cluster_name": types.StringType, + "inbound": types.ListType{ElemType: types.StringType}, + "outbound": types.ListType{ElemType: types.StringType}, +}} + var TfTeamObjectType = types.ObjectType{AttrTypes: map[string]attr.Type{ "team_id": types.StringType, "role_names": types.SetType{ElemType: types.StringType}, From fba3e231f5738db76e810a23766dbfe76eb01985 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Wed, 17 Jan 2024 17:44:59 +0100 Subject: [PATCH 06/15] adjust docs including new attribute --- website/docs/d/project.html.markdown | 32 +++++++++++++++++++------ website/docs/d/projects.html.markdown | 34 ++++++++++++++++++++------- website/docs/r/project.html.markdown | 12 ++++++++-- 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/website/docs/d/project.html.markdown b/website/docs/d/project.html.markdown index 7ffba1b4b0..61aba80300 100644 --- a/website/docs/d/project.html.markdown +++ b/website/docs/d/project.html.markdown @@ -86,13 +86,9 @@ In addition to all arguments above, the following attributes are exported: * `org_id` - The ID of the organization you want to create the project within. * `cluster_count` - The number of Atlas clusters deployed in the project. * `created` - The ISO-8601-formatted timestamp of when Atlas created the project. -* `teams.#.team_id` - The unique identifier of the team you want to associate with the project. The team and project must share the same parent organization. -* `teams.#.role_names` - Each string in the array represents a project role assigned to the team. Every user associated with the team inherits these roles. The [MongoDB Documentation](https://www.mongodb.com/docs/atlas/reference/user-roles/#organization-roles) describes the roles a user can have. -* `limits.#.name` - Human-readable label that identifies this project limit. -* `limits.#.value` - Amount the limit is set to. -* `limits.#.current_usage` - Amount that indicates the current usage of the limit. -* `limits.#.default_limit` - Default value of the limit. -* `limits.#.maximum_limit` - Maximum value of the limit. +* `teams` - Returns all teams to which the authenticated user has access in the project. See [Teams](#teams). +* `limits` - The limits for the specified project. See [Limits](#limits). +* `ip_addresses` - IP addresses in a project categorized by services. See [IP Addresses](#ip-addresses). * `is_collect_database_specifics_statistics_enabled` - Flag that indicates whether to enable statistics in [cluster metrics](https://www.mongodb.com/docs/atlas/monitor-cluster-metrics/) collection for the project. * `is_data_explorer_enabled` - Flag that indicates whether to enable Data Explorer for the project. If enabled, you can query your database with an easy to use interface. @@ -102,5 +98,27 @@ In addition to all arguments above, the following attributes are exported: * `is_schema_advisor_enabled` - Flag that indicates whether to enable Schema Advisor for the project. If enabled, you receive customized recommendations to optimize your data model and enhance performance. Disable this setting to disable schema suggestions in the [Performance Advisor](https://www.mongodb.com/docs/atlas/performance-advisor/#std-label-performance-advisor) and the [Data Explorer](https://www.mongodb.com/docs/atlas/atlas-ui/#std-label-atlas-ui). * `region_usage_restrictions` - If GOV_REGIONS_ONLY the project can be used for government regions only, otherwise defaults to standard regions. For more information see [MongoDB Atlas for Government](https://www.mongodb.com/docs/atlas/government/api/#creating-a-project). + +### Teams + +* `team_id` - The unique identifier of the team you want to associate with the project. The team and project must share the same parent organization. +* `role_names` - Each string in the array represents a project role assigned to the team. Every user associated with the team inherits these roles. The [MongoDB Documentation](https://www.mongodb.com/docs/atlas/reference/user-roles/#organization-roles) describes the roles a user can have. + +### Limits + +* `name` - Human-readable label that identifies this project limit. +* `value` - Amount the limit is set to. +* `current_usage` - Amount that indicates the current usage of the limit. +* `default_limit` - Default value of the limit. +* `maximum_limit` - Maximum value of the limit. + + +### IP Addresses + +* `services.clusters.#.cluster_name` - Human-readable label that identifies the cluster. +* `services.clusters.#.inbound` - List of inbound IP addresses associated with the cluster. If your network allows outbound HTTP requests only to specific IP addresses, you must allow access to the following IP addresses so that your application can connect to your Atlas cluster. +* `services.clusters.#.outbound` - List of outbound IP addresses associated with the cluster. If your network allows inbound HTTP requests only from specific IP addresses, you must allow access from the following IP addresses so that your Atlas cluster can communicate with your webhooks and KMS. + + See [MongoDB Atlas API - Project](https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#tag/Projects) - [and MongoDB Atlas API - Teams](https://docs.atlas.mongodb.com/reference/api/project-get-teams/) Documentation for more information. diff --git a/website/docs/d/projects.html.markdown b/website/docs/d/projects.html.markdown index 48545daee6..2002f9ccd7 100644 --- a/website/docs/d/projects.html.markdown +++ b/website/docs/d/projects.html.markdown @@ -61,13 +61,9 @@ data "mongodbatlas_projects" "test" { * `org_id` - The ID of the organization you want to create the project within. * `cluster_count` - The number of Atlas clusters deployed in the project. * `created` - The ISO-8601-formatted timestamp of when Atlas created the project. -* `teams.#.team_id` - The unique identifier of the team you want to associate with the project. The team and project must share the same parent organization. -* `teams.#.role_names` - Each string in the array represents a project role assigned to the team. Every user associated with the team inherits these roles. The [MongoDB Documentation](https://www.mongodb.com/docs/atlas/reference/user-roles/#organization-roles) describes the roles a user can have. -* `limits.#.name` - Human-readable label that identifies this project limit. -* `limits.#.value` - Amount the limit is set to. -* `limits.#.current_usage` - Amount that indicates the current usage of the limit. -* `limits.#.default_limit` - Default value of the limit. -* `limits.#.maximum_limit` - Maximum value of the limit. +* `teams` - Returns all teams to which the authenticated user has access in the project. See [Teams](#teams). +* `limits` - The limits for the specified project. See [Limits](#limits). +* `ip_addresses` - IP addresses in a project categorized by services. See [IP Addresses](#ip-addresses). * `is_collect_database_specifics_statistics_enabled` - Flag that indicates whether to enable statistics in [cluster metrics](https://www.mongodb.com/docs/atlas/monitor-cluster-metrics/) collection for the project. * `is_data_explorer_enabled` - Flag that indicates whether to enable Data Explorer for the project. If enabled, you can query your database with an easy to use interface. @@ -76,5 +72,27 @@ data "mongodbatlas_projects" "test" { * `is_realtime_performance_panel_enabled` - Flag that indicates whether to enable Real Time Performance Panel for the project. If enabled, you can see real time metrics from your MongoDB database. * `is_schema_advisor_enabled` - Flag that indicates whether to enable Schema Advisor for the project. If enabled, you receive customized recommendations to optimize your data model and enhance performance. Disable this setting to disable schema suggestions in the [Performance Advisor](https://www.mongodb.com/docs/atlas/performance-advisor/#std-label-performance-advisor) and the [Data Explorer](https://www.mongodb.com/docs/atlas/atlas-ui/#std-label-atlas-ui). * `region_usage_restrictions` - If GOV_REGIONS_ONLY the project can be used for government regions only, otherwise defaults to standard regions. For more information see [MongoDB Atlas for Government](https://www.mongodb.com/docs/atlas/government/api/#creating-a-project). - + + +### Teams + +* `team_id` - The unique identifier of the team you want to associate with the project. The team and project must share the same parent organization. +* `role_names` - Each string in the array represents a project role assigned to the team. Every user associated with the team inherits these roles. The [MongoDB Documentation](https://www.mongodb.com/docs/atlas/reference/user-roles/#organization-roles) describes the roles a user can have. + +### Limits + +* `name` - Human-readable label that identifies this project limit. +* `value` - Amount the limit is set to. +* `current_usage` - Amount that indicates the current usage of the limit. +* `default_limit` - Default value of the limit. +* `maximum_limit` - Maximum value of the limit. + + +### IP Addresses + +* `services.clusters.#.cluster_name` - Human-readable label that identifies the cluster. +* `services.clusters.#.inbound` - List of inbound IP addresses associated with the cluster. If your network allows outbound HTTP requests only to specific IP addresses, you must allow access to the following IP addresses so that your application can connect to your Atlas cluster. +* `services.clusters.#.outbound` - List of outbound IP addresses associated with the cluster. If your network allows inbound HTTP requests only from specific IP addresses, you must allow access from the following IP addresses so that your Atlas cluster can communicate with your webhooks and KMS. + + See [MongoDB Atlas API - Projects](https://www.mongodb.com/docs/atlas/reference/api-resources-spec/#tag/Projects) - [and MongoDB Atlas API - Teams](https://docs.atlas.mongodb.com/reference/api/project-get-teams/) Documentation for more information. diff --git a/website/docs/r/project.html.markdown b/website/docs/r/project.html.markdown index e46ec46f4b..b540fb2dde 100644 --- a/website/docs/r/project.html.markdown +++ b/website/docs/r/project.html.markdown @@ -91,8 +91,16 @@ Teams attribute is optional In addition to all arguments above, the following attributes are exported: * `id` - The project id. -* `created` - The ISO-8601-formatted timestamp of when Atlas created the project.. -* `cluster_count` - The number of Atlas clusters deployed in the project.. +* `created` - The ISO-8601-formatted timestamp of when Atlas created the project. +* `cluster_count` - The number of Atlas clusters deployed in the project. +* `ip_addresses` - IP addresses in a project categorized by services. See [IP Addresses](#ip-addresses). + + +### IP Addresses + +* `services.clusters.#.cluster_name` - Human-readable label that identifies the cluster. +* `services.clusters.#.inbound` - List of inbound IP addresses associated with the cluster. If your network allows outbound HTTP requests only to specific IP addresses, you must allow access to the following IP addresses so that your application can connect to your Atlas cluster. +* `services.clusters.#.outbound` - List of outbound IP addresses associated with the cluster. If your network allows inbound HTTP requests only from specific IP addresses, you must allow access from the following IP addresses so that your Atlas cluster can communicate with your webhooks and KMS. ## Import From c6329c1f29fdd560433a0030fc38c7347bd87816 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Wed, 17 Jan 2024 18:15:36 +0100 Subject: [PATCH 07/15] adjust acceptance test and unit test fix --- .../project/data_source_project_test.go | 57 ++++++++++--------- .../service/project/resource_project_test.go | 3 +- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/internal/service/project/data_source_project_test.go b/internal/service/project/data_source_project_test.go index a51a1e4a6e..b75e0996af 100644 --- a/internal/service/project/data_source_project_test.go +++ b/internal/service/project/data_source_project_test.go @@ -15,8 +15,9 @@ import ( func TestAccProjectDSProject_byID(t *testing.T) { var ( - projectName = acctest.RandomWithPrefix("test-acc") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + projectName = acctest.RandomWithPrefix("test-acc") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + dataSourceName = "data.mongodbatlas_project.test" ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acc.PreCheckBasic(t); acc.PreCheckProjectTeamsIdsWithMinCount(t, 2) }, @@ -36,9 +37,10 @@ func TestAccProjectDSProject_byID(t *testing.T) { }, )), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "name"), - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "org_id"), - resource.TestCheckResourceAttr("mongodbatlas_project.test", "teams.#", "2"), + resource.TestCheckResourceAttrSet(dataSourceName, "name"), + resource.TestCheckResourceAttrSet(dataSourceName, "org_id"), + resource.TestCheckResourceAttr(dataSourceName, "teams.#", "2"), + resource.TestCheckResourceAttrSet(dataSourceName, "ip_addresses.services.clusters.#"), ), }, }, @@ -47,8 +49,9 @@ func TestAccProjectDSProject_byID(t *testing.T) { func TestAccProjectDSProject_byName(t *testing.T) { var ( - projectName = acctest.RandomWithPrefix("test-acc") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + projectName = acctest.RandomWithPrefix("test-acc") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + dataSourceName = "data.mongodbatlas_project.test" ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acc.PreCheckBasic(t); acc.PreCheckProjectTeamsIdsWithMinCount(t, 2) }, @@ -69,9 +72,9 @@ func TestAccProjectDSProject_byName(t *testing.T) { }, )), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "name"), - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "org_id"), - resource.TestCheckResourceAttr("mongodbatlas_project.test", "teams.#", "2"), + resource.TestCheckResourceAttrSet(dataSourceName, "name"), + resource.TestCheckResourceAttrSet(dataSourceName, "org_id"), + resource.TestCheckResourceAttr(dataSourceName, "teams.#", "2"), ), }, }, @@ -80,8 +83,9 @@ func TestAccProjectDSProject_byName(t *testing.T) { func TestAccProjectDSProject_defaultFlags(t *testing.T) { var ( - projectName = acctest.RandomWithPrefix("test-acc") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + projectName = acctest.RandomWithPrefix("test-acc") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + dataSourceName = "data.mongodbatlas_project.test" ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acc.PreCheckBasic(t); acc.PreCheckProjectTeamsIdsWithMinCount(t, 2) }, @@ -102,15 +106,15 @@ func TestAccProjectDSProject_defaultFlags(t *testing.T) { }, )), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "name"), - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "org_id"), - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "is_collect_database_specifics_statistics_enabled"), - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "is_data_explorer_enabled"), - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "is_extended_storage_sizes_enabled"), - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "is_performance_advisor_enabled"), - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "is_realtime_performance_panel_enabled"), - resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "is_schema_advisor_enabled"), - resource.TestCheckResourceAttr("mongodbatlas_project.test", "teams.#", "2"), + resource.TestCheckResourceAttrSet(dataSourceName, "name"), + resource.TestCheckResourceAttrSet(dataSourceName, "org_id"), + resource.TestCheckResourceAttrSet(dataSourceName, "is_collect_database_specifics_statistics_enabled"), + resource.TestCheckResourceAttrSet(dataSourceName, "is_data_explorer_enabled"), + resource.TestCheckResourceAttrSet(dataSourceName, "is_extended_storage_sizes_enabled"), + resource.TestCheckResourceAttrSet(dataSourceName, "is_performance_advisor_enabled"), + resource.TestCheckResourceAttrSet(dataSourceName, "is_realtime_performance_panel_enabled"), + resource.TestCheckResourceAttrSet(dataSourceName, "is_schema_advisor_enabled"), + resource.TestCheckResourceAttr(dataSourceName, "teams.#", "2"), ), }, }, @@ -119,8 +123,9 @@ func TestAccProjectDSProject_defaultFlags(t *testing.T) { func TestAccProjectDSProject_limits(t *testing.T) { var ( - projectName = acctest.RandomWithPrefix("test-acc") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + projectName = acctest.RandomWithPrefix("test-acc") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + dataSourceName = "data.mongodbatlas_project.test" ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acc.PreCheckBasic(t) }, @@ -129,9 +134,9 @@ func TestAccProjectDSProject_limits(t *testing.T) { { Config: testAccMongoDBAtlasProjectDSByNameUsingRS(acc.ConfigProjectWithLimits(projectName, orgID, []*admin.DataFederationLimit{})), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("data.mongodbatlas_project.test", "name"), - resource.TestCheckResourceAttrSet("data.mongodbatlas_project.test", "org_id"), - resource.TestCheckResourceAttrSet("data.mongodbatlas_project.test", "limits.0.name"), + resource.TestCheckResourceAttrSet(dataSourceName, "name"), + resource.TestCheckResourceAttrSet(dataSourceName, "org_id"), + resource.TestCheckResourceAttrSet(dataSourceName, "limits.0.name"), ), }, }, diff --git a/internal/service/project/resource_project_test.go b/internal/service/project/resource_project_test.go index 0ed2d3b54c..c63a323808 100644 --- a/internal/service/project/resource_project_test.go +++ b/internal/service/project/resource_project_test.go @@ -111,7 +111,7 @@ func TestGetProjectPropsFromAPI(t *testing.T) { svc.On("ListProjectTeams", mock.Anything, mock.Anything).Return(tc.teamRoleReponse.TeamRole, tc.teamRoleReponse.HTTPResponse, tc.teamRoleReponse.Err) svc.On("ListProjectLimits", mock.Anything, mock.Anything).Return(tc.limitResponse.Limits, tc.limitResponse.HTTPResponse, tc.limitResponse.Err).Maybe() svc.On("GetProjectSettings", mock.Anything, mock.Anything).Return(tc.groupResponse.GroupSettings, tc.groupResponse.HTTPResponse, tc.groupResponse.Err).Maybe() - svc.On("ReturnAllIPAddresses", mock.Anything, mock.Anything).Return(tc.groupResponse.GroupSettings, tc.groupResponse.HTTPResponse, tc.groupResponse.Err).Maybe() + svc.On("ReturnAllIPAddresses", mock.Anything, mock.Anything).Return(tc.ipAddressesResponse.IPAddresses, tc.ipAddressesResponse.HTTPResponse, tc.ipAddressesResponse.Err).Maybe() _, _, _, _, err := project.GetProjectPropsFromAPI(context.Background(), svc, dummyProjectID) @@ -506,6 +506,7 @@ func TestAccProjectRSProject_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "org_id", orgID), resource.TestCheckResourceAttr(resourceName, "cluster_count", clusterCount), resource.TestCheckResourceAttr(resourceName, "teams.#", "2"), + resource.TestCheckResourceAttrSet(resourceName, "ip_addresses.services.clusters.#"), ), }, { From 3e2c1efaf5b24b4d5f5ec155830c2ef4a5a8f89b Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Thu, 18 Jan 2024 09:35:44 +0100 Subject: [PATCH 08/15] refactor: define struct for containing project related properties --- .../service/project/data_source_project.go | 4 +- .../service/project/data_source_projects.go | 4 +- internal/service/project/model_project.go | 20 +++---- .../service/project/model_project_test.go | 54 +++++++++---------- internal/service/project/resource_project.go | 51 ++++++++++++------ .../service/project/resource_project_test.go | 2 +- 6 files changed, 76 insertions(+), 59 deletions(-) diff --git a/internal/service/project/data_source_project.go b/internal/service/project/data_source_project.go index c3ce921112..3a95cd8fda 100644 --- a/internal/service/project/data_source_project.go +++ b/internal/service/project/data_source_project.go @@ -202,13 +202,13 @@ func (d *projectDS) Read(ctx context.Context, req datasource.ReadRequest, resp * } } - atlasTeams, atlasLimits, atlasProjectSettings, ipAddresses, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), project.GetId()) + projectProps, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), project.GetId()) if err != nil { resp.Diagnostics.AddError("error when getting project properties", fmt.Sprintf(ErrorProjectRead, project.GetId(), err.Error())) return } - projectState = NewTFProjectDataSourceModel(ctx, project, atlasTeams, atlasProjectSettings, atlasLimits, ipAddresses) + projectState = NewTFProjectDataSourceModel(ctx, project, *projectProps) resp.Diagnostics.Append(resp.State.Set(ctx, &projectState)...) if resp.Diagnostics.HasError() { diff --git a/internal/service/project/data_source_projects.go b/internal/service/project/data_source_projects.go index 2f349188b7..35717cad1d 100644 --- a/internal/service/project/data_source_projects.go +++ b/internal/service/project/data_source_projects.go @@ -202,9 +202,9 @@ func populateProjectsDataSourceModel(ctx context.Context, connV2 *admin.APIClien results := make([]*TfProjectDSModel, 0, len(input)) for i := range input { project := input[i] - atlasTeams, atlasLimits, atlasProjectSettings, ipAddresses, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), project.GetId()) + projectProps, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), project.GetId()) if err == nil { // if the project is still valid, e.g. could have just been deleted - projectModel := NewTFProjectDataSourceModel(ctx, &project, atlasTeams, atlasProjectSettings, atlasLimits, ipAddresses) + projectModel := NewTFProjectDataSourceModel(ctx, &project, *projectProps) results = append(results, &projectModel) } } diff --git a/internal/service/project/model_project.go b/internal/service/project/model_project.go index 96c34bb2b8..250128dc4a 100644 --- a/internal/service/project/model_project.go +++ b/internal/service/project/model_project.go @@ -8,9 +8,9 @@ import ( "go.mongodb.org/atlas-sdk/v20231115003/admin" ) -func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, - teams *admin.PaginatedTeamRole, projectSettings *admin.GroupSettings, limits []admin.DataFederationLimit, ipAddresses *admin.GroupIPAddresses) TfProjectDSModel { - ipAddressesModel := NewTFIPAddressesModel(ctx, ipAddresses) +func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, projectProps AdditionalProperties) TfProjectDSModel { + ipAddressesModel := NewTFIPAddressesModel(ctx, projectProps.IPAddresses) + projectSettings := projectProps.Settings return TfProjectDSModel{ ID: types.StringValue(project.GetId()), ProjectID: types.StringValue(project.GetId()), @@ -24,8 +24,8 @@ func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, IsPerformanceAdvisorEnabled: types.BoolValue(*projectSettings.IsPerformanceAdvisorEnabled), IsRealtimePerformancePanelEnabled: types.BoolValue(*projectSettings.IsRealtimePerformancePanelEnabled), IsSchemaAdvisorEnabled: types.BoolValue(*projectSettings.IsSchemaAdvisorEnabled), - Teams: NewTFTeamsDataSourceModel(ctx, teams), - Limits: NewTFLimitsDataSourceModel(ctx, limits), + Teams: NewTFTeamsDataSourceModel(ctx, projectProps.Teams), + Limits: NewTFLimitsDataSourceModel(ctx, projectProps.Limits), IPAddresses: ipAddressesModel, } } @@ -85,9 +85,8 @@ func NewTFIPAddressesModel(ctx context.Context, ipAddresses *admin.GroupIPAddres return obj } -func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, - teams *admin.PaginatedTeamRole, projectSettings *admin.GroupSettings, limits []admin.DataFederationLimit, ipAddresses *admin.GroupIPAddresses) *TfProjectRSModel { - ipAddressesModel := NewTFIPAddressesModel(ctx, ipAddresses) +func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, projectProps AdditionalProperties) *TfProjectRSModel { + ipAddressesModel := NewTFIPAddressesModel(ctx, projectProps.IPAddresses) projectPlan := TfProjectRSModel{ ID: types.StringValue(projectRes.GetId()), Name: types.StringValue(projectRes.Name), @@ -95,11 +94,12 @@ func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, ClusterCount: types.Int64Value(projectRes.ClusterCount), Created: types.StringValue(conversion.TimeToString(projectRes.Created)), WithDefaultAlertsSettings: types.BoolPointerValue(projectRes.WithDefaultAlertsSettings), - Teams: newTFTeamsResourceModel(ctx, teams), - Limits: newTFLimitsResourceModel(ctx, limits), + Teams: newTFTeamsResourceModel(ctx, projectProps.Teams), + Limits: newTFLimitsResourceModel(ctx, projectProps.Limits), IPAddresses: ipAddressesModel, } + projectSettings := projectProps.Settings if projectSettings != nil { projectPlan.IsCollectDatabaseSpecificsStatisticsEnabled = types.BoolValue(*projectSettings.IsCollectDatabaseSpecificsStatisticsEnabled) projectPlan.IsDataExplorerEnabled = types.BoolValue(*projectSettings.IsDataExplorerEnabled) diff --git a/internal/service/project/model_project_test.go b/internal/service/project/model_project_test.go index a34139ee06..55823890d2 100644 --- a/internal/service/project/model_project_test.go +++ b/internal/service/project/model_project_test.go @@ -170,24 +170,23 @@ func TestLimitsDataSourceSDKToTFModel(t *testing.T) { func TestProjectDataSourceSDKToDataSourceTFModel(t *testing.T) { testCases := []struct { - name string - project *admin.Group - teams *admin.PaginatedTeamRole - projectSettings *admin.GroupSettings - ipAddresses *admin.GroupIPAddresses - dataFederationLimits []admin.DataFederationLimit - expectedTFModel project.TfProjectDSModel + name string + project *admin.Group + projectProps project.AdditionalProperties + expectedTFModel project.TfProjectDSModel }{ { name: "Project", project: &projectSDK, - teams: &admin.PaginatedTeamRole{ - Results: &teamRolesSDK, - TotalCount: conversion.IntPtr(1), + projectProps: project.AdditionalProperties{ + Teams: &admin.PaginatedTeamRole{ + Results: &teamRolesSDK, + TotalCount: conversion.IntPtr(1), + }, + Settings: &projectSettingsSDK, + IPAddresses: &projectIPAddressesSDK, + Limits: limitsSDK, }, - projectSettings: &projectSettingsSDK, - ipAddresses: &projectIPAddressesSDK, - dataFederationLimits: limitsSDK, expectedTFModel: project.TfProjectDSModel{ ID: types.StringValue(projectID), @@ -211,7 +210,7 @@ func TestProjectDataSourceSDKToDataSourceTFModel(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - resultModel := project.NewTFProjectDataSourceModel(context.Background(), tc.project, tc.teams, tc.projectSettings, tc.dataFederationLimits, tc.ipAddresses) + resultModel := project.NewTFProjectDataSourceModel(context.Background(), tc.project, tc.projectProps) if !assert.Equal(t, tc.expectedTFModel, resultModel) { t.Errorf("created terraform model did not match expected output") } @@ -221,24 +220,23 @@ func TestProjectDataSourceSDKToDataSourceTFModel(t *testing.T) { func TestProjectDataSourceSDKToResourceTFModel(t *testing.T) { testCases := []struct { - name string - project *admin.Group - teams *admin.PaginatedTeamRole - projectSettings *admin.GroupSettings - ipAddresses *admin.GroupIPAddresses - dataFederationLimits []admin.DataFederationLimit - expectedTFModel project.TfProjectRSModel + name string + project *admin.Group + projectProps project.AdditionalProperties + expectedTFModel project.TfProjectRSModel }{ { name: "Project", project: &projectSDK, - teams: &admin.PaginatedTeamRole{ - Results: conversion.NonEmptyToPtr(teamRolesSDK), - TotalCount: conversion.IntPtr(1), + projectProps: project.AdditionalProperties{ + Teams: &admin.PaginatedTeamRole{ + Results: conversion.NonEmptyToPtr(teamRolesSDK), + TotalCount: conversion.IntPtr(1), + }, + Settings: &projectSettingsSDK, + IPAddresses: &projectIPAddressesSDK, + Limits: limitsSDK, }, - projectSettings: &projectSettingsSDK, - ipAddresses: &projectIPAddressesSDK, - dataFederationLimits: limitsSDK, expectedTFModel: project.TfProjectRSModel{ ID: types.StringValue(projectID), @@ -261,7 +259,7 @@ func TestProjectDataSourceSDKToResourceTFModel(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - resultModel := project.NewTFProjectResourceModel(context.Background(), tc.project, tc.teams, tc.projectSettings, tc.dataFederationLimits, tc.ipAddresses) + resultModel := project.NewTFProjectResourceModel(context.Background(), tc.project, tc.projectProps) if !assert.Equal(t, tc.expectedTFModel, *resultModel) { t.Errorf("created terraform model did not match expected output") } diff --git a/internal/service/project/resource_project.go b/internal/service/project/resource_project.go index 0e5787e54e..deeae6c1ff 100644 --- a/internal/service/project/resource_project.go +++ b/internal/service/project/resource_project.go @@ -400,14 +400,16 @@ func (r *projectRS) Create(ctx context.Context, req resource.CreateRequest, resp } // get project props - atlasTeams, atlasLimits, atlasProjectSettings, ipAddresses, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) + projectProps, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) if err != nil { resp.Diagnostics.AddError("error when getting project properties after create", fmt.Sprintf(ErrorProjectRead, projectID, err.Error())) return } - atlasLimits = FilterUserDefinedLimits(atlasLimits, limits) - projectPlanNew := NewTFProjectResourceModel(ctx, projectRes, atlasTeams, atlasProjectSettings, atlasLimits, ipAddresses) + filteredLimits := FilterUserDefinedLimits(projectProps.Limits, limits) + projectProps.Limits = filteredLimits + + projectPlanNew := NewTFProjectResourceModel(ctx, projectRes, *projectProps) updatePlanFromConfig(projectPlanNew, &projectPlan) // set state to fully populated data @@ -446,14 +448,16 @@ func (r *projectRS) Read(ctx context.Context, req resource.ReadRequest, resp *re } // get project props - atlasTeams, atlasLimits, atlasProjectSettings, ipAddresses, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) + projectProps, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) if err != nil { resp.Diagnostics.AddError("error when getting project properties after create", fmt.Sprintf(ErrorProjectRead, projectID, err.Error())) return } - atlasLimits = FilterUserDefinedLimits(atlasLimits, limits) - projectStateNew := NewTFProjectResourceModel(ctx, projectRes, atlasTeams, atlasProjectSettings, atlasLimits, ipAddresses) + filteredLimits := FilterUserDefinedLimits(projectProps.Limits, limits) + projectProps.Limits = filteredLimits + + projectStateNew := NewTFProjectResourceModel(ctx, projectRes, *projectProps) updatePlanFromConfig(projectStateNew, &projectState) // save read data into Terraform state @@ -515,15 +519,18 @@ func (r *projectRS) Update(ctx context.Context, req resource.UpdateRequest, resp } // get project props - atlasTeams, atlasLimits, atlasProjectSettings, ipAddresses, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) + projectProps, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), projectID) if err != nil { resp.Diagnostics.AddError("error when getting project properties after create", fmt.Sprintf(ErrorProjectRead, projectID, err.Error())) return } var planLimits []TfLimitModel _ = projectPlan.Limits.ElementsAs(ctx, &planLimits, false) - atlasLimits = FilterUserDefinedLimits(atlasLimits, planLimits) - projectPlanNew := NewTFProjectResourceModel(ctx, projectRes, atlasTeams, atlasProjectSettings, atlasLimits, ipAddresses) + + filteredLimits := FilterUserDefinedLimits(projectProps.Limits, planLimits) + projectProps.Limits = filteredLimits + + projectPlanNew := NewTFProjectResourceModel(ctx, projectRes, *projectProps) updatePlanFromConfig(projectPlanNew, &projectPlan) // save updated data into Terraform state @@ -576,29 +583,41 @@ func FilterUserDefinedLimits(allAtlasLimits []admin.DataFederationLimit, tflimit return filteredLimits } -// TODO refactor resp into struct -func GetProjectPropsFromAPI(ctx context.Context, client GroupProjectService, projectID string) (*admin.PaginatedTeamRole, []admin.DataFederationLimit, *admin.GroupSettings, *admin.GroupIPAddresses, error) { +type AdditionalProperties struct { + Teams *admin.PaginatedTeamRole + Settings *admin.GroupSettings + IPAddresses *admin.GroupIPAddresses + Limits []admin.DataFederationLimit +} + +// GetProjectPropsFromAPI fetches properties obtained from complementary endpoints associated with a project. +func GetProjectPropsFromAPI(ctx context.Context, client GroupProjectService, projectID string) (*AdditionalProperties, error) { teams, _, err := client.ListProjectTeams(ctx, projectID) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("error getting project's teams assigned (%s): %v", projectID, err.Error()) + return nil, fmt.Errorf("error getting project's teams assigned (%s): %v", projectID, err.Error()) } limits, _, err := client.ListProjectLimits(ctx, projectID) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("error getting project's limits (%s): %s", projectID, err.Error()) + return nil, fmt.Errorf("error getting project's limits (%s): %s", projectID, err.Error()) } projectSettings, _, err := client.GetProjectSettings(ctx, projectID) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("error getting project's settings assigned (%s): %v", projectID, err.Error()) + return nil, fmt.Errorf("error getting project's settings assigned (%s): %v", projectID, err.Error()) } ipAddresses, _, err := client.ReturnAllIPAddresses(ctx, projectID) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("error getting project's IP addresses (%s): %v", projectID, err.Error()) + return nil, fmt.Errorf("error getting project's IP addresses (%s): %v", projectID, err.Error()) } - return teams, limits, projectSettings, ipAddresses, nil + return &AdditionalProperties{ + Teams: teams, + Limits: limits, + Settings: projectSettings, + IPAddresses: ipAddresses, + }, nil } func updateProjectSettings(ctx context.Context, connV2 *admin.APIClient, state, plan *TfProjectRSModel) error { diff --git a/internal/service/project/resource_project_test.go b/internal/service/project/resource_project_test.go index c63a323808..9bea25247a 100644 --- a/internal/service/project/resource_project_test.go +++ b/internal/service/project/resource_project_test.go @@ -113,7 +113,7 @@ func TestGetProjectPropsFromAPI(t *testing.T) { svc.On("GetProjectSettings", mock.Anything, mock.Anything).Return(tc.groupResponse.GroupSettings, tc.groupResponse.HTTPResponse, tc.groupResponse.Err).Maybe() svc.On("ReturnAllIPAddresses", mock.Anything, mock.Anything).Return(tc.ipAddressesResponse.IPAddresses, tc.ipAddressesResponse.HTTPResponse, tc.ipAddressesResponse.Err).Maybe() - _, _, _, _, err := project.GetProjectPropsFromAPI(context.Background(), svc, dummyProjectID) + _, err := project.GetProjectPropsFromAPI(context.Background(), svc, dummyProjectID) if (err != nil) != tc.expectedError { t.Errorf("Case %s: Received unexpected error: %v", tc.name, err) From 7daa45014577cdd2e444d95978ff37f442b76f74 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Thu, 18 Jan 2024 09:49:18 +0100 Subject: [PATCH 09/15] refactor: use TF* over Tf* for consistency --- .../service/project/data_source_project.go | 10 +-- .../service/project/data_source_projects.go | 4 +- internal/service/project/model_project.go | 40 +++++------ .../service/project/model_project_test.go | 40 +++++------ internal/service/project/resource_project.go | 60 ++++++++-------- .../service/project/resource_project_test.go | 72 +++++++++---------- 6 files changed, 113 insertions(+), 113 deletions(-) diff --git a/internal/service/project/data_source_project.go b/internal/service/project/data_source_project.go index 3a95cd8fda..9ce2d7e722 100644 --- a/internal/service/project/data_source_project.go +++ b/internal/service/project/data_source_project.go @@ -30,7 +30,7 @@ type projectDS struct { config.DSCommon } -type TfProjectDSModel struct { +type TFProjectDSModel struct { IPAddresses types.Object `tfsdk:"ip_addresses"` Created types.String `tfsdk:"created"` OrgID types.String `tfsdk:"org_id"` @@ -38,8 +38,8 @@ type TfProjectDSModel struct { ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` ProjectID types.String `tfsdk:"project_id"` - Teams []*TfTeamDSModel `tfsdk:"teams"` - Limits []*TfLimitModel `tfsdk:"limits"` + Teams []*TFTeamDSModel `tfsdk:"teams"` + Limits []*TFLimitModel `tfsdk:"limits"` ClusterCount types.Int64 `tfsdk:"cluster_count"` IsCollectDatabaseSpecificsStatisticsEnabled types.Bool `tfsdk:"is_collect_database_specifics_statistics_enabled"` IsRealtimePerformancePanelEnabled types.Bool `tfsdk:"is_realtime_performance_panel_enabled"` @@ -49,7 +49,7 @@ type TfProjectDSModel struct { IsDataExplorerEnabled types.Bool `tfsdk:"is_data_explorer_enabled"` } -type TfTeamDSModel struct { +type TFTeamDSModel struct { TeamID types.String `tfsdk:"team_id"` RoleNames types.List `tfsdk:"role_names"` } @@ -171,7 +171,7 @@ func (d *projectDS) Schema(ctx context.Context, req datasource.SchemaRequest, re } func (d *projectDS) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var projectState TfProjectDSModel + var projectState TFProjectDSModel connV2 := d.Client.AtlasV2 resp.Diagnostics.Append(req.Config.Get(ctx, &projectState)...) diff --git a/internal/service/project/data_source_projects.go b/internal/service/project/data_source_projects.go index 35717cad1d..e12c9cbd78 100644 --- a/internal/service/project/data_source_projects.go +++ b/internal/service/project/data_source_projects.go @@ -32,7 +32,7 @@ type ProjectsDS struct { type tfProjectsDSModel struct { ID types.String `tfsdk:"id"` - Results []*TfProjectDSModel `tfsdk:"results"` + Results []*TFProjectDSModel `tfsdk:"results"` PageNum types.Int64 `tfsdk:"page_num"` ItemsPerPage types.Int64 `tfsdk:"items_per_page"` TotalCount types.Int64 `tfsdk:"total_count"` @@ -199,7 +199,7 @@ func (d *ProjectsDS) Read(ctx context.Context, req datasource.ReadRequest, resp func populateProjectsDataSourceModel(ctx context.Context, connV2 *admin.APIClient, stateModel *tfProjectsDSModel, projectsRes *admin.PaginatedAtlasGroup) error { input := projectsRes.GetResults() - results := make([]*TfProjectDSModel, 0, len(input)) + results := make([]*TFProjectDSModel, 0, len(input)) for i := range input { project := input[i] projectProps, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), project.GetId()) diff --git a/internal/service/project/model_project.go b/internal/service/project/model_project.go index 250128dc4a..1df2f6b8c8 100644 --- a/internal/service/project/model_project.go +++ b/internal/service/project/model_project.go @@ -8,10 +8,10 @@ import ( "go.mongodb.org/atlas-sdk/v20231115003/admin" ) -func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, projectProps AdditionalProperties) TfProjectDSModel { +func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, projectProps AdditionalProperties) TFProjectDSModel { ipAddressesModel := NewTFIPAddressesModel(ctx, projectProps.IPAddresses) projectSettings := projectProps.Settings - return TfProjectDSModel{ + return TFProjectDSModel{ ID: types.StringValue(project.GetId()), ProjectID: types.StringValue(project.GetId()), Name: types.StringValue(project.Name), @@ -30,15 +30,15 @@ func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, proj } } -func NewTFTeamsDataSourceModel(ctx context.Context, atlasTeams *admin.PaginatedTeamRole) []*TfTeamDSModel { +func NewTFTeamsDataSourceModel(ctx context.Context, atlasTeams *admin.PaginatedTeamRole) []*TFTeamDSModel { if atlasTeams.GetTotalCount() == 0 { return nil } results := atlasTeams.GetResults() - teams := make([]*TfTeamDSModel, len(results)) + teams := make([]*TFTeamDSModel, len(results)) for i, atlasTeam := range results { roleNames, _ := types.ListValueFrom(ctx, types.StringType, atlasTeam.RoleNames) - teams[i] = &TfTeamDSModel{ + teams[i] = &TFTeamDSModel{ TeamID: types.StringValue(atlasTeam.GetTeamId()), RoleNames: roleNames, } @@ -46,11 +46,11 @@ func NewTFTeamsDataSourceModel(ctx context.Context, atlasTeams *admin.PaginatedT return teams } -func NewTFLimitsDataSourceModel(ctx context.Context, dataFederationLimits []admin.DataFederationLimit) []*TfLimitModel { - limits := make([]*TfLimitModel, len(dataFederationLimits)) +func NewTFLimitsDataSourceModel(ctx context.Context, dataFederationLimits []admin.DataFederationLimit) []*TFLimitModel { + limits := make([]*TFLimitModel, len(dataFederationLimits)) for i, dataFederationLimit := range dataFederationLimits { - limits[i] = &TfLimitModel{ + limits[i] = &TFLimitModel{ Name: types.StringValue(dataFederationLimit.Name), Value: types.Int64Value(dataFederationLimit.Value), CurrentUsage: types.Int64PointerValue(dataFederationLimit.CurrentUsage), @@ -85,9 +85,9 @@ func NewTFIPAddressesModel(ctx context.Context, ipAddresses *admin.GroupIPAddres return obj } -func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, projectProps AdditionalProperties) *TfProjectRSModel { +func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, projectProps AdditionalProperties) *TFProjectRSModel { ipAddressesModel := NewTFIPAddressesModel(ctx, projectProps.IPAddresses) - projectPlan := TfProjectRSModel{ + projectPlan := TFProjectRSModel{ ID: types.StringValue(projectRes.GetId()), Name: types.StringValue(projectRes.Name), OrgID: types.StringValue(projectRes.OrgId), @@ -113,10 +113,10 @@ func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, pro } func newTFLimitsResourceModel(ctx context.Context, dataFederationLimits []admin.DataFederationLimit) types.Set { - limits := make([]TfLimitModel, len(dataFederationLimits)) + limits := make([]TFLimitModel, len(dataFederationLimits)) for i, dataFederationLimit := range dataFederationLimits { - limits[i] = TfLimitModel{ + limits[i] = TFLimitModel{ Name: types.StringValue(dataFederationLimit.Name), Value: types.Int64Value(dataFederationLimit.Value), CurrentUsage: types.Int64PointerValue(dataFederationLimit.CurrentUsage), @@ -131,10 +131,10 @@ func newTFLimitsResourceModel(ctx context.Context, dataFederationLimits []admin. func newTFTeamsResourceModel(ctx context.Context, atlasTeams *admin.PaginatedTeamRole) types.Set { results := atlasTeams.GetResults() - teams := make([]TfTeamModel, len(results)) + teams := make([]TFTeamModel, len(results)) for i, atlasTeam := range results { roleNames, _ := types.SetValueFrom(ctx, types.StringType, atlasTeam.RoleNames) - teams[i] = TfTeamModel{ + teams[i] = TFTeamModel{ TeamID: types.StringValue(atlasTeam.GetTeamId()), RoleNames: roleNames, } @@ -144,7 +144,7 @@ func newTFTeamsResourceModel(ctx context.Context, atlasTeams *admin.PaginatedTea return s } -func NewTeamRoleList(ctx context.Context, teams []TfTeamModel) *[]admin.TeamRole { +func NewTeamRoleList(ctx context.Context, teams []TFTeamModel) *[]admin.TeamRole { res := make([]admin.TeamRole, len(teams)) for i, team := range teams { roleNames := conversion.TypesSetToString(ctx, team.RoleNames) @@ -156,22 +156,22 @@ func NewTeamRoleList(ctx context.Context, teams []TfTeamModel) *[]admin.TeamRole return &res } -func NewGroupName(tfProject *TfProjectRSModel) *admin.GroupName { +func NewGroupName(tfProject *TFProjectRSModel) *admin.GroupName { return &admin.GroupName{ Name: tfProject.Name.ValueStringPointer(), } } -func NewTfTeamModelMap(teams []TfTeamModel) map[types.String]TfTeamModel { - teamsMap := make(map[types.String]TfTeamModel) +func NewTfTeamModelMap(teams []TFTeamModel) map[types.String]TFTeamModel { + teamsMap := make(map[types.String]TFTeamModel) for _, team := range teams { teamsMap[team.TeamID] = team } return teamsMap } -func NewTfLimitModelMap(limits []TfLimitModel) map[types.String]TfLimitModel { - limitsMap := make(map[types.String]TfLimitModel) +func NewTfLimitModelMap(limits []TFLimitModel) map[types.String]TFLimitModel { + limitsMap := make(map[types.String]TFLimitModel) for _, limit := range limits { limitsMap[limit.Name] = limit } diff --git a/internal/service/project/model_project_test.go b/internal/service/project/model_project_test.go index 55823890d2..70e7bdf878 100644 --- a/internal/service/project/model_project_test.go +++ b/internal/service/project/model_project_test.go @@ -36,13 +36,13 @@ var ( RoleNames: &roles, }, } - teamsDSTF = []*project.TfTeamDSModel{ + teamsDSTF = []*project.TFTeamDSModel{ { TeamID: types.StringValue("teamId"), RoleNames: roleList, }, } - teamsTFSet, _ = types.SetValueFrom(context.Background(), project.TfTeamObjectType, []project.TfTeamModel{ + teamsTFSet, _ = types.SetValueFrom(context.Background(), project.TfTeamObjectType, []project.TFTeamModel{ { TeamID: types.StringValue("teamId"), RoleNames: roleSet, @@ -57,7 +57,7 @@ var ( MaximumLimit: admin.PtrInt64(limitMaximumLimit), }, } - limitsTF = []*project.TfLimitModel{ + limitsTF = []*project.TFLimitModel{ { Name: types.StringValue(limitName), Value: types.Int64Value(limitValue), @@ -66,7 +66,7 @@ var ( MaximumLimit: types.Int64Value(limitMaximumLimit), }, } - limitsTFSet, _ = types.SetValueFrom(context.Background(), project.TfLimitObjectType, []project.TfLimitModel{ + limitsTFSet, _ = types.SetValueFrom(context.Background(), project.TfLimitObjectType, []project.TFLimitModel{ *limitsTF[0], }) ipAddressesTF, _ = types.ObjectValueFrom(context.Background(), project.IPAddressesObjectType.AttrTypes, project.TFIPAddressesModel{ @@ -117,7 +117,7 @@ func TestTeamsDataSourceSDKToTFModel(t *testing.T) { testCases := []struct { name string paginatedTeamRole *admin.PaginatedTeamRole - expectedTFModel []*project.TfTeamDSModel + expectedTFModel []*project.TFTeamDSModel }{ { name: "TeamRole", @@ -149,7 +149,7 @@ func TestLimitsDataSourceSDKToTFModel(t *testing.T) { testCases := []struct { name string dataFederationLimits []admin.DataFederationLimit - expectedTFModel []*project.TfLimitModel + expectedTFModel []*project.TFLimitModel }{ { name: "Limit", @@ -173,7 +173,7 @@ func TestProjectDataSourceSDKToDataSourceTFModel(t *testing.T) { name string project *admin.Group projectProps project.AdditionalProperties - expectedTFModel project.TfProjectDSModel + expectedTFModel project.TFProjectDSModel }{ { name: "Project", @@ -187,7 +187,7 @@ func TestProjectDataSourceSDKToDataSourceTFModel(t *testing.T) { IPAddresses: &projectIPAddressesSDK, Limits: limitsSDK, }, - expectedTFModel: project.TfProjectDSModel{ + expectedTFModel: project.TFProjectDSModel{ ID: types.StringValue(projectID), ProjectID: types.StringValue(projectID), @@ -223,7 +223,7 @@ func TestProjectDataSourceSDKToResourceTFModel(t *testing.T) { name string project *admin.Group projectProps project.AdditionalProperties - expectedTFModel project.TfProjectRSModel + expectedTFModel project.TFProjectRSModel }{ { name: "Project", @@ -237,7 +237,7 @@ func TestProjectDataSourceSDKToResourceTFModel(t *testing.T) { IPAddresses: &projectIPAddressesSDK, Limits: limitsSDK, }, - expectedTFModel: project.TfProjectRSModel{ + expectedTFModel: project.TFProjectRSModel{ ID: types.StringValue(projectID), Name: types.StringValue(projectName), @@ -269,7 +269,7 @@ func TestProjectDataSourceSDKToResourceTFModel(t *testing.T) { func TestTeamRoleListTFtoSDK(t *testing.T) { var rolesSet, _ = types.SetValueFrom(context.Background(), types.StringType, roles) - teamsTF := []project.TfTeamModel{ + teamsTF := []project.TFTeamModel{ { TeamID: types.StringValue("teamId"), RoleNames: rolesSet, @@ -278,7 +278,7 @@ func TestTeamRoleListTFtoSDK(t *testing.T) { testCases := []struct { name string expectedResult *[]admin.TeamRole - teamRolesTF []project.TfTeamModel + teamRolesTF []project.TFTeamModel }{ { name: "Team roles", @@ -298,7 +298,7 @@ func TestTeamRoleListTFtoSDK(t *testing.T) { } func TestTeamModelMapTF(t *testing.T) { - teams := []project.TfTeamModel{ + teams := []project.TFTeamModel{ { TeamID: types.StringValue("id1"), }, @@ -308,13 +308,13 @@ func TestTeamModelMapTF(t *testing.T) { } testCases := []struct { name string - expectedResult map[types.String]project.TfTeamModel - teamRolesTF []project.TfTeamModel + expectedResult map[types.String]project.TFTeamModel + teamRolesTF []project.TFTeamModel }{ { name: "Team roles", teamRolesTF: teams, - expectedResult: map[types.String]project.TfTeamModel{ + expectedResult: map[types.String]project.TFTeamModel{ types.StringValue("id1"): teams[0], types.StringValue("id2"): teams[1], }, @@ -332,7 +332,7 @@ func TestTeamModelMapTF(t *testing.T) { } func TestLimitModelMapTF(t *testing.T) { - limits := []project.TfLimitModel{ + limits := []project.TFLimitModel{ { Name: types.StringValue("limit1"), }, @@ -342,13 +342,13 @@ func TestLimitModelMapTF(t *testing.T) { } testCases := []struct { name string - expectedResult map[types.String]project.TfLimitModel - limitsTF []project.TfLimitModel + expectedResult map[types.String]project.TFLimitModel + limitsTF []project.TFLimitModel }{ { name: "Limits", limitsTF: limits, - expectedResult: map[types.String]project.TfLimitModel{ + expectedResult: map[types.String]project.TFLimitModel{ types.StringValue("limit1"): limits[0], types.StringValue("limit2"): limits[1], }, diff --git a/internal/service/project/resource_project.go b/internal/service/project/resource_project.go index deeae6c1ff..958a5a5095 100644 --- a/internal/service/project/resource_project.go +++ b/internal/service/project/resource_project.go @@ -54,7 +54,7 @@ type projectRS struct { config.RSCommon } -type TfProjectRSModel struct { +type TFProjectRSModel struct { Limits types.Set `tfsdk:"limits"` Teams types.Set `tfsdk:"teams"` IPAddresses types.Object `tfsdk:"ip_addresses"` @@ -74,12 +74,12 @@ type TfProjectRSModel struct { WithDefaultAlertsSettings types.Bool `tfsdk:"with_default_alerts_settings"` } -type TfTeamModel struct { +type TFTeamModel struct { TeamID types.String `tfsdk:"team_id"` RoleNames types.Set `tfsdk:"role_names"` } -type TfLimitModel struct { +type TFLimitModel struct { Name types.String `tfsdk:"name"` Value types.Int64 `tfsdk:"value"` CurrentUsage types.Int64 `tfsdk:"current_usage"` @@ -87,7 +87,7 @@ type TfLimitModel struct { MaximumLimit types.Int64 `tfsdk:"maximum_limit"` } -type TFIPAddressesModel struct { // TODO: consistency in Tf vs TF +type TFIPAddressesModel struct { Services TFServicesModel `tfsdk:"services"` } @@ -290,9 +290,9 @@ func (r *projectRS) Schema(ctx context.Context, req resource.SchemaRequest, resp } func (r *projectRS) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var projectPlan TfProjectRSModel - var teams []TfTeamModel - var limits []TfLimitModel + var projectPlan TFProjectRSModel + var teams []TFTeamModel + var limits []TFLimitModel connV2 := r.Client.AtlasV2 @@ -421,8 +421,8 @@ func (r *projectRS) Create(ctx context.Context, req resource.CreateRequest, resp } func (r *projectRS) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var projectState TfProjectRSModel - var limits []TfLimitModel + var projectState TFProjectRSModel + var limits []TFLimitModel connV2 := r.Client.AtlasV2 // get current state @@ -468,8 +468,8 @@ func (r *projectRS) Read(ctx context.Context, req resource.ReadRequest, resp *re } func (r *projectRS) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var projectState TfProjectRSModel - var projectPlan TfProjectRSModel + var projectState TFProjectRSModel + var projectPlan TFProjectRSModel connV2 := r.Client.AtlasV2 // get current state @@ -524,7 +524,7 @@ func (r *projectRS) Update(ctx context.Context, req resource.UpdateRequest, resp resp.Diagnostics.AddError("error when getting project properties after create", fmt.Sprintf(ErrorProjectRead, projectID, err.Error())) return } - var planLimits []TfLimitModel + var planLimits []TFLimitModel _ = projectPlan.Limits.ElementsAs(ctx, &planLimits, false) filteredLimits := FilterUserDefinedLimits(projectProps.Limits, planLimits) @@ -538,7 +538,7 @@ func (r *projectRS) Update(ctx context.Context, req resource.UpdateRequest, resp } func (r *projectRS) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var project *TfProjectRSModel + var project *TFProjectRSModel // read Terraform prior state data into the model resp.Diagnostics.Append(req.State.Get(ctx, &project)...) @@ -559,14 +559,14 @@ func (r *projectRS) ImportState(ctx context.Context, req resource.ImportStateReq resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } -func updatePlanFromConfig(projectPlanNewPtr, projectPlan *TfProjectRSModel) { +func updatePlanFromConfig(projectPlanNewPtr, projectPlan *TFProjectRSModel) { // we need to reset defaults from what was previously in the state: // https://discuss.hashicorp.com/t/boolean-optional-default-value-migration-to-framework/55932 projectPlanNewPtr.WithDefaultAlertsSettings = projectPlan.WithDefaultAlertsSettings projectPlanNewPtr.ProjectOwnerID = projectPlan.ProjectOwnerID } -func FilterUserDefinedLimits(allAtlasLimits []admin.DataFederationLimit, tflimits []TfLimitModel) []admin.DataFederationLimit { +func FilterUserDefinedLimits(allAtlasLimits []admin.DataFederationLimit, tflimits []TFLimitModel) []admin.DataFederationLimit { filteredLimits := []admin.DataFederationLimit{} allLimitsMap := make(map[string]admin.DataFederationLimit) @@ -620,7 +620,7 @@ func GetProjectPropsFromAPI(ctx context.Context, client GroupProjectService, pro }, nil } -func updateProjectSettings(ctx context.Context, connV2 *admin.APIClient, state, plan *TfProjectRSModel) error { +func updateProjectSettings(ctx context.Context, connV2 *admin.APIClient, state, plan *TFProjectRSModel) error { projectID := state.ID.ValueString() settings, _, err := connV2.ProjectsApi.GetProjectSettings(ctx, projectID).Execute() if err != nil { @@ -643,9 +643,9 @@ func updateProjectSettings(ctx context.Context, connV2 *admin.APIClient, state, return nil } -func UpdateProjectLimits(ctx context.Context, client GroupProjectService, projectState, projectPlan *TfProjectRSModel) error { - var planLimits []TfLimitModel - var stateLimits []TfLimitModel +func UpdateProjectLimits(ctx context.Context, client GroupProjectService, projectState, projectPlan *TFProjectRSModel) error { + var planLimits []TFLimitModel + var stateLimits []TFLimitModel _ = projectPlan.Limits.ElementsAs(ctx, &planLimits, false) _ = projectState.Limits.ElementsAs(ctx, &stateLimits, false) @@ -681,7 +681,7 @@ func UpdateProjectLimits(ctx context.Context, client GroupProjectService, projec return nil } -func setProjectLimits(ctx context.Context, client GroupProjectService, projectID string, tfLimits []TfLimitModel) error { +func setProjectLimits(ctx context.Context, client GroupProjectService, projectID string, tfLimits []TFLimitModel) error { for _, limit := range tfLimits { dataFederationLimit := &admin.DataFederationLimit{ Name: limit.Name.ValueString(), @@ -695,9 +695,9 @@ func setProjectLimits(ctx context.Context, client GroupProjectService, projectID return nil } -func UpdateProjectTeams(ctx context.Context, client GroupProjectService, projectState, projectPlan *TfProjectRSModel) error { - var planTeams []TfTeamModel - var stateTeams []TfTeamModel +func UpdateProjectTeams(ctx context.Context, client GroupProjectService, projectState, projectPlan *TFProjectRSModel) error { + var planTeams []TFTeamModel + var stateTeams []TFTeamModel _ = projectPlan.Teams.ElementsAs(ctx, &planTeams, false) _ = projectState.Teams.ElementsAs(ctx, &stateTeams, false) @@ -743,7 +743,7 @@ func UpdateProjectTeams(ctx context.Context, client GroupProjectService, project return nil } -func hasTeamsChanged(planTeams, stateTeams []TfTeamModel) bool { +func hasTeamsChanged(planTeams, stateTeams []TFTeamModel) bool { sort.Slice(planTeams, func(i, j int) bool { return planTeams[i].TeamID.ValueString() < planTeams[j].TeamID.ValueString() }) @@ -753,7 +753,7 @@ func hasTeamsChanged(planTeams, stateTeams []TfTeamModel) bool { return !reflect.DeepEqual(planTeams, stateTeams) } -func hasLimitsChanged(planLimits, stateLimits []TfLimitModel) bool { +func hasLimitsChanged(planLimits, stateLimits []TFLimitModel) bool { sort.Slice(planLimits, func(i, j int) bool { return planLimits[i].Name.ValueString() < planLimits[j].Name.ValueString() }) @@ -763,7 +763,7 @@ func hasLimitsChanged(planLimits, stateLimits []TfLimitModel) bool { return !reflect.DeepEqual(planLimits, stateLimits) } -func UpdateProject(ctx context.Context, client GroupProjectService, projectState, projectPlan *TfProjectRSModel) error { +func UpdateProject(ctx context.Context, client GroupProjectService, projectState, projectPlan *TFProjectRSModel) error { if projectPlan.Name.Equal(projectState.Name) { return nil } @@ -833,8 +833,8 @@ func ResourceProjectDependentsDeletingRefreshFunc(ctx context.Context, projectID } } -func getChangesInTeamsSet(planTeams, stateTeams []TfTeamModel) (newElements, changedElements, removedElements []TfTeamModel) { - var removedTeams, newTeams, changedTeams []TfTeamModel +func getChangesInTeamsSet(planTeams, stateTeams []TFTeamModel) (newElements, changedElements, removedElements []TFTeamModel) { + var removedTeams, newTeams, changedTeams []TFTeamModel planTeamsMap := NewTfTeamModelMap(planTeams) stateTeamsMap := NewTfTeamModelMap(stateTeams) @@ -857,8 +857,8 @@ func getChangesInTeamsSet(planTeams, stateTeams []TfTeamModel) (newElements, cha return newTeams, changedTeams, removedTeams } -func getChangesInLimitsSet(planLimits, stateLimits []TfLimitModel) (newElements, changedElements, removedElements []TfLimitModel) { - var removedLimits, newLimits, changedLimits []TfLimitModel +func getChangesInLimitsSet(planLimits, stateLimits []TFLimitModel) (newElements, changedElements, removedElements []TFLimitModel) { + var removedLimits, newLimits, changedLimits []TFLimitModel planLimitsMap := NewTfLimitModelMap(planLimits) stateTeamsMap := NewTfLimitModelMap(stateLimits) diff --git a/internal/service/project/resource_project_test.go b/internal/service/project/resource_project_test.go index 9bea25247a..7b77cb0d0f 100644 --- a/internal/service/project/resource_project_test.go +++ b/internal/service/project/resource_project_test.go @@ -24,10 +24,10 @@ import ( var ( name = types.StringValue("sameName") diffName = types.StringValue("diffName") - projectStateName = project.TfProjectRSModel{ + projectStateName = project.TFProjectRSModel{ Name: name, } - projectStateNameDiff = project.TfProjectRSModel{ + projectStateNameDiff = project.TFProjectRSModel{ Name: diffName, } dummyProjectID = "6575af27f93c7a6a4b50b239" @@ -126,7 +126,7 @@ func TestFilterUserDefinedLimits(t *testing.T) { testCases := []struct { name string allAtlasLimits []admin.DataFederationLimit - tfLimits []project.TfLimitModel + tfLimits []project.TFLimitModel expectedResult []admin.DataFederationLimit }{ { @@ -136,7 +136,7 @@ func TestFilterUserDefinedLimits(t *testing.T) { createDataFederationLimit("2"), createDataFederationLimit("3"), }, - tfLimits: []project.TfLimitModel{ + tfLimits: []project.TFLimitModel{ { Name: types.StringValue("1"), }, @@ -154,7 +154,7 @@ func TestFilterUserDefinedLimits(t *testing.T) { allAtlasLimits: []admin.DataFederationLimit{ createDataFederationLimit("1"), }, - tfLimits: []project.TfLimitModel{}, + tfLimits: []project.TFLimitModel{}, expectedResult: []admin.DataFederationLimit{}, }, } @@ -173,8 +173,8 @@ func TestUpdateProject(t *testing.T) { testCases := []struct { name string mockResponses ProjectResponse - projectState project.TfProjectRSModel - projectPlan project.TfProjectRSModel + projectState project.TFProjectRSModel + projectPlan project.TFProjectRSModel expectedError bool }{ { @@ -224,7 +224,7 @@ func TestUpdateProject(t *testing.T) { } func TestUpdateProjectLimits(t *testing.T) { - twoLimits := []project.TfLimitModel{ + twoLimits := []project.TFLimitModel{ { Name: types.StringValue("limit1"), }, @@ -232,12 +232,12 @@ func TestUpdateProjectLimits(t *testing.T) { Name: types.StringValue("limit2"), }, } - oneLimit := []project.TfLimitModel{ + oneLimit := []project.TFLimitModel{ { Name: types.StringValue("limit1"), }, } - updatedLimit := []project.TfLimitModel{ + updatedLimit := []project.TFLimitModel{ { Name: types.StringValue("limit1"), Value: types.Int64Value(6), @@ -249,17 +249,17 @@ func TestUpdateProjectLimits(t *testing.T) { testCases := []struct { name string mockResponses DeleteProjectLimitResponse - projectState project.TfProjectRSModel - projectPlan project.TfProjectRSModel + projectState project.TFProjectRSModel + projectPlan project.TFProjectRSModel expectedError bool }{ { name: "Limits has not changed", - projectState: project.TfProjectRSModel{ + projectState: project.TFProjectRSModel{ Name: name, Limits: singleLimitSet, }, - projectPlan: project.TfProjectRSModel{ + projectPlan: project.TFProjectRSModel{ Name: name, Limits: singleLimitSet, }, @@ -268,11 +268,11 @@ func TestUpdateProjectLimits(t *testing.T) { }, { name: "Adding limits", - projectState: project.TfProjectRSModel{ + projectState: project.TFProjectRSModel{ Name: name, Limits: singleLimitSet, }, - projectPlan: project.TfProjectRSModel{ + projectPlan: project.TFProjectRSModel{ Name: name, Limits: twoLimitSet, }, @@ -283,11 +283,11 @@ func TestUpdateProjectLimits(t *testing.T) { }, { name: "Removing limits", - projectState: project.TfProjectRSModel{ + projectState: project.TFProjectRSModel{ Name: name, Limits: twoLimitSet, }, - projectPlan: project.TfProjectRSModel{ + projectPlan: project.TFProjectRSModel{ Name: name, Limits: singleLimitSet, }, @@ -298,11 +298,11 @@ func TestUpdateProjectLimits(t *testing.T) { }, { name: "Updating limits", - projectState: project.TfProjectRSModel{ + projectState: project.TFProjectRSModel{ Name: name, Limits: singleLimitSet, }, - projectPlan: project.TfProjectRSModel{ + projectPlan: project.TFProjectRSModel{ Name: name, Limits: updatedLimitSet, }, @@ -331,35 +331,35 @@ func TestUpdateProjectLimits(t *testing.T) { func TestUpdateProjectTeams(t *testing.T) { teamRoles, _ := types.SetValueFrom(context.Background(), types.StringType, []string{"BASIC_PERMISSION"}) - teamOne := project.TfTeamModel{ + teamOne := project.TFTeamModel{ TeamID: types.StringValue("team1"), RoleNames: teamRoles, } - teamTwo := project.TfTeamModel{ + teamTwo := project.TFTeamModel{ TeamID: types.StringValue("team2"), } teamRolesUpdated, _ := types.SetValueFrom(context.Background(), types.StringType, []string{"ADMIN_PERMISSION"}) - updatedTeam := project.TfTeamModel{ + updatedTeam := project.TFTeamModel{ TeamID: types.StringValue("team1"), RoleNames: teamRolesUpdated, } - singleTeamSet, _ := types.SetValueFrom(context.Background(), project.TfTeamObjectType, []project.TfTeamModel{teamOne}) - twoTeamSet, _ := types.SetValueFrom(context.Background(), project.TfTeamObjectType, []project.TfTeamModel{teamOne, teamTwo}) - updatedTeamSet, _ := types.SetValueFrom(context.Background(), project.TfTeamObjectType, []project.TfTeamModel{updatedTeam}) + singleTeamSet, _ := types.SetValueFrom(context.Background(), project.TfTeamObjectType, []project.TFTeamModel{teamOne}) + twoTeamSet, _ := types.SetValueFrom(context.Background(), project.TfTeamObjectType, []project.TFTeamModel{teamOne, teamTwo}) + updatedTeamSet, _ := types.SetValueFrom(context.Background(), project.TfTeamObjectType, []project.TFTeamModel{updatedTeam}) testCases := []struct { name string - projectState project.TfProjectRSModel - projectPlan project.TfProjectRSModel + projectState project.TFProjectRSModel + projectPlan project.TFProjectRSModel expectedError bool }{ { name: "Teams has not changed", - projectState: project.TfProjectRSModel{ + projectState: project.TFProjectRSModel{ Name: name, Teams: singleTeamSet, }, - projectPlan: project.TfProjectRSModel{ + projectPlan: project.TFProjectRSModel{ Name: name, Teams: singleTeamSet, }, @@ -367,11 +367,11 @@ func TestUpdateProjectTeams(t *testing.T) { }, { name: "Add teams", - projectState: project.TfProjectRSModel{ + projectState: project.TFProjectRSModel{ Name: name, Teams: singleTeamSet, }, - projectPlan: project.TfProjectRSModel{ + projectPlan: project.TFProjectRSModel{ Name: name, Teams: twoTeamSet, }, @@ -379,11 +379,11 @@ func TestUpdateProjectTeams(t *testing.T) { }, { name: "Remove teams", - projectState: project.TfProjectRSModel{ + projectState: project.TFProjectRSModel{ Name: name, Teams: twoTeamSet, }, - projectPlan: project.TfProjectRSModel{ + projectPlan: project.TFProjectRSModel{ Name: name, Teams: singleTeamSet, }, @@ -391,11 +391,11 @@ func TestUpdateProjectTeams(t *testing.T) { }, { name: "Update teams", - projectState: project.TfProjectRSModel{ + projectState: project.TFProjectRSModel{ Name: name, Teams: singleTeamSet, }, - projectPlan: project.TfProjectRSModel{ + projectPlan: project.TFProjectRSModel{ Name: name, Teams: updatedTeamSet, }, From 56a883077c3251ba88a016a81156b2755b892e44 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Thu, 18 Jan 2024 10:06:28 +0100 Subject: [PATCH 10/15] adding unit test for empty IP lists --- internal/service/project/model_project.go | 2 +- .../service/project/model_project_test.go | 43 ++++++++++++++++--- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/internal/service/project/model_project.go b/internal/service/project/model_project.go index 1df2f6b8c8..8f91e0e46f 100644 --- a/internal/service/project/model_project.go +++ b/internal/service/project/model_project.go @@ -77,7 +77,7 @@ func NewTFIPAddressesModel(ctx context.Context, ipAddresses *admin.GroupIPAddres } } } - obj, _ := types.ObjectValueFrom(ctx, IPAddressesObjectType.AttrTypes, TFIPAddressesModel{ // TODO handle errors. + obj, _ := types.ObjectValueFrom(ctx, IPAddressesObjectType.AttrTypes, TFIPAddressesModel{ Services: TFServicesModel{ Clusters: clusterIPs, }, diff --git a/internal/service/project/model_project_test.go b/internal/service/project/model_project_test.go index 70e7bdf878..a40e4bff0f 100644 --- a/internal/service/project/model_project_test.go +++ b/internal/service/project/model_project_test.go @@ -30,6 +30,7 @@ var ( roleSet, _ = types.SetValueFrom(context.Background(), types.StringType, roles) ipAddresses = []string{"13.13.13.13"} ipAddressesList, _ = types.ListValueFrom(context.Background(), types.StringType, ipAddresses) + empptyTFList, _ = types.ListValueFrom(context.Background(), types.StringType, []string{}) teamRolesSDK = []admin.TeamRole{ { TeamId: conversion.StringPtr("teamId"), @@ -80,11 +81,22 @@ var ( }, }, }) - emptyIPAddressesTF, _ = types.ObjectValueFrom(context.Background(), project.IPAddressesObjectType.AttrTypes, project.TFIPAddressesModel{ + IPAddressesNoClusterTF, _ = types.ObjectValueFrom(context.Background(), project.IPAddressesObjectType.AttrTypes, project.TFIPAddressesModel{ Services: project.TFServicesModel{ Clusters: []project.TFClusterIPsModel{}, }, }) + IPAddressesWithClusterNoIPsTF, _ = types.ObjectValueFrom(context.Background(), project.IPAddressesObjectType.AttrTypes, project.TFIPAddressesModel{ + Services: project.TFServicesModel{ + Clusters: []project.TFClusterIPsModel{ + { + Inbound: empptyTFList, + Outbound: empptyTFList, + ClusterName: types.StringValue("Cluster0"), + }, + }, + }, + }) projectSDK = admin.Group{ Id: admin.PtrString(projectID), Name: projectName, @@ -99,7 +111,7 @@ var ( IsRealtimePerformancePanelEnabled: admin.PtrBool(true), IsSchemaAdvisorEnabled: admin.PtrBool(true), } - projectIPAddressesSDK = admin.GroupIPAddresses{ + IPAddressesSDK = admin.GroupIPAddresses{ GroupId: admin.PtrString(projectID), Services: &admin.GroupService{ Clusters: &[]admin.ClusterIPAddresses{ @@ -111,6 +123,18 @@ var ( }, }, } + IPAddressesWithClusterNoIPsSDK = admin.GroupIPAddresses{ + GroupId: admin.PtrString(projectID), + Services: &admin.GroupService{ + Clusters: &[]admin.ClusterIPAddresses{ + { + Inbound: &[]string{}, + Outbound: &[]string{}, + ClusterName: admin.PtrString("Cluster0"), + }, + }, + }, + } ) func TestTeamsDataSourceSDKToTFModel(t *testing.T) { @@ -184,7 +208,7 @@ func TestProjectDataSourceSDKToDataSourceTFModel(t *testing.T) { TotalCount: conversion.IntPtr(1), }, Settings: &projectSettingsSDK, - IPAddresses: &projectIPAddressesSDK, + IPAddresses: &IPAddressesSDK, Limits: limitsSDK, }, expectedTFModel: project.TFProjectDSModel{ @@ -234,7 +258,7 @@ func TestProjectDataSourceSDKToResourceTFModel(t *testing.T) { TotalCount: conversion.IntPtr(1), }, Settings: &projectSettingsSDK, - IPAddresses: &projectIPAddressesSDK, + IPAddresses: &IPAddressesSDK, Limits: limitsSDK, }, expectedTFModel: project.TFProjectRSModel{ @@ -374,7 +398,7 @@ func TestIPAddressesModelToTF(t *testing.T) { { name: "No response", sdkModel: nil, - expectedResult: emptyIPAddressesTF, + expectedResult: IPAddressesNoClusterTF, }, { name: "Empty response when no clusters are created", @@ -384,11 +408,16 @@ func TestIPAddressesModelToTF(t *testing.T) { Clusters: &[]admin.ClusterIPAddresses{}, }, }, - expectedResult: emptyIPAddressesTF, + expectedResult: IPAddressesNoClusterTF, + }, + { + name: "One cluster with empty IP lists", + sdkModel: &IPAddressesWithClusterNoIPsSDK, + expectedResult: IPAddressesWithClusterNoIPsTF, }, { name: "Full response", - sdkModel: &projectIPAddressesSDK, + sdkModel: &IPAddressesSDK, expectedResult: ipAddressesTF, }, } From ba4c763a591cef9f7677e19a63430d044e5ba708 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Thu, 18 Jan 2024 16:39:10 +0100 Subject: [PATCH 11/15] Update .github/workflows/code-health.yml Co-authored-by: Leo Antoli <430982+lantoli@users.noreply.github.com> --- .github/workflows/code-health.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-health.yml b/.github/workflows/code-health.yml index fcd96a246d..e41ed7b311 100644 --- a/.github/workflows/code-health.yml +++ b/.github/workflows/code-health.yml @@ -21,7 +21,7 @@ jobs: with: go-version-file: 'go.mod' - name: Mock generation - run: make tools && make generate-mocks + run: make tools generate-mocks - name: Check for uncommited files run: | export FILES=$(git ls-files -o -m --directory --exclude-standard --no-empty-directory) From 39491e40c8670a50f5e8eb92acabcd7c6dfe63f7 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Fri, 19 Jan 2024 11:00:42 +0100 Subject: [PATCH 12/15] adjust order of defining computed attributes --- internal/service/project/data_source_project.go | 6 +++--- internal/service/project/data_source_projects.go | 6 +++--- internal/service/project/resource_project.go | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/service/project/data_source_project.go b/internal/service/project/data_source_project.go index 4e87bd51ec..94b7dce8ab 100644 --- a/internal/service/project/data_source_project.go +++ b/internal/service/project/data_source_project.go @@ -139,10 +139,13 @@ func (d *projectDS) Schema(ctx context.Context, req datasource.SchemaRequest, re }, }, "ip_addresses": schema.SingleNestedAttribute{ + Computed: true, Attributes: map[string]schema.Attribute{ "services": schema.SingleNestedAttribute{ + Computed: true, Attributes: map[string]schema.Attribute{ "clusters": schema.ListNestedAttribute{ + Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "cluster_name": schema.StringAttribute{ @@ -158,13 +161,10 @@ func (d *projectDS) Schema(ctx context.Context, req datasource.SchemaRequest, re }, }, }, - Computed: true, }, }, - Computed: true, }, }, - Computed: true, }, }, } diff --git a/internal/service/project/data_source_projects.go b/internal/service/project/data_source_projects.go index 9a7f4fa098..d2fc5e9c56 100644 --- a/internal/service/project/data_source_projects.go +++ b/internal/service/project/data_source_projects.go @@ -135,10 +135,13 @@ func (d *ProjectsDS) Schema(ctx context.Context, req datasource.SchemaRequest, r }, }, "ip_addresses": schema.SingleNestedAttribute{ + Computed: true, Attributes: map[string]schema.Attribute{ "services": schema.SingleNestedAttribute{ + Computed: true, Attributes: map[string]schema.Attribute{ "clusters": schema.ListNestedAttribute{ + Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "cluster_name": schema.StringAttribute{ @@ -154,13 +157,10 @@ func (d *ProjectsDS) Schema(ctx context.Context, req datasource.SchemaRequest, r }, }, }, - Computed: true, }, }, - Computed: true, }, }, - Computed: true, }, }, }, diff --git a/internal/service/project/resource_project.go b/internal/service/project/resource_project.go index 9d341460a9..b5f02f4c8b 100644 --- a/internal/service/project/resource_project.go +++ b/internal/service/project/resource_project.go @@ -218,10 +218,13 @@ func (r *projectRS) Schema(ctx context.Context, req resource.SchemaRequest, resp Optional: true, }, "ip_addresses": schema.SingleNestedAttribute{ + Computed: true, Attributes: map[string]schema.Attribute{ "services": schema.SingleNestedAttribute{ + Computed: true, Attributes: map[string]schema.Attribute{ "clusters": schema.ListNestedAttribute{ + Computed: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "cluster_name": schema.StringAttribute{ @@ -237,13 +240,10 @@ func (r *projectRS) Schema(ctx context.Context, req resource.SchemaRequest, resp }, }, }, - Computed: true, }, }, - Computed: true, }, }, - Computed: true, }, }, Blocks: map[string]schema.Block{ From a422d904194dac546f05d4bf94c303c2469a0336 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Fri, 19 Jan 2024 11:07:45 +0100 Subject: [PATCH 13/15] extract const for dataSourceName and resourceName --- .../project/data_source_project_test.go | 22 +++++------ .../service/project/resource_project_test.go | 38 ++++++++----------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/internal/service/project/data_source_project_test.go b/internal/service/project/data_source_project_test.go index c63c2c89be..2e66757f18 100644 --- a/internal/service/project/data_source_project_test.go +++ b/internal/service/project/data_source_project_test.go @@ -13,11 +13,12 @@ import ( "github.com/mongodb/terraform-provider-mongodbatlas/internal/testutil/acc" ) +const dataSourceName = "data.mongodbatlas_project.test" + func TestAccProjectDSProject_byID(t *testing.T) { var ( - projectName = acctest.RandomWithPrefix("test-acc") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") - dataSourceName = "data.mongodbatlas_project.test" + projectName = acctest.RandomWithPrefix("test-acc") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acc.PreCheckBasic(t); acc.PreCheckProjectTeamsIdsWithMinCount(t, 2) }, @@ -49,9 +50,8 @@ func TestAccProjectDSProject_byID(t *testing.T) { func TestAccProjectDSProject_byName(t *testing.T) { var ( - projectName = acctest.RandomWithPrefix("test-acc") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") - dataSourceName = "data.mongodbatlas_project.test" + projectName = acctest.RandomWithPrefix("test-acc") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acc.PreCheckBasic(t); acc.PreCheckProjectTeamsIdsWithMinCount(t, 2) }, @@ -83,9 +83,8 @@ func TestAccProjectDSProject_byName(t *testing.T) { func TestAccProjectDSProject_defaultFlags(t *testing.T) { var ( - projectName = acctest.RandomWithPrefix("test-acc") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") - dataSourceName = "data.mongodbatlas_project.test" + projectName = acctest.RandomWithPrefix("test-acc") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acc.PreCheckBasic(t); acc.PreCheckProjectTeamsIdsWithMinCount(t, 2) }, @@ -123,9 +122,8 @@ func TestAccProjectDSProject_defaultFlags(t *testing.T) { func TestAccProjectDSProject_limits(t *testing.T) { var ( - projectName = acctest.RandomWithPrefix("test-acc") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") - dataSourceName = "data.mongodbatlas_project.test" + projectName = acctest.RandomWithPrefix("test-acc") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acc.PreCheckBasic(t) }, diff --git a/internal/service/project/resource_project_test.go b/internal/service/project/resource_project_test.go index 4128bc94d8..bdfaeeb1fa 100644 --- a/internal/service/project/resource_project_test.go +++ b/internal/service/project/resource_project_test.go @@ -473,10 +473,12 @@ func TestResourceProjectDependentsDeletingRefreshFunc(t *testing.T) { } } +const resourceName = "mongodbatlas_project.test" + func TestAccProjectRSProject_basic(t *testing.T) { var ( - group admin.Group - resourceName = "mongodbatlas_project.test" + group admin.Group + projectName = acctest.RandomWithPrefix("test-acc") orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") clusterCount = "0" @@ -565,7 +567,6 @@ func TestAccProjectRSProject_basic(t *testing.T) { func TestAccProjectRSProject_withProjectOwner(t *testing.T) { var ( group admin.Group - resourceName = "mongodbatlas_project.test" projectName = acctest.RandomWithPrefix("test-acc") orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") projectOwnerID = os.Getenv("MONGODB_ATLAS_PROJECT_OWNER_ID") @@ -592,7 +593,6 @@ func TestAccProjectRSProject_withProjectOwner(t *testing.T) { func TestAccProjectRSGovProject_withProjectOwner(t *testing.T) { var ( group admin.Group - resourceName = "mongodbatlas_project.test" projectName = acctest.RandomWithPrefix("tf-acc-project") orgID = os.Getenv("MONGODB_ATLAS_ORG_ID_GOV") projectOwnerID = os.Getenv("MONGODB_ATLAS_PROJECT_OWNER_ID_GOV") @@ -618,7 +618,6 @@ func TestAccProjectRSGovProject_withProjectOwner(t *testing.T) { func TestAccProjectRSProject_withFalseDefaultSettings(t *testing.T) { var ( group admin.Group - resourceName = "mongodbatlas_project.test" projectName = acctest.RandomWithPrefix("tf-acc-project") orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") projectOwnerID = os.Getenv("MONGODB_ATLAS_PROJECT_OWNER_ID") @@ -645,7 +644,6 @@ func TestAccProjectRSProject_withFalseDefaultSettings(t *testing.T) { func TestAccProjectRSProject_withUpdatedSettings(t *testing.T) { var ( group admin.Group - resourceName = "mongodbatlas_project.test" projectName = acctest.RandomWithPrefix("tf-acc-project") orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") projectOwnerID = os.Getenv("MONGODB_ATLAS_PROJECT_OWNER_ID") @@ -707,7 +705,6 @@ func TestAccProjectRSProject_withUpdatedSettings(t *testing.T) { func TestAccProjectRSProject_withUpdatedRole(t *testing.T) { var ( - resourceName = "mongodbatlas_project.test" projectName = acctest.RandomWithPrefix("tf-acc-project") orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") roleName = "GROUP_DATA_ACCESS_ADMIN" @@ -741,10 +738,9 @@ func TestAccProjectRSProject_withUpdatedRole(t *testing.T) { func TestAccProjectRSProject_updatedToEmptyRoles(t *testing.T) { var ( - group admin.Group - resourceName = "mongodbatlas_project.test" - projectName = acctest.RandomWithPrefix("test-acc") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + group admin.Group + projectName = acctest.RandomWithPrefix("test-acc") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acc.PreCheckBasic(t); acc.PreCheckProjectTeamsIdsWithMinCount(t, 1) }, @@ -784,9 +780,8 @@ func TestAccProjectRSProject_updatedToEmptyRoles(t *testing.T) { func TestAccProjectRSProject_importBasic(t *testing.T) { var ( - projectName = acctest.RandomWithPrefix("tf-acc-project") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") - resourceName = "mongodbatlas_project.test" + projectName = acctest.RandomWithPrefix("tf-acc-project") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") ) resource.ParallelTest(t, resource.TestCase{ @@ -812,9 +807,8 @@ func TestAccProjectRSProject_importBasic(t *testing.T) { func TestAccProjectRSProject_withUpdatedLimits(t *testing.T) { var ( - resourceName = "mongodbatlas_project.test" - projectName = acctest.RandomWithPrefix("tf-acc-project") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + projectName = acctest.RandomWithPrefix("tf-acc-project") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") ) resource.ParallelTest(t, resource.TestCase{ @@ -906,9 +900,8 @@ func TestAccProjectRSProject_withUpdatedLimits(t *testing.T) { func TestAccProjectRSProject_updatedToEmptyLimits(t *testing.T) { var ( - resourceName = "mongodbatlas_project.test" - projectName = acctest.RandomWithPrefix("tf-acc-project") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + projectName = acctest.RandomWithPrefix("tf-acc-project") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acc.PreCheckBasic(t) }, @@ -964,9 +957,8 @@ func TestAccProjectRSProject_withInvalidLimitName(t *testing.T) { func TestAccProjectRSProject_withInvalidLimitNameOnUpdate(t *testing.T) { var ( - resourceName = "mongodbatlas_project.test" - projectName = acctest.RandomWithPrefix("tf-acc-project") - orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") + projectName = acctest.RandomWithPrefix("tf-acc-project") + orgID = os.Getenv("MONGODB_ATLAS_ORG_ID") ) resource.ParallelTest(t, resource.TestCase{ From a857967526f626597a4e7d1100c5cf297e2b5a45 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Fri, 19 Jan 2024 11:37:57 +0100 Subject: [PATCH 14/15] handle diags when setting object type --- .../service/project/data_source_project.go | 8 ++++-- .../service/project/data_source_projects.go | 19 ++++++++----- internal/service/project/model_project.go | 27 ++++++++++++------- .../service/project/model_project_test.go | 17 +++++++++--- internal/service/project/resource_project.go | 18 ++++++++++--- 5 files changed, 63 insertions(+), 26 deletions(-) diff --git a/internal/service/project/data_source_project.go b/internal/service/project/data_source_project.go index 94b7dce8ab..d788dc99d9 100644 --- a/internal/service/project/data_source_project.go +++ b/internal/service/project/data_source_project.go @@ -208,9 +208,13 @@ func (d *projectDS) Read(ctx context.Context, req datasource.ReadRequest, resp * return } - projectState = NewTFProjectDataSourceModel(ctx, project, *projectProps) + newProjectState, diags := NewTFProjectDataSourceModel(ctx, project, *projectProps) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } - resp.Diagnostics.Append(resp.State.Set(ctx, &projectState)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &newProjectState)...) if resp.Diagnostics.HasError() { return } diff --git a/internal/service/project/data_source_projects.go b/internal/service/project/data_source_projects.go index d2fc5e9c56..78c7766b5b 100644 --- a/internal/service/project/data_source_projects.go +++ b/internal/service/project/data_source_projects.go @@ -6,6 +6,7 @@ import ( "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/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion" @@ -185,9 +186,9 @@ func (d *ProjectsDS) Read(ctx context.Context, req datasource.ReadRequest, resp return } - err = populateProjectsDataSourceModel(ctx, connV2, &stateModel, projectsRes) - if err != nil { - resp.Diagnostics.AddError("error in monogbatlas_projects data source", err.Error()) + diags := populateProjectsDataSourceModel(ctx, connV2, &stateModel, projectsRes) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { return } @@ -197,19 +198,23 @@ func (d *ProjectsDS) Read(ctx context.Context, req datasource.ReadRequest, resp } } -func populateProjectsDataSourceModel(ctx context.Context, connV2 *admin.APIClient, stateModel *tfProjectsDSModel, projectsRes *admin.PaginatedAtlasGroup) error { +func populateProjectsDataSourceModel(ctx context.Context, connV2 *admin.APIClient, stateModel *tfProjectsDSModel, projectsRes *admin.PaginatedAtlasGroup) diag.Diagnostics { + diagnostics := []diag.Diagnostic{} input := projectsRes.GetResults() results := make([]*TFProjectDSModel, 0, len(input)) for i := range input { project := input[i] projectProps, err := GetProjectPropsFromAPI(ctx, ServiceFromClient(connV2), project.GetId()) if err == nil { // if the project is still valid, e.g. could have just been deleted - projectModel := NewTFProjectDataSourceModel(ctx, &project, *projectProps) - results = append(results, &projectModel) + projectModel, diags := NewTFProjectDataSourceModel(ctx, &project, *projectProps) + diagnostics = append(diagnostics, diags...) + if projectModel != nil { + results = append(results, projectModel) + } } } stateModel.Results = results stateModel.TotalCount = types.Int64Value(int64(projectsRes.GetTotalCount())) stateModel.ID = types.StringValue(id.UniqueId()) - return nil + return diagnostics } diff --git a/internal/service/project/model_project.go b/internal/service/project/model_project.go index 9fbb08d28a..fdd53c1a72 100644 --- a/internal/service/project/model_project.go +++ b/internal/service/project/model_project.go @@ -3,15 +3,19 @@ package project import ( "context" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion" "go.mongodb.org/atlas-sdk/v20231115004/admin" ) -func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, projectProps AdditionalProperties) TFProjectDSModel { - ipAddressesModel := NewTFIPAddressesModel(ctx, projectProps.IPAddresses) +func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, projectProps AdditionalProperties) (*TFProjectDSModel, diag.Diagnostics) { + ipAddressesModel, diags := NewTFIPAddressesModel(ctx, projectProps.IPAddresses) + if diags.HasError() { + return nil, diags + } projectSettings := projectProps.Settings - return TFProjectDSModel{ + return &TFProjectDSModel{ ID: types.StringValue(project.GetId()), ProjectID: types.StringValue(project.GetId()), Name: types.StringValue(project.Name), @@ -27,7 +31,7 @@ func NewTFProjectDataSourceModel(ctx context.Context, project *admin.Group, proj Teams: NewTFTeamsDataSourceModel(ctx, projectProps.Teams), Limits: NewTFLimitsDataSourceModel(ctx, projectProps.Limits), IPAddresses: ipAddressesModel, - } + }, nil } func NewTFTeamsDataSourceModel(ctx context.Context, atlasTeams *admin.PaginatedTeamRole) []*TFTeamDSModel { @@ -62,7 +66,7 @@ func NewTFLimitsDataSourceModel(ctx context.Context, dataFederationLimits []admi return limits } -func NewTFIPAddressesModel(ctx context.Context, ipAddresses *admin.GroupIPAddresses) types.Object { +func NewTFIPAddressesModel(ctx context.Context, ipAddresses *admin.GroupIPAddresses) (types.Object, diag.Diagnostics) { clusterIPs := []TFClusterIPsModel{} if ipAddresses != nil && ipAddresses.Services != nil { clusterIPAddresses := ipAddresses.Services.GetClusters() @@ -77,16 +81,19 @@ func NewTFIPAddressesModel(ctx context.Context, ipAddresses *admin.GroupIPAddres } } } - obj, _ := types.ObjectValueFrom(ctx, IPAddressesObjectType.AttrTypes, TFIPAddressesModel{ + obj, diags := types.ObjectValueFrom(ctx, IPAddressesObjectType.AttrTypes, TFIPAddressesModel{ Services: TFServicesModel{ Clusters: clusterIPs, }, }) - return obj + return obj, diags } -func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, projectProps AdditionalProperties) *TFProjectRSModel { - ipAddressesModel := NewTFIPAddressesModel(ctx, projectProps.IPAddresses) +func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, projectProps AdditionalProperties) (*TFProjectRSModel, diag.Diagnostics) { + ipAddressesModel, diags := NewTFIPAddressesModel(ctx, projectProps.IPAddresses) + if diags.HasError() { + return nil, diags + } projectPlan := TFProjectRSModel{ ID: types.StringValue(projectRes.GetId()), Name: types.StringValue(projectRes.Name), @@ -109,7 +116,7 @@ func NewTFProjectResourceModel(ctx context.Context, projectRes *admin.Group, pro projectPlan.IsSchemaAdvisorEnabled = types.BoolValue(*projectSettings.IsSchemaAdvisorEnabled) } - return &projectPlan + return &projectPlan, nil } func newTFLimitsResourceModel(ctx context.Context, dataFederationLimits []admin.DataFederationLimit) types.Set { diff --git a/internal/service/project/model_project_test.go b/internal/service/project/model_project_test.go index abd727d7eb..8051d71989 100644 --- a/internal/service/project/model_project_test.go +++ b/internal/service/project/model_project_test.go @@ -234,8 +234,11 @@ func TestProjectDataSourceSDKToDataSourceTFModel(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - resultModel := project.NewTFProjectDataSourceModel(context.Background(), tc.project, tc.projectProps) - if !assert.Equal(t, tc.expectedTFModel, resultModel) { + resultModel, diags := project.NewTFProjectDataSourceModel(context.Background(), tc.project, tc.projectProps) + if diags.HasError() { + t.Errorf("unexpected errors found: %s", diags.Errors()[0].Summary()) + } + if !assert.Equal(t, tc.expectedTFModel, *resultModel) { t.Errorf("created terraform model did not match expected output") } }) @@ -283,7 +286,10 @@ func TestProjectDataSourceSDKToResourceTFModel(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - resultModel := project.NewTFProjectResourceModel(context.Background(), tc.project, tc.projectProps) + resultModel, diags := project.NewTFProjectResourceModel(context.Background(), tc.project, tc.projectProps) + if diags.HasError() { + t.Errorf("unexpected errors found: %s", diags.Errors()[0].Summary()) + } if !assert.Equal(t, tc.expectedTFModel, *resultModel) { t.Errorf("created terraform model did not match expected output") } @@ -424,7 +430,10 @@ func TestIPAddressesModelToTF(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - resultModel := project.NewTFIPAddressesModel(context.Background(), tc.sdkModel) + resultModel, diags := project.NewTFIPAddressesModel(context.Background(), tc.sdkModel) + if diags.HasError() { + t.Errorf("unexpected errors found: %s", diags.Errors()[0].Summary()) + } if !assert.Equal(t, tc.expectedResult, resultModel) { t.Errorf("created terraform model did not match expected output") } diff --git a/internal/service/project/resource_project.go b/internal/service/project/resource_project.go index b5f02f4c8b..d0a5a3a70a 100644 --- a/internal/service/project/resource_project.go +++ b/internal/service/project/resource_project.go @@ -409,7 +409,11 @@ func (r *projectRS) Create(ctx context.Context, req resource.CreateRequest, resp filteredLimits := FilterUserDefinedLimits(projectProps.Limits, limits) projectProps.Limits = filteredLimits - projectPlanNew := NewTFProjectResourceModel(ctx, projectRes, *projectProps) + projectPlanNew, diags := NewTFProjectResourceModel(ctx, projectRes, *projectProps) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } updatePlanFromConfig(projectPlanNew, &projectPlan) // set state to fully populated data @@ -457,7 +461,11 @@ func (r *projectRS) Read(ctx context.Context, req resource.ReadRequest, resp *re filteredLimits := FilterUserDefinedLimits(projectProps.Limits, limits) projectProps.Limits = filteredLimits - projectStateNew := NewTFProjectResourceModel(ctx, projectRes, *projectProps) + projectStateNew, diags := NewTFProjectResourceModel(ctx, projectRes, *projectProps) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } updatePlanFromConfig(projectStateNew, &projectState) // save read data into Terraform state @@ -530,7 +538,11 @@ func (r *projectRS) Update(ctx context.Context, req resource.UpdateRequest, resp filteredLimits := FilterUserDefinedLimits(projectProps.Limits, planLimits) projectProps.Limits = filteredLimits - projectPlanNew := NewTFProjectResourceModel(ctx, projectRes, *projectProps) + projectPlanNew, diags := NewTFProjectResourceModel(ctx, projectRes, *projectProps) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } updatePlanFromConfig(projectPlanNew, &projectPlan) // save updated data into Terraform state From 605705b81323dceff92dd2a52a82df9efba61f95 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Fri, 19 Jan 2024 12:05:09 +0100 Subject: [PATCH 15/15] remove redudant if and message when asserting model --- .../service/project/model_project_test.go | 32 +++++-------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/internal/service/project/model_project_test.go b/internal/service/project/model_project_test.go index 8051d71989..e287b84716 100644 --- a/internal/service/project/model_project_test.go +++ b/internal/service/project/model_project_test.go @@ -162,9 +162,7 @@ func TestTeamsDataSourceSDKToTFModel(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { resultModel := project.NewTFTeamsDataSourceModel(context.Background(), tc.paginatedTeamRole) - if !assert.Equal(t, tc.expectedTFModel, resultModel) { - t.Errorf("created terraform model did not match expected output") - } + assert.Equal(t, tc.expectedTFModel, resultModel) }) } } @@ -185,9 +183,7 @@ func TestLimitsDataSourceSDKToTFModel(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { resultModel := project.NewTFLimitsDataSourceModel(context.Background(), tc.dataFederationLimits) - if !assert.Equal(t, tc.expectedTFModel, resultModel) { - t.Errorf("created terraform model did not match expected output") - } + assert.Equal(t, tc.expectedTFModel, resultModel) }) } } @@ -238,9 +234,7 @@ func TestProjectDataSourceSDKToDataSourceTFModel(t *testing.T) { if diags.HasError() { t.Errorf("unexpected errors found: %s", diags.Errors()[0].Summary()) } - if !assert.Equal(t, tc.expectedTFModel, *resultModel) { - t.Errorf("created terraform model did not match expected output") - } + assert.Equal(t, tc.expectedTFModel, *resultModel) }) } } @@ -290,9 +284,7 @@ func TestProjectDataSourceSDKToResourceTFModel(t *testing.T) { if diags.HasError() { t.Errorf("unexpected errors found: %s", diags.Errors()[0].Summary()) } - if !assert.Equal(t, tc.expectedTFModel, *resultModel) { - t.Errorf("created terraform model did not match expected output") - } + assert.Equal(t, tc.expectedTFModel, *resultModel) }) } } @@ -320,9 +312,7 @@ func TestTeamRoleListTFtoSDK(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { resultModel := project.NewTeamRoleList(context.Background(), tc.teamRolesTF) - if !assert.Equal(t, tc.expectedResult, resultModel) { - t.Errorf("created terraform model did not match expected output") - } + assert.Equal(t, tc.expectedResult, resultModel) }) } } @@ -354,9 +344,7 @@ func TestTeamModelMapTF(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { resultModel := project.NewTfTeamModelMap(tc.teamRolesTF) - if !assert.Equal(t, tc.expectedResult, resultModel) { - t.Errorf("created terraform model did not match expected output") - } + assert.Equal(t, tc.expectedResult, resultModel) }) } } @@ -388,9 +376,7 @@ func TestLimitModelMapTF(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { resultModel := project.NewTfLimitModelMap(tc.limitsTF) - if !assert.Equal(t, tc.expectedResult, resultModel) { - t.Errorf("created terraform model did not match expected output") - } + assert.Equal(t, tc.expectedResult, resultModel) }) } } @@ -434,9 +420,7 @@ func TestIPAddressesModelToTF(t *testing.T) { if diags.HasError() { t.Errorf("unexpected errors found: %s", diags.Errors()[0].Summary()) } - if !assert.Equal(t, tc.expectedResult, resultModel) { - t.Errorf("created terraform model did not match expected output") - } + assert.Equal(t, tc.expectedResult, resultModel) }) } }