Skip to content

Commit

Permalink
fix(67): Add Tags resource (#122)
Browse files Browse the repository at this point in the history
 * Add Tags resource

* Add tags support to feature resource

* Add docs

* update changelog

* bump plugin-docs
  • Loading branch information
gagantrivedi authored Jan 30, 2024
1 parent e88dd5a commit 52bb9eb
Show file tree
Hide file tree
Showing 14 changed files with 568 additions and 115 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
## 0.7.0
FEATURES:
* Add resource `flagsmith_tag`
* Update resource `flagsmith_feature` to add support for tags

BUG FIXES
fix https://github.com/Flagsmith/terraform-provider-flagsmith/issues/67

## 0.6.0
NOTES:
* This Go module(and related dependencies) has been updated to GO 1.20 as per the Go Support policy

## 0.5.1
BUG FIXES
fix https://github.com/Flagsmith/terraform-provider-flagsmith/issues/81
Expand Down
1 change: 1 addition & 0 deletions docs/resources/feature.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ resource "flagsmith_feature" "new_standard_feature" {
- `initial_value` (String) Determines the initial value of the feature.
- `is_archived` (Boolean) Can be used to archive/unarchive a feature. If unspecified, it will default to false
- `owners` (Set of Number) List of user IDs representing the owners of the feature.
- `tags` (Set of Number) List of tag IDs representing the tags attached to the feature.
- `type` (String) Type of the feature, can be STANDARD, or MULTIVARIATE. if unspecified, it will default to STANDARD

### Read-Only
Expand Down
32 changes: 32 additions & 0 deletions docs/resources/tag.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "flagsmith_tag Resource - terraform-provider-flagsmith"
subcategory: ""
description: |-
Flagsmith Tag
---

# flagsmith_tag (Resource)

Flagsmith Tag



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `project_uuid` (String) UUID of project the tag belongs to
- `tag_colour` (String) Hexadecimal value of the tag color
- `tag_name` (String) Name of the tag

### Optional

- `description` (String) Description of the feature

### Read-Only

- `id` (Number) ID of the tag
- `project_id` (Number) ID of the project
- `uuid` (String) UUID of the tag
63 changes: 62 additions & 1 deletion flagsmith/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ type FeatureResourceData struct {
DefaultEnabled types.Bool `tfsdk:"default_enabled"`
IsArchived types.Bool `tfsdk:"is_archived"`
Owners *[]types.Int64 `tfsdk:"owners"`
Tags *[]types.Int64 `tfsdk:"tags"`
ProjectID types.Int64 `tfsdk:"project_id"`
ProjectUUID types.String `tfsdk:"project_uuid"`
}
Expand All @@ -241,6 +242,7 @@ func (f *FeatureResourceData) ToClientFeature() *flagsmithapi.Feature {
DefaultEnabled: f.DefaultEnabled.ValueBool(),
IsArchived: f.IsArchived.ValueBool(),
ProjectUUID: f.ProjectUUID.ValueString(),
Tags: []int64{},
Owners: &[]int64{},
}
if !f.ID.IsNull() && !f.ID.IsUnknown() {
Expand All @@ -259,7 +261,12 @@ func (f *FeatureResourceData) ToClientFeature() *flagsmithapi.Feature {
for _, owner := range *f.Owners {
ownerID := owner.ValueInt64()
*feature.Owners = append(*feature.Owners, ownerID)

}
}
if f.Tags != nil {
for _, tag := range *f.Tags {
tagID := tag.ValueInt64()
feature.Tags = append(feature.Tags, tagID)
}
}
return &feature
Expand Down Expand Up @@ -292,6 +299,12 @@ func MakeFeatureResourceDataFromClientFeature(clientFeature *flagsmithapi.Featur
*resourceData.Owners = append(*resourceData.Owners, types.Int64Value(owner))
}
}
if clientFeature.Tags != nil && len(clientFeature.Tags) > 0 {
resourceData.Tags = &[]types.Int64{}
for _, tag := range clientFeature.Tags {
*resourceData.Tags = append(*resourceData.Tags, types.Int64Value(tag))
}
}
return resourceData
}

Expand Down Expand Up @@ -439,3 +452,51 @@ func MakeSegmentResourceDataFromClientSegment(clientSegment *flagsmithapi.Segmen
}
return resourceData
}

type TagResourceData struct {
ID types.Int64 `tfsdk:"id"`
UUID types.String `tfsdk:"uuid"`
Name types.String `tfsdk:"tag_name"`
Description types.String `tfsdk:"description"`
ProjectID types.Int64 `tfsdk:"project_id"`
ProjectUUID types.String `tfsdk:"project_uuid"`
Colour types.String `tfsdk:"tag_colour"`
}

