diff --git a/docs/resources/port_action.md b/docs/resources/port_action.md index 6e64dd9b..050d916e 100644 --- a/docs/resources/port_action.md +++ b/docs/resources/port_action.md @@ -6,34 +6,34 @@ description: |- Action resource Docs for the Action resource can be found here https://docs.getport.io/create-self-service-experiences/. Example Usage - hcl - resource "port_action" "create_microservice" { + ```hcl + resource "portaction" "createmicroservice" { title = "Create Microservice" identifier = "create-microservice" icon = "Terraform" - self_service_trigger = { + selfservicetrigger = { operation = "CREATE" - blueprint_identifier = port_blueprint.microservice.identifier - user_properties = { - string_props = { + blueprintidentifier = portblueprint.microservice.identifier + userproperties = { + stringprops = { myStringIdentifier = { title = "My String Identifier" required = true format = "entity" - blueprint = port_blueprint.parent.identifier + blueprint = portblueprint.parent.identifier dataset = { combinator = "and" rules = [{ property = "$title" operator = "contains" value = { - jq_query = "\"specificValue\"" + jqquery = "\"specificValue\"" } }] } } } - number_props = { + numberprops = { myNumberIdentifier = { title = "My Number Identifier" required = true @@ -41,25 +41,25 @@ description: |- minimum = 0 } } - boolean_props = { + booleanprops = { myBooleanIdentifier = { title = "My Boolean Identifier" required = true } } - object_props = { + objectprops = { myObjectIdentifier = { title = "My Object Identifier" required = true } } - array_props = { + arrayprops = { myArrayIdentifier = { title = "My Array Identifier" required = true - string_items = { + stringitems = { format = "entity" - blueprint = port_blueprint.parent.identifier + blueprint = portblueprint.parent.identifier dataset = jsonencode({ combinator = "and" rules = [{ @@ -79,6 +79,42 @@ description: |- }) } } + ``` + Example Usage With Condition + ```hcl + resource "portaction" "createmicroservice" { + title = "Create Microservice" + identifier = "create-microservice" + icon = "Terraform" + selfservicetrigger = { + operation = "CREATE" + blueprintidentifier = portblueprint.microservice.identifier + condition = jsonencode({ + type = "SEARCH" + combinator = "and" + rules = [ + { + property = "$title" + operator = "!=" + value = "Test" + } + ] + }) + userproperties = { + stringprops = { + myStringIdentifier = { + title = "My String Identifier" + required = true + } + } + } + } + kafka_method = { + payload = jsonencode({ + runId: "{{.run.id}}" + }) + } + ``` --- # port_action (Resource) @@ -162,6 +198,45 @@ resource "port_action" "create_microservice" { }) } } + +``` + +## Example Usage With Condition + +```hcl +resource "port_action" "create_microservice" { + title = "Create Microservice" + identifier = "create-microservice" + icon = "Terraform" + self_service_trigger = { + operation = "CREATE" + blueprint_identifier = port_blueprint.microservice.identifier + condition = jsonencode({ + type = "SEARCH" + combinator = "and" + rules = [ + { + property = "$title" + operator = "!=" + value = "Test" + } + ] + }) + user_properties = { + string_props = { + myStringIdentifier = { + title = "My String Identifier" + required = true + } + } + } + } + kafka_method = { + payload = jsonencode({ + runId: "{{.run.id}}" + }) + } + ``` @@ -273,6 +348,7 @@ Required: Optional: - `blueprint_identifier` (String) The ID of the blueprint +- `condition` (String) The `condition` field allows you to define rules using Port's [search & query syntax](https://docs.getport.io/search-and-query/#rules) to determine which entities the action will be available for. - `order_properties` (List of String) Order properties - `required_jq_query` (String) The required jq query of the property - `user_properties` (Attributes) User properties (see [below for nested schema](#nestedatt--self_service_trigger--user_properties)) diff --git a/internal/cli/models.go b/internal/cli/models.go index 3d581043..88b53560 100644 --- a/internal/cli/models.go +++ b/internal/cli/models.go @@ -191,9 +191,10 @@ type ( } TriggerCondition struct { - Type string `json:"type"` - Expressions []string `json:"expressions"` + Expressions []string `json:"expressions,omitempty"` Combinator *string `json:"combinator,omitempty"` + Rules []any `json:"rules,omitempty"` + Type string `json:"type"` } Trigger struct { diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 95709ab1..87e7e856 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -98,14 +98,15 @@ func GoObjectToTerraformString(v interface{}) (types.String, error) { return types.StringValue(value), nil } -func TerraformStringToGoObject(s types.String) (interface{}, error) { +func TerraformStringToGoType[T any](s types.String) (T, error) { + var obj T + if s.IsNull() { - return nil, nil + return obj, nil } - var obj interface{} if err := json.Unmarshal([]byte(s.ValueString()), &obj); err != nil { - return nil, err + return obj, err } return obj, nil @@ -136,7 +137,7 @@ func InterfaceToStringArray(o interface{}) []string { func TFStringListToStringArray(list []types.String) []string { res := make([]string, len(list)) - for i, item := range list{ + for i, item := range list { res[i] = item.ValueString() } diff --git a/port/action/actionStateToPortBody.go b/port/action/actionStateToPortBody.go index 4754c553..49b1f33b 100644 --- a/port/action/actionStateToPortBody.go +++ b/port/action/actionStateToPortBody.go @@ -2,6 +2,7 @@ package action import ( "context" + "github.com/port-labs/terraform-provider-port-labs/v2/internal/cli" "github.com/port-labs/terraform-provider-port-labs/v2/internal/consts" "github.com/port-labs/terraform-provider-port-labs/v2/internal/utils" @@ -98,6 +99,14 @@ func triggerToBody(ctx context.Context, data *ActionModel) (*cli.Trigger, error) selfServiceTrigger.UserInputs.Order = orderString } + if !data.SelfServiceTrigger.Condition.IsNull() { + condition, err := utils.TerraformStringToGoType[cli.TriggerCondition](data.SelfServiceTrigger.Condition) + if err != nil { + return nil, err + } + selfServiceTrigger.Condition = &condition + } + return selfServiceTrigger, nil } @@ -146,7 +155,7 @@ func actionPropertiesToBody(ctx context.Context, actionTrigger *cli.Trigger, dat func invocationMethodToBody(ctx context.Context, data *ActionModel) (*cli.InvocationMethod, error) { if data.KafkaMethod != nil { - payload, err := utils.TerraformStringToGoObject(data.KafkaMethod.Payload) + payload, err := utils.TerraformStringToGoType[interface{}](data.KafkaMethod.Payload) if err != nil { return nil, err } @@ -155,11 +164,11 @@ func invocationMethodToBody(ctx context.Context, data *ActionModel) (*cli.Invoca } if data.WebhookMethod != nil { - agent, err := utils.TerraformStringToGoObject(data.WebhookMethod.Agent) + agent, err := utils.TerraformStringToGoType[interface{}](data.WebhookMethod.Agent) if err != nil { return nil, err } - synchronized, err := utils.TerraformStringToGoObject(data.WebhookMethod.Synchronized) + synchronized, err := utils.TerraformStringToGoType[interface{}](data.WebhookMethod.Synchronized) if err != nil { return nil, err } @@ -173,7 +182,7 @@ func invocationMethodToBody(ctx context.Context, data *ActionModel) (*cli.Invoca } headers[key] = keyValue } - body, err := utils.TerraformStringToGoObject(data.WebhookMethod.Body) + body, err := utils.TerraformStringToGoType[interface{}](data.WebhookMethod.Body) if err != nil { return nil, err } @@ -192,11 +201,11 @@ func invocationMethodToBody(ctx context.Context, data *ActionModel) (*cli.Invoca } if data.GithubMethod != nil { - reportWorkflowStatus, err := utils.TerraformStringToGoObject(data.GithubMethod.ReportWorkflowStatus) + reportWorkflowStatus, err := utils.TerraformStringToGoType[interface{}](data.GithubMethod.ReportWorkflowStatus) if err != nil { return nil, err } - wi, err := utils.TerraformStringToGoObject(data.GithubMethod.WorkflowInputs) + wi, err := utils.TerraformStringToGoType[interface{}](data.GithubMethod.WorkflowInputs) if err != nil { return nil, err } @@ -215,7 +224,7 @@ func invocationMethodToBody(ctx context.Context, data *ActionModel) (*cli.Invoca } if data.GitlabMethod != nil { - pv, err := utils.TerraformStringToGoObject(data.GitlabMethod.PipelineVariables) + pv, err := utils.TerraformStringToGoType[interface{}](data.GitlabMethod.PipelineVariables) if err != nil { return nil, err } @@ -233,7 +242,7 @@ func invocationMethodToBody(ctx context.Context, data *ActionModel) (*cli.Invoca } if data.AzureMethod != nil { - payload, err := utils.TerraformStringToGoObject(data.AzureMethod.Payload) + payload, err := utils.TerraformStringToGoType[interface{}](data.AzureMethod.Payload) if err != nil { return nil, err } diff --git a/port/action/model.go b/port/action/model.go index 8ace9dc9..81c76a35 100644 --- a/port/action/model.go +++ b/port/action/model.go @@ -275,6 +275,7 @@ type SelfServiceTriggerModel struct { UserProperties *UserPropertiesModel `tfsdk:"user_properties"` RequiredJqQuery types.String `tfsdk:"required_jq_query"` OrderProperties types.List `tfsdk:"order_properties"` + Condition types.String `tfsdk:"condition"` } type EntityCreatedEventModel struct { diff --git a/port/action/refreshActionState.go b/port/action/refreshActionState.go index 9afdc2d6..4c022952 100644 --- a/port/action/refreshActionState.go +++ b/port/action/refreshActionState.go @@ -2,6 +2,7 @@ package action import ( "context" + "encoding/json" "fmt" "reflect" @@ -297,6 +298,14 @@ func writeTriggerToResource(ctx context.Context, a *cli.Action, state *ActionMod } } + if a.Trigger.Condition != nil { + triggerCondition, err := json.Marshal(a.Trigger.Condition) + if err != nil { + return err + } + state.SelfServiceTrigger.Condition = types.StringValue(string(triggerCondition)) + } + return nil } diff --git a/port/action/resource_test.go b/port/action/resource_test.go index 2426229c..ff49b227 100644 --- a/port/action/resource_test.go +++ b/port/action/resource_test.go @@ -1734,3 +1734,81 @@ func TestAccPortActionNoUserPropertiesConditional(t *testing.T) { }, }) } + +func TestAccPortActionConditionalTrigger(t *testing.T) { + identifier := utils.GenID() + actionIdentifier := utils.GenID() + var testAccActionConfigCreate = testAccCreateBlueprintConfig(identifier) + fmt.Sprintf(` + resource "port_action" "create_microservice" { + title = "TF Provider Test" + identifier = "%s" + icon = "Terraform" + self_service_trigger = { + operation = "DAY-2" + blueprint_identifier = port_blueprint.microservice.identifier + condition = jsonencode({ + type = "SEARCH" + combinator = "and" + rules = [ + { + property = "$identifier" + operator = "=" + value = "Test" + } + ] + }) + } + kafka_method = {} + }`, actionIdentifier) + + var testAccActionConfigUpdate = testAccCreateBlueprintConfig(identifier) + fmt.Sprintf(` + resource "port_action" "create_microservice" { + title = "TF Provider Test" + identifier = "%s" + icon = "Terraform" + self_service_trigger = { + operation = "DAY-2" + blueprint_identifier = port_blueprint.microservice.identifier + condition = jsonencode({ + type = "SEARCH" + combinator = "and" + rules = [ + { + property = "$title" + operator = "=" + value = "Test" + } + ] + }) + } + kafka_method = {} + }`, actionIdentifier) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: acctest.ProviderConfig + testAccActionConfigCreate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_action.create_microservice", "title", "TF Provider Test"), + resource.TestCheckResourceAttr("port_action.create_microservice", "identifier", actionIdentifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "icon", "Terraform"), + resource.TestCheckResourceAttr("port_action.create_microservice", "self_service_trigger.blueprint_identifier", identifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "self_service_trigger.operation", "DAY-2"), + resource.TestCheckResourceAttr("port_action.create_microservice", "self_service_trigger.condition", `{"combinator":"and","rules":[{"operator":"=","property":"$identifier","value":"Test"}],"type":"SEARCH"}`), + ), + }, + { + Config: acctest.ProviderConfig + testAccActionConfigUpdate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("port_action.create_microservice", "title", "TF Provider Test"), + resource.TestCheckResourceAttr("port_action.create_microservice", "identifier", actionIdentifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "icon", "Terraform"), + resource.TestCheckResourceAttr("port_action.create_microservice", "self_service_trigger.blueprint_identifier", identifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "self_service_trigger.operation", "DAY-2"), + resource.TestCheckResourceAttr("port_action.create_microservice", "self_service_trigger.condition", `{"combinator":"and","rules":[{"operator":"=","property":"$title","value":"Test"}],"type":"SEARCH"}`), + ), + }, + }, + }) +} diff --git a/port/action/schema.go b/port/action/schema.go index 8694fe94..fc4c8c8b 100644 --- a/port/action/schema.go +++ b/port/action/schema.go @@ -3,8 +3,11 @@ package action import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectdefault" + "regexp" + "github.com/hashicorp/terraform-plugin-framework-validators/boolvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" @@ -19,7 +22,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-go/tftypes" "github.com/port-labs/terraform-provider-port-labs/v2/internal/utils" - "regexp" ) func MetadataProperties() map[string]schema.Attribute { @@ -126,6 +128,10 @@ func ActionSchema() map[string]schema.Attribute { Optional: true, ElementType: types.StringType, }, + "condition": schema.StringAttribute{ + MarkdownDescription: "The `condition` field allows you to define rules using Port's [search & query syntax](https://docs.getport.io/search-and-query/#rules) to determine which entities the action will be available for.", + Optional: true, + }, }, Validators: []validator.Object{ objectvalidator.ExactlyOneOf( @@ -966,4 +972,45 @@ resource "port_action" "create_microservice" { runId: "{{"{{.run.id}}"}}" }) } -}` + "\n```" +} +` + "\n```" + ` + +## Example Usage With Condition + +` + "```hcl" + ` +resource "port_action" "create_microservice" { + title = "Create Microservice" + identifier = "create-microservice" + icon = "Terraform" + self_service_trigger = { + operation = "CREATE" + blueprint_identifier = port_blueprint.microservice.identifier + condition = jsonencode({ + type = "SEARCH" + combinator = "and" + rules = [ + { + property = "$title" + operator = "!=" + value = "Test" + } + ] + }) + user_properties = { + string_props = { + myStringIdentifier = { + title = "My String Identifier" + required = true + } + } + } + } + kafka_method = { + payload = jsonencode({ + runId: "{{"{{.run.id}}"}}" + }) + } + +` + "```" + ` + +`