Skip to content

Commit

Permalink
Merge pull request #36 from nullplatform/features/account
Browse files Browse the repository at this point in the history
feature: accounts on terraform
  • Loading branch information
sebasnallar authored Nov 6, 2024
2 parents c29f030 + 18af945 commit d97e51c
Show file tree
Hide file tree
Showing 6 changed files with 403 additions and 0 deletions.
114 changes: 114 additions & 0 deletions nullplatform/account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package nullplatform

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)

const ACCOUNT_PATH = "/account"

type Account struct {
Id int `json:"id,omitempty"`
Name string `json:"name,omitempty"`
OrganizationId string `json:"organization_id,omitempty"`
RepositoryPrefix string `json:"repository_prefix,omitempty"`
RepositoryProvider string `json:"repository_provider,omitempty"`
Slug string `json:"slug,omitempty"`
Status string `json:"status,omitempty"`
}

func (c *NullClient) CreateAccount(account *Account) (*Account, error) {
var buf bytes.Buffer
err := json.NewEncoder(&buf).Encode(*account)

if err != nil {
return nil, err
}

res, err := c.MakeRequest("POST", ACCOUNT_PATH, &buf)
if err != nil {
return nil, err
}
defer res.Body.Close()

if res.StatusCode != http.StatusOK {
var nErr NullErrors
if err := json.NewDecoder(res.Body).Decode(&nErr); err != nil {
return nil, fmt.Errorf("failed to decode null error response: %w", err)
}
return nil, fmt.Errorf("error creating account resource, got status code: %d, message: %s", res.StatusCode, nErr.Message)
}

accountRes := &Account{}
derr := json.NewDecoder(res.Body).Decode(accountRes)

if derr != nil {
return nil, derr
}

return accountRes, nil
}

func (c *NullClient) PatchAccount(accountId string, account *Account) error {
path := fmt.Sprintf("%s/%s", ACCOUNT_PATH, accountId)

var buf bytes.Buffer
err := json.NewEncoder(&buf).Encode(*account)

if err != nil {
return err
}

res, err := c.MakeRequest("PATCH", path, &buf)
if err != nil {
return err
}
defer res.Body.Close()

if (res.StatusCode != http.StatusOK) && (res.StatusCode != http.StatusNoContent) {
var nErr NullErrors
if err := json.NewDecoder(res.Body).Decode(&nErr); err != nil {
return fmt.Errorf("failed to decode null error response: %w", err)
}
return fmt.Errorf("error updating account resource, got status code: %d, message: %s", res.StatusCode, nErr.Message)
}

return nil
}

func (c *NullClient) GetAccount(accountId string) (*Account, error) {
path := fmt.Sprintf("%s/%s", ACCOUNT_PATH, accountId)

res, err := c.MakeRequest("GET", path, nil)
if err != nil {
return nil, err
}
defer res.Body.Close()

account := &Account{}
derr := json.NewDecoder(res.Body).Decode(account)

if derr != nil {
return nil, derr
}

if account.Status == "deleted" {
return account, fmt.Errorf("error getting account resource, the status is %s", account.Status)
}

if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("error getting account resource, got %d for %s", res.StatusCode, accountId)
}

return account, nil
}

func (c *NullClient) DeleteAccount(accountId string) error {
account := &Account{
Status: "inactive",
}

return c.PatchAccount(accountId, account)
}
5 changes: 5 additions & 0 deletions nullplatform/null_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ type NullOps interface {
CreateDimensionValue(dv *DimensionValue) (*DimensionValue, error)
GetDimensionValue(dimensionID, valueID int) (*DimensionValue, error)
DeleteDimensionValue(dimensionID, valueID int) error

CreateAccount(account *Account) (*Account, error)
GetAccount(accountId string) (*Account, error)
PatchAccount(accountId string, account *Account) error
DeleteAccount(accountId string) error
}

