diff --git a/bitbucket/resource_project.go b/bitbucket/resource_project.go index 3fae508f..865a8a01 100644 --- a/bitbucket/resource_project.go +++ b/bitbucket/resource_project.go @@ -29,6 +29,7 @@ type ProjectLinks struct { type Link struct { Href string `json:"href,omitempty"` + Name string `json:"name,omitempty"` } func resourceProject() *schema.Resource { diff --git a/bitbucket/resource_repository.go b/bitbucket/resource_repository.go index 7aaca536..8d4b12cd 100644 --- a/bitbucket/resource_repository.go +++ b/bitbucket/resource_repository.go @@ -13,17 +13,16 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -// CloneURL is the internal struct we use to represent urls -type CloneURL struct { - Href string `json:"href,omitempty"` - Name string `json:"name,omitempty"` -} - // PipelinesEnabled is the struct we send to turn on or turn off pipelines for a repository type PipelinesEnabled struct { Enabled bool `json:"enabled"` } +type RepoLinks struct { + Clone []Link `json:"clone,omitempty"` + Avatar Link `json:"avatar,omitempty"` +} + // Repository is the struct we need to send off to the Bitbucket API to create a repository type Repository struct { SCM string `json:"scm,omitempty"` @@ -40,9 +39,7 @@ type Repository struct { Project struct { Key string `json:"key,omitempty"` } `json:"project,omitempty"` - Links struct { - Clone []CloneURL `json:"clone,omitempty"` - } `json:"links,omitempty"` + Links *RepoLinks `json:"links,omitempty"` } func resourceRepository() *schema.Resource { @@ -130,6 +127,32 @@ func resourceRepository() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "link": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "avatar": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "href": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return strings.HasPrefix(old, "https://bytebucket.org/ravatar/") + }, + }, + }, + }, + }, + }, + }, + }, }, } } @@ -148,6 +171,10 @@ func newRepositoryFromResource(d *schema.ResourceData) *Repository { Website: d.Get("website").(string), } + if v, ok := d.GetOk("link"); ok && len(v.([]interface{})) > 0 && v.([]interface{}) != nil { + repo.Links = expandRepoLinks(v.([]interface{})) + } + repo.Project.Key = d.Get("project_key").(string) return repo } @@ -310,6 +337,9 @@ func resourceRepositoryRead(d *schema.ResourceData, m interface{}) error { d.Set("clone_ssh", cloneURL.Href) } } + + d.Set("link", flattenRepoLinks(repo.Links)) + pipelinesConfigReq, err := client.Get(fmt.Sprintf("2.0/repositories/%s/%s/pipelines_config", owner, repoSlug)) // pipelines_config returns 404 if they've never been enabled for the project @@ -352,3 +382,35 @@ func resourceRepositoryDelete(d *schema.ResourceData, m interface{}) error { return err } + +func expandRepoLinks(l []interface{}) *RepoLinks { + if len(l) == 0 || l[0] == nil { + return nil + } + + tfMap, ok := l[0].(map[string]interface{}) + + if !ok { + return nil + } + + rp := &RepoLinks{} + + if v, ok := tfMap["avatar"].([]interface{}); ok && len(v) > 0 { + rp.Avatar = expandProjectLink(v) + } + + return rp +} + +func flattenRepoLinks(rp *RepoLinks) []interface{} { + if rp == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "avatar": flattenProjectLink(rp.Avatar), + } + + return []interface{}{m} +} diff --git a/bitbucket/resource_repository_test.go b/bitbucket/resource_repository_test.go index e854b6da..e33fa7c6 100644 --- a/bitbucket/resource_repository_test.go +++ b/bitbucket/resource_repository_test.go @@ -37,6 +37,39 @@ func TestAccBitbucketRepository_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "slug", rName), resource.TestCheckResourceAttr(resourceName, "is_private", "true"), resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "link.#", "1"), + resource.TestCheckResourceAttr(resourceName, "link.0.avatar.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "link.0.avatar.0.href"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBitbucketRepository_avatar(t *testing.T) { + var repo Repository + + rName := acctest.RandomWithPrefix("tf-test") + testUser := os.Getenv("BITBUCKET_TEAM") + resourceName := "bitbucket_repository.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBitbucketRepositoryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccBitbucketRepoAvatarConfig(testUser, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckBitbucketRepositoryExists(resourceName, &repo), + resource.TestCheckResourceAttr(resourceName, "link.#", "1"), + resource.TestCheckResourceAttr(resourceName, "link.0.avatar.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "link.0.avatar.0.href"), ), }, { @@ -96,6 +129,21 @@ resource "bitbucket_repository" "test" { `, testUser, rName) } +func testAccBitbucketRepoAvatarConfig(testUser, rName string) string { + return fmt.Sprintf(` +resource "bitbucket_repository" "test" { + owner = %[1]q + name = %[2]q + + link { + avatar { + href = "https://d301sr5gafysq2.cloudfront.net/dfb18959be9c/img/repo-avatars/python.png" + } + } +} +`, testUser, rName) +} + func testAccBitbucketRepoSlugConfig(testUser, rName, rName2 string) string { return fmt.Sprintf(` resource "bitbucket_repository" "test" { diff --git a/docs/resources/repository.md b/docs/resources/repository.md index 5b347747..a1fae649 100644 --- a/docs/resources/repository.md +++ b/docs/resources/repository.md @@ -55,7 +55,16 @@ The following arguments are supported: * `fork_policy` - (Optional) What the fork policy should be. Defaults to `allow_forks`. Valid values are `allow_forks`, `no_public_forks`, `no_forks`. * `description` - (Optional) What the description of the repo is. -* `pipelines_enabled` - (Optional) Turn on to enable pipelines support +* `pipelines_enabled` - (Optional) Turn on to enable pipelines support. +* `link` - (Optional) A set of links to a resource related to this object. See [Link](#link) Below. + +### Link + +* `avatar` - (Optional) A avatr link to a resource related to this object. See [Avatar](#avatar) Below. + +#### Avatar + +* `href` - (Optional) href of the avatar. ## Attributes Reference