func (t *TagResourceData) ToClientTag() *flagsmithapi.Tag {
tag := flagsmithapi.Tag{
UUID: t.UUID.ValueString(),
Name: t.Name.ValueString(),
ProjectUUID: t.ProjectUUID.ValueString(),
Colour: t.Colour.ValueString(),
}
if t.Description.ValueString() != "" {
value := t.Description.ValueString()
tag.Description = &value
}
if !t.ID.IsNull() && !t.ID.IsUnknown() {
tagID := t.ID.ValueInt64()
tag.ID = &tagID
}
if !t.ProjectID.IsNull() && !t.ProjectID.IsUnknown() {
projectID := t.ProjectID.ValueInt64()
tag.ProjectID = &projectID
}
return &tag
}

func MakeTagResourceDataFromClientTag(clientTag *flagsmithapi.Tag) TagResourceData {
resourceData := TagResourceData{
ID: types.Int64Value(*clientTag.ID),
UUID: types.StringValue(clientTag.UUID),
Name: types.StringValue(clientTag.Name),
ProjectID: types.Int64Value(*clientTag.ProjectID),
ProjectUUID: types.StringValue(clientTag.ProjectUUID),
Colour: types.StringValue(clientTag.Colour),
}
if clientTag.Description != nil {
resourceData.Description = types.StringValue(*clientTag.Description)
}

return resourceData
}
1 change: 1 addition & 0 deletions flagsmith/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func (p *fsProvider) Resources(ctx context.Context) []func() resource.Resource {
newFeatureStateResource,
newSegmentResource,
newMultivariateResource,
newTagResource,
}

}
Expand Down
16 changes: 16 additions & 0 deletions flagsmith/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import (
"github.com/Flagsmith/terraform-provider-flagsmith/flagsmith"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"

"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"os"
"strconv"
"testing"
"fmt"
)

// Create provider factories - to be used by resource tests
Expand Down Expand Up @@ -68,3 +71,16 @@ func testClient() *flagsmithapi.Client {

return tc
}
func getAttributefromState(s *terraform.State, resourceName , attribute string) (string, error) {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return "", fmt.Errorf("not found: %s", resourceName)
}

uuid := rs.Primary.Attributes[attribute]

if uuid == "" {
return "", fmt.Errorf("no uuid is set")
}
return uuid, nil
}
5 changes: 5 additions & 0 deletions flagsmith/resource_feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ func (t *featureResource) Schema(ctx context.Context, req resource.SchemaRequest
ElementType: types.Int64Type,
MarkdownDescription: "List of user IDs representing the owners of the feature.",
},
"tags": schema.SetAttribute{
Optional: true,
ElementType: types.Int64Type,
MarkdownDescription: "List of tag IDs representing the tags attached to the feature.",
},
"project_uuid": schema.StringAttribute{
MarkdownDescription: "UUID of project the feature belongs to",
Required: true,
Expand Down
14 changes: 6 additions & 8 deletions flagsmith/resource_feature_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"regexp"
"strconv"
"testing"
"regexp"
)

func TestAccEnvironmentFeatureStateResource(t *testing.T) {
Expand All @@ -19,15 +19,13 @@ func TestAccEnvironmentFeatureStateResource(t *testing.T) {
Steps: []resource.TestStep{
// Test feature State value validator
{
Config: testAccInvalidFeatureStateValueConfig(),
Config: testAccInvalidFeatureStateValueConfig(),
ExpectError: regexp.MustCompile(`Exactly one of these attributes must be configured:\n\[feature_state_value.string_value,feature_state_value.integer_value,feature_state_value.boolean_value\]`),

},
// Test feature State string value validator
{
Config: testAccEnvironmentFeatureStateResourceConfig(" some_value ", true),
Config: testAccEnvironmentFeatureStateResourceConfig(" some_value ", true),
ExpectError: regexp.MustCompile(`Attribute feature_state_value.string_value Leading and trailing whitespace is\n.*not allowed`),

},

// Create and Read testing
Expand Down Expand Up @@ -134,7 +132,7 @@ func TestAccSegmentFeatureStateResource(t *testing.T) {

func getFeatureStateImportID(n string) resource.ImportStateIdFunc {
return func(s *terraform.State) (string, error) {
uuid, err := getUUIDfromState(s, n)
uuid, err := getAttributefromState(s, n, "uuid")
if err != nil {
return "", err
}
Expand All @@ -144,7 +142,7 @@ func getFeatureStateImportID(n string) resource.ImportStateIdFunc {
}

func testAccCheckSegmentFeatureStateDestroy(s *terraform.State) error {
uuid, err := getUUIDfromState(s, "flagsmith_feature_state.dummy_environment_feature_x_segment_override")
uuid, err := getAttributefromState(s, "flagsmith_feature_state.dummy_environment_feature_x_segment_override", "uuid")
if err != nil {
return err
}
Expand Down Expand Up @@ -237,5 +235,5 @@ resource "flagsmith_feature_state" "dummy_environment_feature_x" {
}
}
`, environmentKey(), featureID())
`, environmentKey(), featureID())
}
33 changes: 16 additions & 17 deletions flagsmith/resource_feature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ func TestAccFeatureResource(t *testing.T) {
resource.TestCheckResourceAttr("flagsmith_feature.test_feature", "owners.0", fmt.Sprintf("%d", firstUserID)),
resource.TestCheckResourceAttr("flagsmith_feature.test_feature", "owners.1", fmt.Sprintf("%d", secondUserID)),

resource.TestCheckResourceAttrSet("flagsmith_feature.test_feature", "tags.0"),

resource.TestCheckResourceAttrSet("flagsmith_feature.test_feature", "id"),
resource.TestCheckResourceAttrSet("flagsmith_feature.test_feature", "uuid"),
resource.TestCheckResourceAttrSet("flagsmith_feature.test_feature", "project_id"),
Expand All @@ -56,6 +58,8 @@ func TestAccFeatureResource(t *testing.T) {
resource.TestCheckResourceAttr("flagsmith_feature.test_feature", "owners.0", fmt.Sprintf("%d", firstUserID)),
resource.TestCheckResourceAttr("flagsmith_feature.test_feature", "owners.1", fmt.Sprintf("%d", secondUserID)),

resource.TestCheckResourceAttrSet("flagsmith_feature.test_feature", "tags.0"),

resource.TestCheckResourceAttrSet("flagsmith_feature.test_feature", "id"),
resource.TestCheckResourceAttrSet("flagsmith_feature.test_feature", "uuid"),
resource.TestCheckResourceAttrSet("flagsmith_feature.test_feature", "project_id"),
Expand All @@ -70,6 +74,8 @@ func TestAccFeatureResource(t *testing.T) {
resource.TestCheckResourceAttr("flagsmith_feature.test_feature", "description", "feature description updated"),
resource.TestCheckResourceAttr("flagsmith_feature.test_feature", "project_uuid", projectUUID()),

resource.TestCheckResourceAttrSet("flagsmith_feature.test_feature", "tags.0"),

resource.TestCheckResourceAttr("flagsmith_feature.test_feature", "owners.0", fmt.Sprintf("%d", firstUserID)),
resource.TestCheckResourceAttr("flagsmith_feature.test_feature", "owners.1", fmt.Sprintf("%d", thirdUserID)),
),
Expand Down Expand Up @@ -136,14 +142,15 @@ func TestAccFeatureResourceOwners(t *testing.T) {
},
})
}

func getFeatureImportID(n string) resource.ImportStateIdFunc {
return func(s *terraform.State) (string, error) {
return getUUIDfromState(s, n)
return getAttributefromState(s, n, "uuid")
}
}

func testAccCheckFeatureResourceDestroy(s *terraform.State) error {
uuid, err := getUUIDfromState(s, "flagsmith_feature.test_feature")
uuid, err := getAttributefromState(s, "flagsmith_feature.test_feature", "uuid")
if err != nil {
return err
}
Expand All @@ -156,33 +163,25 @@ func testAccCheckFeatureResourceDestroy(s *terraform.State) error {

}

func getUUIDfromState(s *terraform.State, resourceName string) (string, error) {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return "", fmt.Errorf("not found: %s", resourceName)
}

uuid := rs.Primary.Attributes["uuid"]

if uuid == "" {
return "", fmt.Errorf("no uuid is set")
}
return uuid, nil
}

func testAccFeatureResourceConfig(featureName, description string, owners []int) string {
return fmt.Sprintf(`
provider "flagsmith" {
}
resource "flagsmith_tag" "test_tag" {
tag_name = "feature_acc_test_tag"
tag_colour = "#000000"
project_uuid = "%s"
}
resource "flagsmith_feature" "test_feature" {
feature_name = "%s"
description = "%s"
project_uuid = "%s"
type = "STANDARD"
owners = %s
tags = [flagsmith_tag.test_tag.id]
}
`, featureName, description, projectUUID(), strings.Join(strings.Fields(fmt.Sprint(owners)), ","))
`, projectUUID(), featureName, description, projectUUID(), strings.Join(strings.Fields(fmt.Sprint(owners)), ","))
}
Loading

0 comments on commit 52bb9eb

Please sign in to comment.