Skip to content

Commit

Permalink
Merge pull request #18 from DrFaust92/group
Browse files Browse the repository at this point in the history
Group Resouce and Workspace data source
  • Loading branch information
DrFaust92 authored Jan 2, 2022
2 parents 3eb45c1 + 24e9243 commit 589616a
Show file tree
Hide file tree
Showing 6 changed files with 439 additions and 7 deletions.
19 changes: 12 additions & 7 deletions bitbucket/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type Client struct {
}

// Do Will just call the bitbucket api but also add auth to it and some extra headers
func (c *Client) Do(method, endpoint string, payload *bytes.Buffer) (*http.Response, error) {
func (c *Client) Do(method, endpoint string, payload *bytes.Buffer, addJsonHeader bool) (*http.Response, error) {

absoluteendpoint := BitbucketEndpoint + endpoint
log.Printf("[DEBUG] Sending request to %s %s", method, absoluteendpoint)
Expand All @@ -57,7 +57,7 @@ func (c *Client) Do(method, endpoint string, payload *bytes.Buffer) (*http.Respo

req.SetBasicAuth(c.Username, c.Password)

if payload != nil {
if payload != nil && addJsonHeader {
// Can cause bad request when putting default reviews if set.
req.Header.Add("Content-Type", "application/json")
}
Expand Down Expand Up @@ -92,25 +92,30 @@ func (c *Client) Do(method, endpoint string, payload *bytes.Buffer) (*http.Respo

// Get is just a helper method to do but with a GET verb
func (c *Client) Get(endpoint string) (*http.Response, error) {
return c.Do("GET", endpoint, nil)
return c.Do("GET", endpoint, nil, true)
}

// Post is just a helper method to do but with a POST verb
func (c *Client) Post(endpoint string, jsonpayload *bytes.Buffer) (*http.Response, error) {
return c.Do("POST", endpoint, jsonpayload)
return c.Do("POST", endpoint, jsonpayload, true)
}

// PostNonJson is just a helper method to do but with a POST verb without Json Header
func (c *Client) PostNonJson(endpoint string, jsonpayload *bytes.Buffer) (*http.Response, error) {
return c.Do("POST", endpoint, jsonpayload, false)
}

// Put is just a helper method to do but with a PUT verb
func (c *Client) Put(endpoint string, jsonpayload *bytes.Buffer) (*http.Response, error) {
return c.Do("PUT", endpoint, jsonpayload)
return c.Do("PUT", endpoint, jsonpayload, true)
}

// PutOnly is just a helper method to do but with a PUT verb and a nil body
func (c *Client) PutOnly(endpoint string) (*http.Response, error) {
return c.Do("PUT", endpoint, nil)
return c.Do("PUT", endpoint, nil, true)
}

// Delete is just a helper to Do but with a DELETE verb
func (c *Client) Delete(endpoint string) (*http.Response, error) {
return c.Do("DELETE", endpoint, nil)
return c.Do("DELETE", endpoint, nil, true)
}
85 changes: 85 additions & 0 deletions bitbucket/data_workspace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package bitbucket

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"

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

type Workspace struct {
Slug string `json:"slug"`
IsPrivate bool `json:"is_private"`
Name string `json:"name"`
UUID string `json:"uuid"`
}

func dataWorkspace() *schema.Resource {
return &schema.Resource{
Read: dataReadWorkspace,

Schema: map[string]*schema.Schema{
"workspace": {
Type: schema.TypeString,
Required: true,
},
"slug": {
Type: schema.TypeString,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"is_private": {
Type: schema.TypeBool,
Computed: true,
},
},
}
}

func dataReadWorkspace(d *schema.ResourceData, m interface{}) error {
c := m.(*Client)

workspace := d.Get("workspace").(string)
workspaceReq, err := c.Get(fmt.Sprintf("2.0/workspaces/%s", workspace))
if err != nil {
return err
}

if workspaceReq.StatusCode == http.StatusNotFound {
return fmt.Errorf("user not found")
}

if workspaceReq.StatusCode >= http.StatusInternalServerError {
return fmt.Errorf("internal server error fetching user")
}

body, readerr := ioutil.ReadAll(workspaceReq.Body)
if readerr != nil {
return readerr
}

log.Printf("[DEBUG] Workspace Response JSON: %v", string(body))

var work Workspace

decodeerr := json.Unmarshal(body, &work)
if decodeerr != nil {
return decodeerr
}

log.Printf("[DEBUG] Workspace Response Decoded: %#v", work)

d.SetId(work.UUID)
d.Set("workspace", workspace)
d.Set("name", work.Name)
d.Set("slug", work.Slug)
d.Set("is_private", work.IsPrivate)

return nil
}
37 changes: 37 additions & 0 deletions bitbucket/data_workspace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package bitbucket

import (
"fmt"
"os"
"testing"

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

func TestAccWorkspace_basic(t *testing.T) {
dataSourceName := "data.bitbucket_workspace.test"
workspace := os.Getenv("BITBUCKET_TEAM")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccBitbucketWorkspaceConfig(workspace),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "workspace", workspace),
resource.TestCheckResourceAttrSet(dataSourceName, "name"),
resource.TestCheckResourceAttrSet(dataSourceName, "slug"),
resource.TestCheckResourceAttr(dataSourceName, "is_private", "true"),
),
},
},
})
}

func testAccBitbucketWorkspaceConfig(workspace string) string {
return fmt.Sprintf(`
data "bitbucket_workspace" "test" {
workspace = %[1]q
}
`, workspace)
}
2 changes: 2 additions & 0 deletions bitbucket/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func Provider() *schema.Provider {
ConfigureFunc: providerConfigure,
ResourcesMap: map[string]*schema.Resource{
"bitbucket_hook": resourceHook(),
"bitbucket_group": resourceGroup(),
"bitbucket_default_reviewers": resourceDefaultReviewers(),
"bitbucket_repository": resourceRepository(),
"bitbucket_repository_variable": resourceRepositoryVariable(),
Expand All @@ -38,6 +39,7 @@ func Provider() *schema.Provider {
DataSourcesMap: map[string]*schema.Resource{
"bitbucket_user": dataUser(),
"bitbucket_current_user": dataCurrentUser(),
"bitbucket_workspace": dataWorkspace(),
},
}
}
Expand Down
189 changes: 189 additions & 0 deletions bitbucket/resource_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package bitbucket

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"strings"

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

type UserGroup struct {
Name string `json:"name,omitempty"`
Slug string `json:"slug,omitempty"`
}

func resourceGroup() *schema.Resource {
return &schema.Resource{
Create: resourceGroupsCreate,
Read: resourceGroupsRead,
Update: resourceGroupsUpdate,
Delete: resourceGroupsDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"workspace": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Required: true,
// ForceNew: true,
},
"slug": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func resourceGroupsCreate(d *schema.ResourceData, m interface{}) error {
client := m.(*Client)

group := expandGroup(d)
log.Printf("[DEBUG] Group Request: %#v", group)

workspace := d.Get("workspace").(string)
body := []byte(fmt.Sprintf("name=%s", group.Name))
groupReq, err := client.PostNonJson(fmt.Sprintf("1.0/groups/%s", workspace), bytes.NewBuffer(body))
if err != nil {
return err
}

body, readerr := ioutil.ReadAll(groupReq.Body)
if readerr != nil {
return readerr
}

log.Printf("[DEBUG] Group Req Response JSON: %v", string(body))

decodeerr := json.Unmarshal(body, &group)
if decodeerr != nil {
return decodeerr
}

log.Printf("[DEBUG] Group Req Response Decoded: %#v", group)

d.SetId(string(fmt.Sprintf("%s/%s", workspace, group.Slug)))

return resourceGroupsRead(d, m)
}

func resourceGroupsRead(d *schema.ResourceData, m interface{}) error {
client := m.(*Client)

workspace, slug, err := groupId(d.Id())
if err != nil {
return err
}
groupsReq, _ := client.Get(fmt.Sprintf("1.0/groups/%s/", workspace))

if groupsReq.StatusCode == 404 {
log.Printf("[WARN] Group (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if groupsReq.Body == nil {
return fmt.Errorf("error getting Group (%s): empty response", d.Id())
}

var groups []*UserGroup
body, readerr := ioutil.ReadAll(groupsReq.Body)
if readerr != nil {
return readerr
}

log.Printf("[DEBUG] Groups Response JSON: %v", string(body))

decodeerr := json.Unmarshal(body, &groups)
if decodeerr != nil {
return decodeerr
}

log.Printf("[DEBUG] Groups Response Decoded: %#v", groups)

if len(groups) == 0 {
return fmt.Errorf("error getting Group (%s): empty response", d.Id())
}

var group *UserGroup
for _, grp := range groups {
if grp.Slug == slug {
group = grp
break
}
}

log.Printf("[DEBUG] Group Response Decoded: %#v", groups)

d.Set("workspace", workspace)
d.Set("slug", group.Slug)
d.Set("name", group.Name)

return nil
}

func resourceGroupsUpdate(d *schema.ResourceData, m interface{}) error {
client := m.(*Client)

group := expandGroup(d)
log.Printf("[DEBUG] Group Request: %#v", group)
bytedata, err := json.Marshal(group)

if err != nil {
return err
}

_, err = client.Put(fmt.Sprintf("1.0/groups/%s/%s/",
d.Get("workspace").(string), d.Get("slug").(string)), bytes.NewBuffer(bytedata))

if err != nil {
return err
}

return resourceGroupsRead(d, m)
}

func resourceGroupsDelete(d *schema.ResourceData, m interface{}) error {
client := m.(*Client)

workspace, slug, err := groupId(d.Id())
if err != nil {
return err
}

_, err = client.Delete(fmt.Sprintf("1.0/groups/%s/%s", workspace, slug))

if err != nil {
return err
}

return err
}

func expandGroup(d *schema.ResourceData) *UserGroup {
key := &UserGroup{
Name: d.Get("name").(string),
}

return key
}

func groupId(id string) (string, string, error) {
parts := strings.Split(id, "/")

if len(parts) != 2 {
return "", "", fmt.Errorf("unexpected format of ID (%q), expected WORKSPACE-ID/GROUP-SLUG-ID", id)
}

return parts[0], parts[1], nil
}
Loading

0 comments on commit 589616a

Please sign in to comment.