Skip to content

Commit

Permalink
make nullable wrapper type-safe, at the cost of verbousity
Browse files Browse the repository at this point in the history
  • Loading branch information
SwiftEngineer committed Jun 10, 2022
1 parent dd08b27 commit cf06e02
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 17 deletions.
14 changes: 7 additions & 7 deletions admin_organization.go
Expand Up @@ -59,13 +59,13 @@ type AdminOrganization struct {
// AdminOrganizationUpdateOptions represents the admin options for updating an organization.
// https://www.terraform.io/docs/cloud/api/admin/organizations.html#request-body
type AdminOrganizationUpdateOptions struct {
AccessBetaTools *bool `jsonapi:"attr,access-beta-tools,omitempty"`
GlobalModuleSharing *bool `jsonapi:"attr,global-module-sharing,omitempty"`
IsDisabled *bool `jsonapi:"attr,is-disabled,omitempty"`
TerraformBuildWorkerApplyTimeout *string `jsonapi:"attr,terraform-build-worker-apply-timeout,omitempty"`
TerraformBuildWorkerPlanTimeout *string `jsonapi:"attr,terraform-build-worker-plan-timeout,omitempty"`
TerraformWorkerSudoEnabled bool `jsonapi:"attr,terraform-worker-sudo-enabled,omitempty"`
WorkspaceLimit *Nullable `jsonapi:"attr,workspace-limit,omitempty"`
AccessBetaTools *bool `jsonapi:"attr,access-beta-tools,omitempty"`
GlobalModuleSharing *bool `jsonapi:"attr,global-module-sharing,omitempty"`
IsDisabled *bool `jsonapi:"attr,is-disabled,omitempty"`
TerraformBuildWorkerApplyTimeout *string `jsonapi:"attr,terraform-build-worker-apply-timeout,omitempty"`
TerraformBuildWorkerPlanTimeout *string `jsonapi:"attr,terraform-build-worker-plan-timeout,omitempty"`
TerraformWorkerSudoEnabled bool `jsonapi:"attr,terraform-worker-sudo-enabled,omitempty"`
WorkspaceLimit *NullableInt `jsonapi:"attr,workspace-limit,omitempty"`
}

// AdminOrganizationList represents a list of organizations via Admin API.
Expand Down
4 changes: 2 additions & 2 deletions admin_organization_integration_test.go
Expand Up @@ -252,7 +252,7 @@ func TestAdminOrganizations_Update(t *testing.T) {
opts = AdminOrganizationUpdateOptions{
GlobalModuleSharing: &globalModuleSharing,
IsDisabled: &isDisabled,
WorkspaceLimit: &Nullable{&workspaceLimit},
WorkspaceLimit: &NullableInt{&workspaceLimit},
}

adminOrg, err = client.Admin.Organizations.Update(ctx, org.Name, opts)
Expand All @@ -268,7 +268,7 @@ func TestAdminOrganizations_Update(t *testing.T) {
opts = AdminOrganizationUpdateOptions{
GlobalModuleSharing: &globalModuleSharing,
IsDisabled: &isDisabled,
WorkspaceLimit: &Nullable{nil},
WorkspaceLimit: &NullableInt{nil},
}

adminOrg, err = client.Admin.Organizations.Update(ctx, org.Name, opts)
Expand Down
20 changes: 20 additions & 0 deletions nullable.go
Expand Up @@ -7,6 +7,8 @@ import "encoding/json"
//
// This is particularly useful for attributes that are set to null if provided as null in a json request body.
// Conversely, if they are omitted from the request body, they will retain their existing value.
//
// This struct should use generics, but this project doesn't support them yet :(
type Nullable struct {
value interface{}
}
Expand All @@ -17,3 +19,21 @@ func (i Nullable) MarshalJSON() ([]byte, error) {
}
return json.Marshal(nil)
}

// int version of Nullable
type NullableInt struct {
value *int
}

func (i NullableInt) MarshalJSON() ([]byte, error) {
return Nullable{i.value}.MarshalJSON()
}

// string version of Nullable
type NullableString struct {
value *string
}

func (i NullableString) MarshalJSON() ([]byte, error) {
return Nullable{i.value}.MarshalJSON()
}
22 changes: 14 additions & 8 deletions nullable_test.go
Expand Up @@ -11,16 +11,22 @@ import (

func TestNullable_MarshalJSON(t *testing.T) {
type fields struct {
WithValue *Nullable `jsonapi:"attr,with-value,omitempty"`
WithZeroValue *Nullable `jsonapi:"attr,with-zero-value,omitempty"`
WithNilValue *Nullable `jsonapi:"attr,with-nil-value,omitempty"`
Unset *Nullable `jsonapi:"attr,unset,omitempty"`
IntWithValue *NullableInt `jsonapi:"attr,i-with-value,omitempty"`
IntWithZeroValue *NullableInt `jsonapi:"attr,i-with-zero-value,omitempty"`
IntWithNilValue *NullableInt `jsonapi:"attr,i-with-nil-value,omitempty"`
StringWithValue *NullableString `jsonapi:"attr,s-with-value,omitempty"`
StringWithEmptyValue *NullableString `jsonapi:"attr,s-with-empty-value,omitempty"`
StringWithNilValue *NullableString `jsonapi:"attr,s-with-nil-value,omitempty"`
Unset *Nullable `jsonapi:"attr,unset,omitempty"`
}

var instance = fields{
WithValue: &Nullable{42},
WithZeroValue: &Nullable{0},
WithNilValue: &Nullable{nil},
IntWithValue: &NullableInt{Int(42)},
IntWithZeroValue: &NullableInt{Int(0)},
IntWithNilValue: &NullableInt{nil},
StringWithValue: &NullableString{String("hello")},
StringWithEmptyValue: &NullableString{String("")},
StringWithNilValue: &NullableString{nil},
}

reqBody, err := serializeRequestBody(&instance)
Expand All @@ -29,5 +35,5 @@ func TestNullable_MarshalJSON(t *testing.T) {
bodyBytes, err := req.BodyBytes()
assert.NoError(t, err)
bodyString := strings.Trim(string(bodyBytes), "\n")
assert.Equal(t, `{"data":{"type":"","attributes":{"with-nil-value":null,"with-value":42,"with-zero-value":0}}}`, bodyString)
assert.Equal(t, `{"data":{"type":"","attributes":{"i-with-nil-value":null,"i-with-value":42,"i-with-zero-value":0,"s-with-empty-value":"","s-with-nil-value":null,"s-with-value":"hello"}}}`, bodyString)
}

0 comments on commit cf06e02

Please sign in to comment.