Skip to content

Commit

Permalink
Fix readonly fields issue (#319)
Browse files Browse the repository at this point in the history
* Fix readonly fields issue

* tests and changelog
  • Loading branch information
beevital authored Jun 11, 2024
1 parent 9badcb8 commit 56387cf
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 3 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased](https://github.com/fivetran/terraform-provider-fivetran/compare/v1.1.24...HEAD)
## [Unreleased](https://github.com/fivetran/terraform-provider-fivetran/compare/v1.1.25...HEAD)

## [1.1.25](https://github.com/fivetran/terraform-provider-fivetran/compare/v1.1.24...v1.1.25)

## Fixed

- Fix issue when provider tries to set readonly config fields to `null` on update for connector/destination.

## [1.1.24](https://github.com/fivetran/terraform-provider-fivetran/compare/v1.1.23...v1.1.24)

Expand Down
2 changes: 1 addition & 1 deletion fivetran/framework/core/model/connector_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func PrepareConfigAuthPatch(state, plan map[string]interface{}, service string,
for k := range state {
if _, ok := plan[k]; !ok {
if f, ok := allFields[k]; ok {
if f.Nullable || f.FieldValueType == common.ObjectList || f.FieldValueType == common.StringList {
if (f.Nullable || f.FieldValueType == common.ObjectList || f.FieldValueType == common.StringList) && !f.Readonly {
// If the field is not represented in plan (deleted from config)
// And the field is nullable - it should be set to null explicitly
result[k] = nil
Expand Down
2 changes: 1 addition & 1 deletion fivetran/framework/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
)

const Version = "1.1.24" // Current provider version
const Version = "1.1.25" // Current provider version

type fivetranProvider struct {
mockClient httputils.HttpClient
Expand Down
132 changes: 132 additions & 0 deletions fivetran/framework/resources/destination_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,138 @@ import (
"github.com/hashicorp/terraform-plugin-testing/terraform"
)

func TestReadonlyFieldSetMock(t *testing.T) {
var testDestinationData map[string]interface{}
var destinationMappingDeleteHandler *mock.Handler
step1 := resource.TestStep{
Config: `
resource "fivetran_destination" "mydestination" {
provider = fivetran-provider
group_id = "group_id"
service = "new_s3_datalake"
time_zone_offset = "0"
region = "GCP_US_EAST4"
run_setup_tests = "false"
config {
bucket = "bucket"
fivetran_role_arn = "fivetran_role_arn"
region = "region"
}
}`,
}

step2 := resource.TestStep{
Config: `
resource "fivetran_destination" "mydestination" {
provider = fivetran-provider
group_id = "group_id"
service = "new_s3_datalake"
time_zone_offset = "0"
region = "GCP_US_EAST4"
run_setup_tests = "false"
config {
bucket = "bucket1"
fivetran_role_arn = "fivetran_role_arn"
region = "region"
}
}`,
}

resource.Test(
t,
resource.TestCase{
PreCheck: func() {
tfmock.MockClient().Reset()

tfmock.MockClient().When(http.MethodGet, "/v1/destinations/group_id").ThenCall(
func(req *http.Request) (*http.Response, error) {
return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", testDestinationData), nil
},
)

tfmock.MockClient().When(http.MethodPost, "/v1/destinations").ThenCall(
func(req *http.Request) (*http.Response, error) {
testDestinationData = tfmock.CreateMapFromJsonString(t, `
{
"id":"group_id",
"group_id":"group_id",
"service":"new_s3_datalake",
"region":"GCP_US_EAST4",
"time_zone_offset":"0",
"setup_status":"connected",
"daylight_saving_time_enabled":true,
"config":{
"external_id": "group_id",
"bucket": "bucket",
"fivetran_role_arn": "fivetran_role_arn",
"region": "region"
}
}
`)
return tfmock.FivetranSuccessResponse(t, req, http.StatusCreated, "Success", testDestinationData), nil
},
)

tfmock.MockClient().When(http.MethodPatch, "/v1/destinations/group_id").ThenCall(
func(req *http.Request) (*http.Response, error) {
body := tfmock.RequestBodyToJson(t, req)

tfmock.AssertKeyExists(t, body, "config")

config := body["config"].(map[string]interface{})
tfmock.AssertKeyExistsAndHasValue(t, config, "bucket", "bucket1")

tfmock.AssertKeyDoesNotExist(t, config, "external_id")

testDestinationData = tfmock.CreateMapFromJsonString(t, `
{
"id":"group_id",
"group_id":"group_id",
"service":"new_s3_datalake",
"region":"GCP_US_EAST4",
"time_zone_offset":"0",
"setup_status":"connected",
"daylight_saving_time_enabled":true,
"config":{
"external_id": "",
"bucket": "bucket1",
"fivetran_role_arn": "fivetran_role_arn",
"region": "region"
}
}
`)
return tfmock.FivetranSuccessResponse(t, req, http.StatusOK, "Success", testDestinationData), nil
},
)

destinationMappingDeleteHandler = tfmock.MockClient().When(http.MethodDelete, "/v1/destinations/group_id").ThenCall(
func(req *http.Request) (*http.Response, error) {
testDestinationData = nil
response := tfmock.FivetranSuccessResponse(t, req, 200,
"Destination with id 'destionation_id' has been deleted", nil)
return response, nil
},
)
},
ProtoV6ProviderFactories: tfmock.ProtoV6ProviderFactories,
CheckDestroy: func(s *terraform.State) error {
tfmock.AssertEqual(t, destinationMappingDeleteHandler.Interactions, 1)
tfmock.AssertEmpty(t, testDestinationData)
return nil
},

Steps: []resource.TestStep{
step1,
step2,
},
},
)
}

func TestResourceDestinationMappingMock(t *testing.T) {
var testDestinationData map[string]interface{}
var destinationMappingGetHandler *mock.Handler
Expand Down

0 comments on commit 56387cf

Please sign in to comment.