Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(67): Add Tags resource #122

Merged
merged 8 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading