Skip to content

Commit

Permalink
Merge pull request #158 from cgroschupp/feature/commit-file
Browse files Browse the repository at this point in the history
feat: add commit file
  • Loading branch information
DrFaust92 authored Apr 22, 2023
2 parents 0a15e43 + 2ba236e commit b910955
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 10 deletions.
25 changes: 15 additions & 10 deletions bitbucket/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,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, addJsonHeader bool) (*http.Response, error) {
func (c *Client) Do(method, endpoint string, payload *bytes.Buffer, contentType string) (*http.Response, error) {
absoluteendpoint := BitbucketEndpoint + endpoint
log.Printf("[DEBUG] Sending request to %s %s", method, absoluteendpoint)

Expand Down Expand Up @@ -77,9 +77,9 @@ func (c *Client) Do(method, endpoint string, payload *bytes.Buffer, addJsonHeade
token.SetAuthHeader(req)
}

if payload != nil && addJsonHeader {
if payload != nil && contentType != "" {
// Can cause bad request when putting default reviews if set.
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Content-Type", contentType)
}

req.Close = true
Expand Down Expand Up @@ -112,30 +112,35 @@ func (c *Client) Do(method, endpoint string, payload *bytes.Buffer, addJsonHeade

// 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, true)
return c.Do("GET", endpoint, nil, "application/json")
}

// 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, true)
return c.Do("POST", endpoint, jsonpayload, "application/json")
}

// 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)
func (c *Client) PostNonJson(endpoint string, payload *bytes.Buffer) (*http.Response, error) {
return c.Do("POST", endpoint, payload, "")
}

// PostWithContentType is just a helper method to do but with a POST verb and a provided content type
func (c *Client) PostWithContentType(endpoint, contentType string, payload *bytes.Buffer) (*http.Response, error) {
return c.Do("POST", endpoint, payload, contentType)
}

// 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, true)
return c.Do("PUT", endpoint, jsonpayload, "application/json")
}

// 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, true)
return c.Do("PUT", endpoint, nil, "application/json")
}

// 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, true)
return c.Do("DELETE", endpoint, nil, "application/json")
}
1 change: 1 addition & 0 deletions bitbucket/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func Provider() *schema.Provider {
ResourcesMap: map[string]*schema.Resource{
"bitbucket_branch_restriction": resourceBranchRestriction(),
"bitbucket_branching_model": resourceBranchingModel(),
"bitbucket_commit_file": resourceCommitFile(),
"bitbucket_default_reviewers": resourceDefaultReviewers(),
"bitbucket_deploy_key": resourceDeployKey(),
"bitbucket_deployment": resourceDeployment(),
Expand Down
160 changes: 160 additions & 0 deletions bitbucket/resource_commit_file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package bitbucket

import (
"bytes"
"context"
"fmt"
"mime/multipart"
"net/http"
"strings"

"github.com/DrFaust92/bitbucket-go-client"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceCommitFile() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceCommitFilePut,
ReadWithoutTimeout: resourceCommitFileRead,
DeleteWithoutTimeout: resourceCommitFileDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"workspace": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"repo_slug": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"content": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"filename": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"branch": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"commit_message": {
Type: schema.TypeString,
Required: true,
Description: "The SHA of the commit that modified the file",
ForceNew: true,
},
"commit_author": {
Type: schema.TypeString,
Required: true,
Description: "The SHA of the commit that modified the file",
ForceNew: true,
},
"commit_sha": {
Type: schema.TypeString,
Computed: true,
Description: "The SHA of the commit that modified the file",
},
},
}
}

func resourceCommitFilePut(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
client := m.(Clients).httpClient

repoSlug := d.Get("repo_slug").(string)
workspace := d.Get("workspace").(string)
content := d.Get("content").(string)
filename := d.Get("filename").(string)
branch := d.Get("branch").(string)
commitMessage := d.Get("commit_message").(string)
commitAuthor := d.Get("commit_author").(string)

body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, _ := writer.CreateFormFile(filename, filename)
_, err := part.Write([]byte(content))
if err != nil {
return diag.FromErr(err)
}
defer writer.Close()

messageFormField, err := writer.CreateFormField("message")
if err != nil {
return diag.FromErr(err)
}
_, err = messageFormField.Write([]byte(commitMessage))
if err != nil {
return diag.FromErr(err)
}
authorFormField, err := writer.CreateFormField("author")
if err != nil {
return diag.FromErr(err)
}
_, err = authorFormField.Write([]byte(commitAuthor))
if err != nil {
return diag.FromErr(err)
}

branchFormField, err := writer.CreateFormField("branch")
if err != nil {
return diag.FromErr(err)
}
_, err = branchFormField.Write([]byte(branch))
if err != nil {
return diag.FromErr(err)
}

response, err := client.PostWithContentType(fmt.Sprintf("2.0/repositories/%s/%s/src",
workspace,
repoSlug,
), writer.FormDataContentType(), body)

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

if response.StatusCode != http.StatusCreated {
return diag.FromErr(fmt.Errorf(""))
}

d.SetId(string(fmt.Sprintf("%s/%s/%s/%s", workspace, repoSlug, branch, filename)))

location, _ := response.Location()
splitPath := strings.Split(location.Path, "/")
d.Set("commit_sha", splitPath[len(splitPath)-1])

return resourceCommitFileRead(ctx, d, m)
}

func resourceCommitFileRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(Clients).genClient
sourceApi := c.ApiClient.SourceApi

repoSlug := d.Get("repo_slug").(string)
workspace := d.Get("workspace").(string)
filename := d.Get("filename").(string)
commit := d.Get("commit_sha").(string)

_, _, err := sourceApi.RepositoriesWorkspaceRepoSlugSrcCommitPathGet(c.AuthContext, commit, filename, repoSlug, workspace, &bitbucket.SourceApiRepositoriesWorkspaceRepoSlugSrcCommitPathGetOpts{})

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

return nil
}

func resourceCommitFileDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
return nil
}
52 changes: 52 additions & 0 deletions bitbucket/resource_commit_file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package bitbucket

import (
"fmt"
"os"
"testing"

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

func testAccBitbucketCommitFileConfig(owner, rName string) string {
return fmt.Sprintf(`
resource "bitbucket_repository" "test" {
owner = %[1]q
name = %[2]q
}
resource "bitbucket_commit_file" "test" {
filename = "README.md"
content = "abc"
repo_slug = bitbucket_repository.test.name
workspace = bitbucket_repository.test.owner
commit_author = "Unit test <[email protected]>"
branch = "main"
commit_message = "test"
}
`, owner, rName)
}

func TestAccBitbucketCommitFile_basic(t *testing.T) {
rName := acctest.RandomWithPrefix("tf-test")
owner := os.Getenv("BITBUCKET_TEAM")
resourceName := "bitbucket_commit_file.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBitbucketDefaultReviewersDestroy,
Steps: []resource.TestStep{
{
Config: testAccBitbucketCommitFileConfig(owner, rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckBitbucketDefaultReviewersExists(resourceName),
resource.TestCheckResourceAttrPair(resourceName, "repository", "bitbucket_commit_file.test", "name"),
resource.TestCheckResourceAttr(resourceName, "content", "abc"),
resource.TestCheckResourceAttr(resourceName, "branch", "main"),
),
},
},
})
}
41 changes: 41 additions & 0 deletions docs/resources/commit_file.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
layout: "bitbucket"
page_title: "Bitbucket: bitbucket_commit_file"
sidebar_current: "docs-bitbucket-resource-commit-file"
description: |-
Commit a file
---

# bitbucket\_commit\_file

Commit a file.

This resource allows you to create a commit within a Bitbucket repository.

OAuth2 Scopes: `repository:write`

## Example Usage

```hcl
resource "bitbucket_commit_file" "test" {
filename = "README.md"
content = "abc"
repo_slug = "test"
workspace = "test"
commit_author = "Test <[email protected]>"
branch = "main"
commit_message = "test"
}
```

## Argument Reference

The following arguments are supported:

* `workspace` - (Required) The workspace id.
* `repo_slug` - (Required) The repository slug.
* `filename` - (Required) The path of the file to manage.
* `content` - (Required) The file content.
* `commit_author` - (Required) Committer author to use.
* `branch` - (Required) Git branch.
* `commit_message` - (Required) The message of the commit.

0 comments on commit b910955

Please sign in to comment.