func (c *NullClient) MakeRequest(method, path string, body *bytes.Buffer) (*http.Response, error) {
Expand Down
1 change: 1 addition & 0 deletions nullplatform/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func Provider() *schema.Provider {
},
},
ResourcesMap: map[string]*schema.Resource{
"nullplatform_account": resourceAccount(),
"nullplatform_scope": resourceScope(),
"nullplatform_service": resourceService(),
"nullplatform_link": resourceLink(),
Expand Down
3 changes: 3 additions & 0 deletions nullplatform/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func testAccPreCheck(t *testing.T) {

func TestProvider_HasChildResources(t *testing.T) {
expectedResources := []string{
"nullplatform_account",
"nullplatform_scope",
"nullplatform_service",
"nullplatform_link",
Expand All @@ -40,6 +41,8 @@ func TestProvider_HasChildResources(t *testing.T) {
"nullplatform_notification_channel",
"nullplatform_runtime_configuration",
"nullplatform_provider_config",
"nullplatform_dimension",
"nullplatform_dimension_value",
}

resources := nullplatform.Provider().ResourcesMap
Expand Down
160 changes: 160 additions & 0 deletions nullplatform/resource_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package nullplatform

import (
"context"
"fmt"
"strconv"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceAccount() *schema.Resource {
return &schema.Resource{
Description: "The account resource allows you to configure a nullplatform account",

Create: AccountCreate,
Read: AccountRead,
Update: AccountUpdate,
Delete: AccountDelete,

Importer: &schema.ResourceImporter{
StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
d.Set("id", d.Id())
return []*schema.ResourceData{d}, nil
},
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "The name of the account",
},
"organization_id": {
Type: schema.TypeString,
Computed: true,
ForceNew: true,
Description: "The ID of the organization this account belongs to (computed from authentication token)",
},
"repository_prefix": {
Type: schema.TypeString,
Required: true,
Description: "The prefix used for repositories in this account",
},
"repository_provider": {
Type: schema.TypeString,
Required: true,
Description: "The repository provider for this account",
},
"slug": {
Type: schema.TypeString,
Required: true,
Description: "The unique slug identifier for the account",
},
},
}
}

func AccountCreate(d *schema.ResourceData, m any) error {
nullOps := m.(NullOps)
client := nullOps.(*NullClient)

organizationID, err := client.GetOrganizationIDFromToken()
if err != nil {
return fmt.Errorf("error getting organization ID from token: %w", err)
}

newAccount := &Account{
Name: d.Get("name").(string),
OrganizationId: organizationID,
RepositoryPrefix: d.Get("repository_prefix").(string),
RepositoryProvider: d.Get("repository_provider").(string),
Slug: d.Get("slug").(string),
}

account, err := nullOps.CreateAccount(newAccount)
if err != nil {
return err
}

d.SetId(strconv.Itoa(account.Id))

// Set the computed organization_id in the state
if err := d.Set("organization_id", account.OrganizationId); err != nil {
return fmt.Errorf("error setting organization_id: %w", err)
}

return AccountRead(d, m)
}

func AccountRead(d *schema.ResourceData, m any) error {
nullOps := m.(NullOps)
accountId := d.Id()

account, err := nullOps.GetAccount(accountId)
if err != nil {
if account.Status == "inactive" {
d.SetId("")
return nil
}
return err
}

if err := d.Set("name", account.Name); err != nil {
return err
}
if err := d.Set("organization_id", account.OrganizationId); err != nil {
return err
}
if err := d.Set("repository_prefix", account.RepositoryPrefix); err != nil {
return err
}
if err := d.Set("repository_provider", account.RepositoryProvider); err != nil {
return err
}
if err := d.Set("slug", account.Slug); err != nil {
return err
}

return nil
}

func AccountUpdate(d *schema.ResourceData, m any) error {
nullOps := m.(NullOps)
accountId := d.Id()

account := &Account{}

if d.HasChange("name") {
account.Name = d.Get("name").(string)
}
if d.HasChange("repository_prefix") {
account.RepositoryPrefix = d.Get("repository_prefix").(string)
}
if d.HasChange("repository_provider") {
account.RepositoryProvider = d.Get("repository_provider").(string)
}
if d.HasChange("slug") {
account.Slug = d.Get("slug").(string)
}

err := nullOps.PatchAccount(accountId, account)
if err != nil {
return err
}

return AccountRead(d, m)
}

func AccountDelete(d *schema.ResourceData, m any) error {
nullOps := m.(NullOps)
accountId := d.Id()

err := nullOps.DeleteAccount(accountId)
if err != nil {
return err
}

d.SetId("")
return nil
}
Loading

0 comments on commit d97e51c

Please sign in to comment.