From ad5bf8b42c8f15ccc5cf44e51ef15d1be1994c09 Mon Sep 17 00:00:00 2001 From: Ard-Jan Aalberts Date: Wed, 6 May 2020 17:41:29 +0200 Subject: [PATCH] Added support for entries. Created the entries resource. The entries can be created, updated and deleted. The archiving and publishing is also added into the resource, keep in mind that a resource can only be deleted when unpublished. --- contentful/provider.go | 1 + .../resource_contentful_contenttype_test.go | 14 +- contentful/resource_contentful_entry.go | 206 +++++++++++++++++ contentful/resource_contentful_entry_test.go | 207 ++++++++++++++++++ go.mod | 2 +- 5 files changed, 422 insertions(+), 8 deletions(-) create mode 100644 contentful/resource_contentful_entry.go create mode 100644 contentful/resource_contentful_entry_test.go diff --git a/contentful/provider.go b/contentful/provider.go index 55e5f66..19d95b9 100644 --- a/contentful/provider.go +++ b/contentful/provider.go @@ -30,6 +30,7 @@ func Provider() terraform.ResourceProvider { "contentful_webhook": resourceContentfulWebhook(), "contentful_locale": resourceContentfulLocale(), "contentful_environment": resourceContentfulEnvironment(), + "contentful_entry": resourceContentfulEntry(), }, ConfigureFunc: providerConfigure, } diff --git a/contentful/resource_contentful_contenttype_test.go b/contentful/resource_contentful_contenttype_test.go index 298624a..68d30cc 100644 --- a/contentful/resource_contentful_contenttype_test.go +++ b/contentful/resource_contentful_contenttype_test.go @@ -18,17 +18,17 @@ func TestAccContentfulContentType_Basic(t *testing.T) { { Config: testAccContentfulContentTypeConfig, Check: resource.TestCheckResourceAttr( - "contentful_contenttype.mycontenttype", "name", "TF Acc Test CT 1"), + "contentful_contenttype.mycontenttype", "name", "tf_test1"), }, { Config: testAccContentfulContentTypeUpdateConfig, Check: resource.TestCheckResourceAttr( - "contentful_contenttype.mycontenttype", "name", "TF Acc Test CT name change"), + "contentful_contenttype.mycontenttype", "name", "tf_test1"), }, { Config: testAccContentfulContentTypeLinkConfig, Check: resource.TestCheckResourceAttr( - "contentful_contenttype.mylinked_contenttype", "name", "TF Acc Test Linked CT"), + "contentful_contenttype.mylinked_contenttype", "name", "tf_linked"), }, }, }) @@ -91,7 +91,7 @@ func testAccCheckContentfulContentTypeDestroy(s *terraform.State) (err error) { var testAccContentfulContentTypeConfig = ` resource "contentful_contenttype" "mycontenttype" { space_id = "` + spaceID + `" - name = "TF Acc Test CT 1" + name = "tf_test1" description = "Terraform Acc Test Content Type" display_field = "field1" field { @@ -118,7 +118,7 @@ resource "contentful_contenttype" "mycontenttype" { var testAccContentfulContentTypeUpdateConfig = ` resource "contentful_contenttype" "mycontenttype" { space_id = "` + spaceID + `" - name = "TF Acc Test CT name change" + name = "tf_test1" description = "Terraform Acc Test Content Type description change" display_field = "field1" field { @@ -145,7 +145,7 @@ resource "contentful_contenttype" "mycontenttype" { var testAccContentfulContentTypeLinkConfig = ` resource "contentful_contenttype" "mycontenttype" { space_id = "` + spaceID + `" - name = "TF Acc Test CT name change" + name = "tf_test1" description = "Terraform Acc Test Content Type description change" display_field = "field1" field { @@ -170,7 +170,7 @@ resource "contentful_contenttype" "mycontenttype" { resource "contentful_contenttype" "mylinked_contenttype" { space_id = "` + spaceID + `" - name = "TF Acc Test Linked CT" + name = "tf_linked" description = "Terraform Acc Test Content Type with links" display_field = "asset_field" field { diff --git a/contentful/resource_contentful_entry.go b/contentful/resource_contentful_entry.go new file mode 100644 index 0000000..fd35500 --- /dev/null +++ b/contentful/resource_contentful_entry.go @@ -0,0 +1,206 @@ +package contentful + +import ( + "github.com/hashicorp/terraform/helper/schema" + contentful "github.com/labd/contentful-go" +) + +func resourceContentfulEntry() *schema.Resource { + return &schema.Resource{ + Create: resourceCreateEntry, + Read: resourceReadEntry, + Update: resourceUpdateEntry, + Delete: resourceDeleteEntry, + + Schema: map[string]*schema.Schema{ + "entry_id": { + Type: schema.TypeString, + Required: true, + }, + "version": { + Type: schema.TypeInt, + Computed: true, + }, + "space_id": { + Type: schema.TypeString, + Required: true, + }, + "contenttype_id": { + Type: schema.TypeString, + Required: true, + }, + "locale": { + Type: schema.TypeString, + Required: true, + }, + "field": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + }, + "content": { + Type: schema.TypeString, + Required: true, + }, + "locale": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "published": { + Type: schema.TypeBool, + Required: true, + }, + "archived": { + Type: schema.TypeBool, + Required: true, + }, + }, + } +} + +func resourceCreateEntry(d *schema.ResourceData, m interface{}) (err error) { + client := m.(*contentful.Client) + + fieldProperties := map[string]interface{}{} + rawField := d.Get("field").([]interface{}) + for i := 0; i < len(rawField); i++ { + field := rawField[i].(map[string]interface{}) + fieldProperties[field["id"].(string)] = map[string]interface{}{} + fieldProperties[field["id"].(string)].(map[string]interface{})[field["locale"].(string)] = field["content"].(string) + } + + entry := &contentful.Entry{ + Locale: d.Get("locale").(string), + Fields: fieldProperties, + Sys: &contentful.Sys{ + ID: d.Get("entry_id").(string), + }, + } + + err = client.Entries.Upsert(d.Get("space_id").(string), d.Get("contenttype_id").(string), entry) + if err != nil { + return err + } + + err = setEntryState(d, m) + if err != nil { + return err + } + + if err := setEntryProperties(d, entry); err != nil { + return err + } + + d.SetId(entry.Sys.ID) + return nil +} + +func resourceUpdateEntry(d *schema.ResourceData, m interface{}) (err error) { + client := m.(*contentful.Client) + spaceID := d.Get("space_id").(string) + entryID := d.Id() + + entry, err := client.Entries.Get(spaceID, entryID) + if err != nil { + return err + } + + fieldProperties := map[string]interface{}{} + rawField := d.Get("field").([]interface{}) + for i := 0; i < len(rawField); i++ { + field := rawField[i].(map[string]interface{}) + fieldProperties[field["id"].(string)] = map[string]interface{}{} + fieldProperties[field["id"].(string)].(map[string]interface{})[field["locale"].(string)] = field["content"].(string) + } + + entry.Fields = fieldProperties + entry.Locale = d.Get("locale").(string) + + err = client.Entries.Upsert(d.Get("space_id").(string), d.Get("contenttype_id").(string), entry) + if err != nil { + return err + } + + if err := setEntryState(d, m); err != nil { + return err + } + + if err := setEntryProperties(d, entry); err != nil { + return err + } + d.SetId(entry.Sys.ID) + + return nil +} + +func setEntryState(d *schema.ResourceData, m interface{}) (err error) { + client := m.(*contentful.Client) + spaceID := d.Get("space_id").(string) + entryID := d.Id() + + entry, _ := client.Entries.Get(spaceID, entryID) + + if d.Get("published").(bool) && entry.Sys.PublishedAt == "" { + err = client.Entries.Publish(spaceID, entry) + } else if d.Get("published").(bool) && entry.Sys.PublishedAt != "" { + err = client.Entries.Unpublish(spaceID, entry) + } + + if d.Get("archived").(bool) && entry.Sys.ArchivedAt == "" { + err = client.Entries.Publish(spaceID, entry) + } else if d.Get("archived").(bool) && entry.Sys.ArchivedAt != "" { + err = client.Entries.Unpublish(spaceID, entry) + } + + return nil +} + +func resourceReadEntry(d *schema.ResourceData, m interface{}) (err error) { + client := m.(*contentful.Client) + spaceID := d.Get("space_id").(string) + entryID := d.Id() + + entry, err := client.Entries.Get(spaceID, entryID) + if _, ok := err.(contentful.NotFoundError); ok { + d.SetId("") + return nil + } + + return setEntryProperties(d, entry) +} + +func resourceDeleteEntry(d *schema.ResourceData, m interface{}) (err error) { + client := m.(*contentful.Client) + spaceID := d.Get("space_id").(string) + entryID := d.Id() + + _, err = client.Entries.Get(spaceID, entryID) + if err != nil { + return err + } + + return client.Entries.Delete(spaceID, entryID) +} + +func setEntryProperties(d *schema.ResourceData, entry *contentful.Entry) error { + if err := d.Set("space_id", entry.Sys.Space.Sys.ID); err != nil { + return err + } + + if err := d.Set("version", entry.Sys.Version); err != nil { + return err + } + + if err := d.Set("contenttype_id", entry.Sys.ContentType.Sys.ID); err != nil { + return err + } + + return nil +} diff --git a/contentful/resource_contentful_entry_test.go b/contentful/resource_contentful_entry_test.go new file mode 100644 index 0000000..a98b796 --- /dev/null +++ b/contentful/resource_contentful_entry_test.go @@ -0,0 +1,207 @@ +package contentful + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + contentful "github.com/labd/contentful-go" +) + +func TestAccContentfulEntry_Basic(t *testing.T) { + var entry contentful.Entry + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccContentfulEntryDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContentfulEntryConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckContentfulEntryExists("contentful_entry.myentry", &entry), + testAccCheckContentfulEntryAttributes(&entry, map[string]interface{}{ + "space_id": spaceID, + }), + ), + }, + { + Config: testAccContentfulEntryUpdateConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckContentfulEntryExists("contentful_entry.myentry", &entry), + testAccCheckContentfulEntryAttributes(&entry, map[string]interface{}{ + "space_id": spaceID, + }), + ), + }, + }, + }) +} + +func testAccCheckContentfulEntryExists(n string, entry *contentful.Entry) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("not Found: %s", n) + } + + spaceID := rs.Primary.Attributes["space_id"] + if spaceID == "" { + return fmt.Errorf("no space_id is set") + } + + contenttypeID := rs.Primary.Attributes["contenttype_id"] + if contenttypeID == "" { + return fmt.Errorf("no contenttype_id is set") + } + + client := testAccProvider.Meta().(*contentful.Client) + + contentfulEntry, err := client.Entries.Get(spaceID, rs.Primary.ID) + if err != nil { + return err + } + + *entry = *contentfulEntry + + return nil + } +} + +func testAccCheckContentfulEntryAttributes(entry *contentful.Entry, attrs map[string]interface{}) resource.TestCheckFunc { + return func(s *terraform.State) error { + + spaceIDCheck := attrs["space_id"].(string) + if entry.Sys.Space.Sys.ID != spaceIDCheck { + return fmt.Errorf("space id does not match: %s, %s", entry.Sys.Space.Sys.ID, spaceIDCheck) + } + + return nil + } +} + +func testAccContentfulEntryDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "contentful_entry" { + continue + } + + // get space id from resource data + spaceID := rs.Primary.Attributes["space_id"] + if spaceID == "" { + return fmt.Errorf("no space_id is set") + } + + // check webhook resource id + if rs.Primary.ID == "" { + return fmt.Errorf("no entry ID is set") + } + + // sdk client + client := testAccProvider.Meta().(*contentful.Client) + + entry, _ := client.Entries.Get(spaceID, rs.Primary.ID) + if entry == nil { + return nil + } + + return fmt.Errorf("entry still exists with id: %s", rs.Primary.ID) + } + + return nil +} + +var testAccContentfulEntryConfig = ` +resource "contentful_contenttype" "mycontenttype" { + space_id = "` + spaceID + `" + name = "tf_test_1" + description = "Terraform Acc Test Content Type" + display_field = "field1" + field { + disabled = false + id = "field1" + localized = false + name = "Field 1" + omitted = false + required = true + type = "Text" + } + field { + disabled = false + id = "field2" + localized = false + name = "Field 2" + omitted = false + required = true + type = "Text" + } +} + +resource "contentful_entry" "myentry" { + entry_id = "mytestentry" + space_id = "` + spaceID + `" + contenttype_id = "tf_test_1" + locale = "en-US" + field { + id = "field1" + content = "Hello, World!" + locale = "en-US" + } + field { + id = "field2" + content = "Bacon is healthy!" + locale = "en-US" + } + published = true + archived = false + depends_on = [contentful_contenttype.mycontenttype] +} +` + +var testAccContentfulEntryUpdateConfig = ` +resource "contentful_contenttype" "mycontenttype" { + space_id = "` + spaceID + `" + name = "tf_test_1" + description = "Terraform Acc Test Content Type" + display_field = "field1" + field { + disabled = false + id = "field1" + localized = false + name = "Field 1" + omitted = false + required = true + type = "Text" + } + field { + disabled = false + id = "field2" + localized = false + name = "Field 2" + omitted = false + required = true + type = "Text" + } +} + +resource "contentful_entry" "myentry" { + entry_id = "mytestentry" + space_id = "` + spaceID + `" + contenttype_id = "tf_test_1" + locale = "en-US" + field { + id = "field1" + content = "Hello, World!" + locale = "en-US" + } + field { + id = "field2" + content = "Bacon is healthy!" + locale = "en-US" + } + published = true + archived = false + depends_on = [contentful_contenttype.mycontenttype] +} +` diff --git a/go.mod b/go.mod index 574b785..c04fc29 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,5 @@ go 1.14 require ( github.com/hashicorp/terraform v0.12.24 - github.com/labd/contentful-go v0.4.1-0.20200422125935-8ec49e6fa941 + github.com/labd/contentful-go v0.4.1-0.20200506145415-ef08d6413074 )