Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions internal/services/account_token/custom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package account_token

import (
"encoding/json"

"github.com/cloudflare/terraform-provider-cloudflare/internal/apijson"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func MarshalCustom(m AccountTokenModel) (data []byte, err error) {
if data, err = apijson.MarshalRoot(m); err != nil {
return
}
var base map[string]json.RawMessage
if err := json.Unmarshal(data, &base); err != nil {
return nil, err
}

// for each policy, marshal the resources string as raw json
policyJsons := make([]json.RawMessage, len(*m.Policies))
for i, policy := range *m.Policies {
policyData, err := apijson.MarshalRoot(policy)
if err != nil {
return nil, err
}
var policyBase map[string]json.RawMessage
if err := json.Unmarshal(policyData, &policyBase); err != nil {
return nil, err
}
resources := json.RawMessage(policy.Resources.ValueString())
policyBase["resources"] = resources
policyJsons[i], err = json.Marshal(policyBase)
if err != nil {
return nil, err
}
}
base["policies"], err = json.Marshal(policyJsons)
if err != nil {
return nil, err
}

return json.Marshal(base)
}

func UnmarshalCustom(data []byte, model *AccountTokenResultEnvelope) (err error) {
if err = apijson.Unmarshal(data, model); err != nil {
return
}

// pull out the raw JSON values for each policy resource and map to the model
var base map[string]json.RawMessage
if err := json.Unmarshal(data, &base); err != nil {
return err
}
var result map[string]json.RawMessage
if err := json.Unmarshal(base["result"], &result); err != nil {
return err
}
var policyJsons []json.RawMessage
if err := json.Unmarshal(result["policies"], &policyJsons); err != nil {
return err
}
for i, policyJson := range policyJsons {
var policy map[string]json.RawMessage
if err := json.Unmarshal(policyJson, &policy); err != nil {
return err
}
(*model.Result.Policies)[i].Resources = types.StringValue(string(policy["resources"]))
}
return
}

func UnmarshalComputedCustom(data []byte, model *AccountTokenResultEnvelope) (err error) {
if err = apijson.UnmarshalComputed(data, model); err != nil {
return
}
forPoliciesOnly := AccountTokenResultEnvelope{}
err = UnmarshalCustom(data, &forPoliciesOnly)
if err != nil {
return err
}
model.Result.Policies = forPoliciesOnly.Result.Policies
return nil
}
154 changes: 154 additions & 0 deletions internal/services/account_token/custom_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package account_token

import (
"encoding/json"
"testing"

"github.com/hashicorp/terraform-plugin-framework/types"
)

func TestMarshalCustom(t *testing.T) {
permGroups := []*AccountTokenPoliciesPermissionGroupsModel{
{
ID: types.StringValue("permgroup1"),
},
}
policies := []*AccountTokenPoliciesModel{
{
Effect: types.StringValue("effect"),
PermissionGroups: &permGroups,
Resources: types.StringValue("{\"com.cloudflare.api.account.123\": \"*\"}"),
},
}
tt := AccountTokenModel{
ID: types.StringValue("id"),
Name: types.StringValue("name"),
Policies: &policies,
Condition: nil,
}
data, err := MarshalCustom(tt)
if err != nil {
t.Fatal(err)
}
expectedData := json.RawMessage("{\"name\":\"name\",\"policies\":[{\"effect\":\"effect\",\"permission_groups\":[{\"id\":\"permgroup1\"}],\"resources\":{\"com.cloudflare.api.account.123\":\"*\"}}]}")
if string(data) != string(expectedData) {
t.Fatalf("expected %s, got %s", string(expectedData), string(data))
}
}

func TestMarshalCustom_BadJson(t *testing.T) {
permGroups := []*AccountTokenPoliciesPermissionGroupsModel{
{
ID: types.StringValue("permgroup1"),
},
}
policies := []*AccountTokenPoliciesModel{
{
Effect: types.StringValue("effect"),
PermissionGroups: &permGroups,
Resources: types.StringValue("{\"com.cloudflare.api.account.123\": \"*\""),
},
}
tt := AccountTokenModel{
ID: types.StringValue("id"),
Name: types.StringValue("name"),
Policies: &policies,
Condition: nil,
}
_, err := MarshalCustom(tt)
if err == nil {
t.Fatal("expected error")
}
}

func TestUnmarshalCustom(t *testing.T) {
data := json.RawMessage(`{
"result":{
"name":"name",
"policies":[
{
"effect":"effect",
"permission_groups":[{"id":"permgroup1"}],
"resources":{"com.cloudflare.api.account.123":"*"}
},
{
"effect":"effect",
"permission_groups":[{"id":"permgroup1"}],
"resources":{"com.cloudflare.api.account.123":{"com.cloudflare.api.account.zone.*":"*"}}
}
]
}
}`)
env := AccountTokenResultEnvelope{}
err := UnmarshalCustom(data, &env)
if err != nil {
t.Fatal(err)
}
if env.Result.Name != types.StringValue("name") {
t.Fatalf("expected %s, got %s", "name", env.Result.Name)
}
policies := env.Result.Policies
if (*policies)[0].Resources != types.StringValue("{\"com.cloudflare.api.account.123\":\"*\"}") {
t.Fatalf("expected %s, got %s", "{\"com.cloudflare.api.account.123\":\"*\"}", (*policies)[0].Resources)
}
if (*policies)[1].Resources != types.StringValue("{\"com.cloudflare.api.account.123\":{\"com.cloudflare.api.account.zone.*\":\"*\"}}") {
t.Fatalf("expected %s, got %s", "{\"com.cloudflare.api.account.123\":{\"com.cloudflare.api.account.zone.*\":\"*\"}}", (*policies)[0].Resources)
}
}

func TestUnmarshalComputedCustom(t *testing.T) {
data := json.RawMessage(`{
"result":{
"name":"name",
"policies":[
{
"effect":"effect",
"permission_groups":[{"id":"permgroup1"}],
"resources":{"com.cloudflare.api.account.123":"*"}
},
{
"effect":"effect",
"permission_groups":[{"id":"permgroup1"}],
"resources":{"com.cloudflare.api.account.123":{"com.cloudflare.api.account.zone.*":"*"}}
}
]
}
}`)
permGroups1 := []*AccountTokenPoliciesPermissionGroupsModel{
{
ID: types.StringValue("permgroup1"),
},
}
// Policy order in client-side model is not the same as the server-side data
// model.
policies := []*AccountTokenPoliciesModel{
{
Effect: types.StringValue("effect"),
PermissionGroups: &permGroups1,
Resources: types.StringValue("{\"com.cloudflare.api.account.123\":{\"com.cloudflare.api.account.zone.*\":\"*\"}}"),
},
{
Effect: types.StringValue("effect"),
PermissionGroups: &permGroups1,
Resources: types.StringValue("{\"com.cloudflare.api.account.123\": \"*\"}"),
},
}
env := AccountTokenResultEnvelope{
Result: AccountTokenModel{
Name: types.StringValue("name"),
Policies: &policies,
},
}
err := UnmarshalComputedCustom(data, &env)
if err != nil {
t.Fatal(err)
}
// The order of policies should be the same as the server-side data model
foundPolicies := env.Result.Policies
if (*foundPolicies)[0].Resources != types.StringValue("{\"com.cloudflare.api.account.123\":\"*\"}") {
t.Fatalf("expected %s, got %s", "{\"com.cloudflare.api.account.123\":\"*\"}", (*foundPolicies)[0].Resources)
}
if (*foundPolicies)[1].Resources != types.StringValue("{\"com.cloudflare.api.account.123\":{\"com.cloudflare.api.account.zone.*\":\"*\"}}") {
t.Fatalf("expected %s, got %s", "{\"com.cloudflare.api.account.123\":{\"com.cloudflare.api.account.zone.*\":\"*\"}}", (*foundPolicies)[0].Resources)
}
}
13 changes: 4 additions & 9 deletions internal/services/account_token/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
package account_token

import (
"github.com/cloudflare/terraform-provider-cloudflare/internal/apijson"
"github.com/cloudflare/terraform-provider-cloudflare/internal/customfield"
"github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
"github.com/hashicorp/terraform-plugin-framework/types"
)
Expand All @@ -29,24 +27,21 @@ type AccountTokenModel struct {
}

func (m AccountTokenModel) MarshalJSON() (data []byte, err error) {
return apijson.MarshalRoot(m)
return MarshalCustom(m)
}

func (m AccountTokenModel) MarshalJSONForUpdate(state AccountTokenModel) (data []byte, err error) {
return apijson.MarshalForUpdate(m, state)
return MarshalCustom(m)
}

type AccountTokenPoliciesModel struct {
ID types.String `tfsdk:"id" json:"id,computed,force_encode,encode_state_for_unknown"`
Effect types.String `tfsdk:"effect" json:"effect,required"`
PermissionGroups *[]*AccountTokenPoliciesPermissionGroupsModel `tfsdk:"permission_groups" json:"permission_groups,required"`
Resources *map[string]types.String `tfsdk:"resources" json:"resources,required"`
Resources types.String `tfsdk:"resources" json:"resources,required"`
}

type AccountTokenPoliciesPermissionGroupsModel struct {
ID types.String `tfsdk:"id" json:"id,required"`
Meta customfield.NestedObject[AccountTokenPoliciesPermissionGroupsMetaModel] `tfsdk:"meta" json:"meta,computed_optional"`
Name types.String `tfsdk:"name" json:"name,computed"`
ID types.String `tfsdk:"id" json:"id,required"`
}

type AccountTokenPoliciesPermissionGroupsMetaModel struct {
Expand Down
9 changes: 4 additions & 5 deletions internal/services/account_token/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/cloudflare/cloudflare-go/v6"
"github.com/cloudflare/cloudflare-go/v6/accounts"
"github.com/cloudflare/cloudflare-go/v6/option"
"github.com/cloudflare/terraform-provider-cloudflare/internal/apijson"
"github.com/cloudflare/terraform-provider-cloudflare/internal/importpath"
"github.com/cloudflare/terraform-provider-cloudflare/internal/logging"
"github.com/hashicorp/terraform-plugin-framework/resource"
Expand Down Expand Up @@ -85,7 +84,7 @@ func (r *AccountTokenResource) Create(ctx context.Context, req resource.CreateRe
return
}
bytes, _ := io.ReadAll(res.Body)
err = apijson.UnmarshalComputed(bytes, &env)
err = UnmarshalComputedCustom(bytes, &env)
if err != nil {
resp.Diagnostics.AddError("failed to deserialize http request", err.Error())
return
Expand Down Expand Up @@ -135,7 +134,7 @@ func (r *AccountTokenResource) Update(ctx context.Context, req resource.UpdateRe
return
}
bytes, _ := io.ReadAll(res.Body)
err = apijson.UnmarshalComputed(bytes, &env)
err = UnmarshalComputedCustom(bytes, &env)
if err != nil {
resp.Diagnostics.AddError("failed to deserialize http request", err.Error())
return
Expand Down Expand Up @@ -177,7 +176,7 @@ func (r *AccountTokenResource) Read(ctx context.Context, req resource.ReadReques
return
}
bytes, _ := io.ReadAll(res.Body)
err = apijson.Unmarshal(bytes, &env)
err = UnmarshalCustom(bytes, &env)
if err != nil {
resp.Diagnostics.AddError("failed to deserialize http request", err.Error())
return
Expand Down Expand Up @@ -248,7 +247,7 @@ func (r *AccountTokenResource) ImportState(ctx context.Context, req resource.Imp
return
}
bytes, _ := io.ReadAll(res.Body)
err = apijson.Unmarshal(bytes, &env)
err = UnmarshalCustom(bytes, &env)
if err != nil {
resp.Diagnostics.AddError("failed to deserialize http request", err.Error())
return
Expand Down
Loading