diff --git a/docs/resources/port_scorecard.md b/docs/resources/port_scorecard.md index ef309f11..8a3fe24c 100644 --- a/docs/resources/port_scorecard.md +++ b/docs/resources/port_scorecard.md @@ -459,6 +459,10 @@ Required: - `query` (Attributes) The query of the rule (see [below for nested schema](#nestedatt--rules--query)) - `title` (String) The title of the rule +Optional: + +- `description` (String) The description of the rule + ### Nested Schema for `rules.query` diff --git a/internal/cli/models.go b/internal/cli/models.go index 95a75dce..48ec4f48 100644 --- a/internal/cli/models.go +++ b/internal/cli/models.go @@ -254,7 +254,7 @@ type ( ChangelogDestination *ChangelogDestination `json:"changelogDestination,omitempty"` TeamInheritance *TeamInheritance `json:"teamInheritance,omitempty"` Relations map[string]Relation `json:"relations"` - Ownership *Ownership `json:"ownership,omitempty"` + Ownership *Ownership `json:"ownership,omitempty"` } Action struct { @@ -354,10 +354,11 @@ type ( } Rule struct { - Identifier string `json:"identifier,omitempty"` - Title string `json:"title,omitempty"` - Level string `json:"level,omitempty"` - Query Query `json:"query,omitempty"` + Identifier string `json:"identifier,omitempty"` + Title string `json:"title,omitempty"` + Description string `json:"description,omitempty"` + Level string `json:"level,omitempty"` + Query Query `json:"query,omitempty"` } Level struct { diff --git a/port/scorecard/model.go b/port/scorecard/model.go index fbf860ce..43db81ff 100644 --- a/port/scorecard/model.go +++ b/port/scorecard/model.go @@ -10,10 +10,11 @@ type Query struct { } type Rule struct { - Identifier types.String `tfsdk:"identifier"` - Title types.String `tfsdk:"title"` - Level types.String `tfsdk:"level"` - Query *Query `tfsdk:"query"` + Identifier types.String `tfsdk:"identifier"` + Title types.String `tfsdk:"title"` + Description types.String `tfsdk:"description"` + Level types.String `tfsdk:"level"` + Query *Query `tfsdk:"query"` } type Level struct { diff --git a/port/scorecard/refreshScorecardState.go b/port/scorecard/refreshScorecardState.go index 79ded6da..bc82830b 100644 --- a/port/scorecard/refreshScorecardState.go +++ b/port/scorecard/refreshScorecardState.go @@ -3,18 +3,20 @@ package scorecard import ( "context" "fmt" + "github.com/port-labs/terraform-provider-port-labs/v2/internal/utils" + "reflect" + "github.com/hashicorp/terraform-plugin-framework/types" "github.com/port-labs/terraform-provider-port-labs/v2/internal/cli" - "reflect" ) func shouldRefreshLevels(stateLevels []Level, cliLevels []cli.Level) bool { - // When you create a scorecard in Port, the scorecard gets created with default levels. + // When you create a scorecard in Port, the scorecard gets created with default levels. // If your scorecard doesn't have the "levels" attribute, it means the scorecard is created with default levels behind the scenes. // - // If the TF state has no levels and the Port existing levels are the default levels, This means both are considered + // If the TF state has no levels and the Port existing levels are the default levels, This means both are considered // to have default levels, And so we don't need to update them. if len(stateLevels) == 0 && reflect.DeepEqual(cliLevels, DefaultCliLevels()) { return false @@ -22,7 +24,7 @@ func shouldRefreshLevels(stateLevels []Level, cliLevels []cli.Level) bool { // If the TF state has defined levels, we have to make sure that Port's existing levels are the same as the TF state levels. // also, // If TF state doesn't have levels and the Port existing levels are not the default ones, - // this means we have to make sure that Port's defined levels are the default levels, + // this means we have to make sure that Port's defined levels are the default levels, // as the state without levels is considered to have default levels. if len(stateLevels) > 0 || (len(stateLevels) == 0 && !reflect.DeepEqual(cliLevels, DefaultCliLevels())) { return true @@ -81,6 +83,13 @@ func refreshScorecardState(ctx context.Context, state *ScorecardModel, s *cli.Sc Level: types.StringValue(rule.Level), Identifier: types.StringValue(rule.Identifier), } + + if rule.Description != "" { + stateRule.Description = types.StringValue(rule.Description) + } else { + stateRule.Description = types.StringNull() + } + stateQuery := &Query{ Combinator: types.StringValue(rule.Query.Combinator), } diff --git a/port/scorecard/resource_test.go b/port/scorecard/resource_test.go index 8ab1657f..3d61ef5d 100644 --- a/port/scorecard/resource_test.go +++ b/port/scorecard/resource_test.go @@ -195,6 +195,7 @@ func TestAccPortScorecardUpdate(t *testing.T) { rules = [{ identifier = "hasTeam" title = "Has Team" + description = "Checks if the entity has a team assigned" level = "Gold" query = { combinator = "and" @@ -218,6 +219,7 @@ func TestAccPortScorecardUpdate(t *testing.T) { rules = [{ identifier = "hasTeam" title = "Has Team" + description = "Updated: Verifies team assignment" level = "Bronze" query = { combinator = "or" @@ -245,6 +247,7 @@ func TestAccPortScorecardUpdate(t *testing.T) { resource.TestCheckResourceAttr("port_scorecard.test", "rules.#", "1"), resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.identifier", "hasTeam"), resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.title", "Has Team"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.description", "Checks if the entity has a team assigned"), resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.level", "Gold"), resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.combinator", "and"), resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.conditions.#", "1"), @@ -259,6 +262,7 @@ func TestAccPortScorecardUpdate(t *testing.T) { resource.TestCheckResourceAttr("port_scorecard.test", "rules.#", "1"), resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.identifier", "hasTeam"), resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.title", "Has Team"), + resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.description", "Updated: Verifies team assignment"), resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.level", "Bronze"), resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.combinator", "or"), resource.TestCheckResourceAttr("port_scorecard.test", "rules.0.query.conditions.#", "1"), diff --git a/port/scorecard/schema.go b/port/scorecard/schema.go index 1e4629c8..e2cab003 100644 --- a/port/scorecard/schema.go +++ b/port/scorecard/schema.go @@ -2,6 +2,7 @@ package scorecard import ( "context" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -36,6 +37,10 @@ func RuleSchema() map[string]schema.Attribute { MarkdownDescription: "The title of the rule", Required: true, }, + "description": schema.StringAttribute{ + MarkdownDescription: "The description of the rule", + Optional: true, + }, "level": schema.StringAttribute{ MarkdownDescription: "The level of the rule", Required: true, diff --git a/port/scorecard/scorecardResourceToPortBody.go b/port/scorecard/scorecardResourceToPortBody.go index 7f078558..b0c2ad0f 100644 --- a/port/scorecard/scorecardResourceToPortBody.go +++ b/port/scorecard/scorecardResourceToPortBody.go @@ -3,6 +3,7 @@ package scorecard import ( "context" "encoding/json" + "github.com/port-labs/terraform-provider-port-labs/v2/internal/cli" ) @@ -32,6 +33,11 @@ func scorecardResourceToPortBody(ctx context.Context, state *ScorecardModel) (*c Identifier: stateRule.Identifier.ValueString(), Title: stateRule.Title.ValueString(), } + + if !stateRule.Description.IsNull() { + rule.Description = stateRule.Description.ValueString() + } + query := &cli.Query{ Combinator: stateRule.Query.Combinator.ValueString(), }