Skip to content

Commit

Permalink
Implement resource deals (#9)
Browse files Browse the repository at this point in the history
* Basic read functionality and import

* Implemented read function for `Deals`

* Created docs for resources

* Add basic update of title only.

* Add function to handle dynamic number of attributes to be updated

* Dynamic adding resources

* Add the ability to delete
  • Loading branch information
naitmare01 authored Nov 21, 2022
1 parent 804736f commit 52a2fff
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 7 deletions.
8 changes: 5 additions & 3 deletions docs/data-sources/deals.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ description: |-
## Example Usage

```hcl
data pipedrive_deals "example"{
data "pipedrive_deals" "example"{
id = "123"
}
output "name" {
value = data.pipedrive_deals.example
}
```
<!-- schema generated by tfplugindocs -->

## Argument Reference

The following arguments are supported:
Expand All @@ -30,5 +30,7 @@ The following arguments are supported:

In addition to all argument, the following attributes are exported:

* **id** The ID of this resource.
* **id** The ID of this resource
* **title** The title of this resource
* **org_name** The organization name
* **status** Status of the deal open = Open, won = Won, lost = Lost, deleted = Deleted.
2 changes: 1 addition & 1 deletion docs/data-sources/organizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Example Usage

```hcl
data pipedrive_organizations "example"{
data "pipedrive_organizations" "example"{
id = "123"
}
Expand Down
43 changes: 43 additions & 0 deletions docs/resources/deals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Resource: pipedrive_deals

## Example Usage

```hcl
resource "pipedrive_deals" "example" {
title = "Example Deals"
org_id = "123"
}
```

## Deals example when using a tracked organization

```hcl
data "pipedrive_organizations" "example"{
id = "123"
}
resource "pipedrive_deals" "example" {
title = "Example Deals"
org_id = data.pipedrive_organizations
}
```

## Argument Reference

The following arguments are supported:

* `title` - (Required) The title of the deals that will be created in pipedrive.
* `org_id` - (Required) The organization ID of the organization where the deal will be created.
* `status` - (Optional) `open`, `won`, `lost`, `deleted`. If omitted, status will be set to open.

## Attributes Reference

In addition to all argument, the following attributes are exported:

* `id` - The ID of this resource

## Import

Pipedrive deals can be imported using the `id` eg,

`terraform import pipedrive_deals.example 123`
13 changes: 13 additions & 0 deletions pipedrive/data_source_deals.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ func dataSourceDeals() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"org_name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
Expand Down Expand Up @@ -54,7 +62,12 @@ func dataSourceDealsRead(ctx context.Context, d *schema.ResourceData, m interfac
return diag.FromErr(err)
}
title := result["data"].(map[string]interface{})["title"]
org_name := result["data"].(map[string]interface{})["org_name"]
status := result["data"].(map[string]interface{})["status"]

d.Set("title", title)
d.Set("org_name", org_name)
d.Set("status", status)

// always run
d.SetId(id)
Expand Down
36 changes: 33 additions & 3 deletions pipedrive/provider.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package pipedrive

import (
"strings"

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

Expand All @@ -15,7 +17,9 @@ func Provider() *schema.Provider {
DefaultFunc: schema.EnvDefaultFunc("PIPEDRIVE_PASSWORD", nil),
},
},
ResourcesMap: map[string]*schema.Resource{},
ResourcesMap: map[string]*schema.Resource{
"pipedrive_deals": resourceDeals(),
},
DataSourcesMap: map[string]*schema.Resource{
"pipedrive_deals": dataSourceDeals(),
"pipedrive_organizations": dataSourceOrganizations(),
Expand All @@ -30,8 +34,8 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
}

type Client struct {
apitoken string
baseurl string
apitoken string "omitempty"
baseurl string "omitempty"
}

// NewClient creates common settings
Expand All @@ -41,3 +45,29 @@ func NewClient(apitoken string) *Client {
baseurl: "https://api.pipedrive.com/v1",
}
}

func DealsBody(d *schema.ResourceData) *strings.Reader {
result := ""
resultstart := `{`
resultend := `}`

title := d.Get("title").(string)
status := d.Get("status").(string)
org_id := d.Get("org_id").(string)

if title != "" {
result += `"title": "` + title + `",`
}
if status != "" {
result += `"status": "` + status + `",`
}
if org_id != "" {
result += `"org_id": "` + org_id + `",`
}

result = strings.TrimSuffix(result, ",")
result = resultstart + result + resultend
returnresult := strings.NewReader(result)

return returnresult
}
204 changes: 204 additions & 0 deletions pipedrive/resource_deals.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package pipedrive

import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"

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

func resourceDeals() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"title": {
Type: schema.TypeString,
Required: true,
},
"org_id": {
Type: schema.TypeString,
Required: true,
},
"org_name": {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeString,
Optional: true,
Default: "open",
ValidateFunc: func(val any, key string) (warns []string, errs []error) {
value := val.(string)
expected := map[string]bool{
"open": true,
"won": true,
"lost": true,
"deleted": true,
}
if !expected[value] {
errs = append(errs, fmt.Errorf("%q is not a valid value. Please use open, won, lost, deleted", value))
}
return
},
},
},
CreateContext: resourceDealsCreate,
ReadContext: resourceDealsRead,
UpdateContext: resourceDealsUpdate,
DeleteContext: resourceDealsDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
}
}

func resourceDealsCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := &http.Client{Timeout: 10 * time.Second}
apikey := m.(*Client).apitoken
baseurl := m.(*Client).baseurl
apiurl := fmt.Sprintf("%s/deals%s", baseurl, apikey)
payload := DealsBody(d)

// Warning or errors can be collected in a slice type
var diags diag.Diagnostics

req, err := http.NewRequest("POST", apiurl, payload)
if err != nil {
return diag.FromErr(err)
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")

r, err := client.Do(req)
if err != nil {
return diag.FromErr(err)
}
defer r.Body.Close()

var result map[string]any
err = json.NewDecoder(r.Body).Decode(&result)
if err != nil {
return diag.FromErr(err)
}

id := result["data"].(map[string]interface{})["id"]
id_string := fmt.Sprintf("%v", id)
org_name := result["data"].(map[string]interface{})["org_name"]

d.Set("org_name", org_name)
d.SetId(id_string)

return diags
}

func resourceDealsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := &http.Client{Timeout: 10 * time.Second}
id := d.Id()
apikey := m.(*Client).apitoken
baseurl := m.(*Client).baseurl
apiurl := fmt.Sprintf("%s/deals/%s%s", baseurl, id, apikey)

// Warning or errors can be collected in a slice type
var diags diag.Diagnostics
req, err := http.NewRequest("GET", apiurl, nil)
if err != nil {
return diag.FromErr(err)
}

r, err := client.Do(req)
if r.StatusCode == 404 && err != nil {
d.SetId("")
diag.FromErr(err)
}
if err != nil {
return diag.FromErr(err)
}
defer r.Body.Close()

var result map[string]any
err = json.NewDecoder(r.Body).Decode(&result)
if err != nil {
return diag.FromErr(err)
}

org_name := result["data"].(map[string]interface{})["org_name"]
title := result["data"].(map[string]interface{})["title"]
status := result["data"].(map[string]interface{})["status"]

d.Set("org_name", org_name)
d.Set("title", title)
d.Set("status", status)

return diags
}

func resourceDealsUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := &http.Client{Timeout: 10 * time.Second}
id := d.Id()
apikey := m.(*Client).apitoken
baseurl := m.(*Client).baseurl
apiurl := fmt.Sprintf("%s/deals/%s%s", baseurl, id, apikey)
payload := DealsBody(d)

req, err := http.NewRequest("PUT", apiurl, payload)

if err != nil {
return diag.FromErr(err)
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")

r, err := client.Do(req)
if err != nil {
return diag.FromErr(err)
}
defer r.Body.Close()

return resourceDealsRead(ctx, d, m)

}

func resourceDealsDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := &http.Client{Timeout: 10 * time.Second}
id := d.Id()
apikey := m.(*Client).apitoken
baseurl := m.(*Client).baseurl
apiurl := fmt.Sprintf("%s/deals/%s%s", baseurl, id, apikey)

// Warning or errors can be collected in a slice type
var diags diag.Diagnostics
req, err := http.NewRequest("DELETE", apiurl, nil)

if err != nil {
return diag.FromErr(err)
}

r, err := client.Do(req)
if err != nil {
return diag.FromErr(err)
}
defer r.Body.Close()

var result map[string]any
err = json.NewDecoder(r.Body).Decode(&result)
if err != nil {
return diag.FromErr(err)
}

success := result["success"]

if success == false {
error_msg := result["error"]
error_msg_verbose := result["error_info"]
return diag.Errorf("%s %s", error_msg, error_msg_verbose)
}

// d.SetId("") is automatically called assuming delete returns no errors, but
// it is added here for explicitness.
d.SetId("")

return diags
}

0 comments on commit 52a2fff

Please sign in to comment.