diff --git a/docs/resources/port_action.md b/docs/resources/port_action.md index 050d916e..e33352ec 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 "portaction" "createmicroservice" { + hcl + resource "port_action" "create_microservice" { title = "Create Microservice" identifier = "create-microservice" icon = "Terraform" - selfservicetrigger = { + self_service_trigger = { operation = "CREATE" - blueprintidentifier = portblueprint.microservice.identifier - userproperties = { - stringprops = { + blueprint_identifier = port_blueprint.microservice.identifier + user_properties = { + string_props = { myStringIdentifier = { title = "My String Identifier" required = true format = "entity" - blueprint = portblueprint.parent.identifier + blueprint = port_blueprint.parent.identifier dataset = { combinator = "and" rules = [{ property = "$title" operator = "contains" value = { - jqquery = "\"specificValue\"" + jq_query = "\"specificValue\"" } }] } } } - numberprops = { + number_props = { myNumberIdentifier = { title = "My Number Identifier" required = true @@ -41,25 +41,25 @@ description: |- minimum = 0 } } - booleanprops = { + boolean_props = { myBooleanIdentifier = { title = "My Boolean Identifier" required = true } } - objectprops = { + object_props = { myObjectIdentifier = { title = "My Object Identifier" required = true } } - arrayprops = { + array_props = { myArrayIdentifier = { title = "My Array Identifier" required = true - stringitems = { + string_items = { format = "entity" - blueprint = portblueprint.parent.identifier + blueprint = port_blueprint.parent.identifier dataset = jsonencode({ combinator = "and" rules = [{ @@ -79,6 +79,26 @@ description: |- }) } } + + Example Usage with Automation trigger + Port allows setting an automation trigger to an action, for executing an action based on event occurred to an entity in Port. + ```hcl + resource "portaction" "deletetemporarymicroservice" { + title = "Delete Temporary Microservice" + identifier = "delete-temp-microservice" + icon = "Terraform" + automationtrigger = { + timerpropertyexpiredevent = { + blueprintidentifier = portblueprint.microservice.identifier + propertyidentifier = "ttl" + } + } + kafka_method = { + payload = jsonencode({ + runId: "{{.run.id}}" + }) + } + } ``` Example Usage With Condition ```hcl @@ -198,6 +218,29 @@ resource "port_action" "create_microservice" { }) } } +``` + +## Example Usage with Automation trigger + +Port allows setting an automation trigger to an action, for executing an action based on event occurred to an entity in Port. + +```hcl +resource "port_action" "delete_temporary_microservice" { + title = "Delete Temporary Microservice" + identifier = "delete-temp-microservice" + icon = "Terraform" + automation_trigger = { + timer_property_expired_event = { + blueprint_identifier = port_blueprint.microservice.identifier + property_identifier = "ttl" + } + } + kafka_method = { + payload = jsonencode({ + runId: "{{.run.id}}" + }) + } +} ``` @@ -252,6 +295,7 @@ resource "port_action" "create_microservice" { - `approval_email_notification` (Object) The email notification of the approval (see [below for nested schema](#nestedatt--approval_email_notification)) - `approval_webhook_notification` (Attributes) The webhook notification of the approval (see [below for nested schema](#nestedatt--approval_webhook_notification)) +- `automation_trigger` (Attributes) Automation trigger for the action (see [below for nested schema](#nestedatt--automation_trigger)) - `azure_method` (Attributes) Azure DevOps invocation method (see [below for nested schema](#nestedatt--azure_method)) - `blueprint` (String, Deprecated) The blueprint identifier the action relates to - `description` (String) Description @@ -263,6 +307,7 @@ resource "port_action" "create_microservice" { - `required_approval` (Boolean) Require approval before invoking the action - `self_service_trigger` (Attributes) Self service trigger for the action (see [below for nested schema](#nestedatt--self_service_trigger)) - `title` (String) Title +- `upsert_entity_method` (Attributes) Upsert Entity invocation method (see [below for nested schema](#nestedatt--upsert_entity_method)) - `webhook_method` (Attributes) Webhook invocation method (see [below for nested schema](#nestedatt--webhook_method)) ### Read-Only @@ -288,6 +333,72 @@ Optional: - `format` (String) The format to invoke the webhook + +### Nested Schema for `automation_trigger` + +Optional: + +- `any_entity_change_event` (Attributes) Any entity change event trigger (see [below for nested schema](#nestedatt--automation_trigger--any_entity_change_event)) +- `entity_created_event` (Attributes) Entity created event trigger (see [below for nested schema](#nestedatt--automation_trigger--entity_created_event)) +- `entity_deleted_event` (Attributes) Entity deleted event trigger (see [below for nested schema](#nestedatt--automation_trigger--entity_deleted_event)) +- `entity_updated_event` (Attributes) Entity updated event trigger (see [below for nested schema](#nestedatt--automation_trigger--entity_updated_event)) +- `jq_condition` (Attributes) JQ condition for automation trigger (see [below for nested schema](#nestedatt--automation_trigger--jq_condition)) +- `timer_property_expired_event` (Attributes) Timer property expired event trigger (see [below for nested schema](#nestedatt--automation_trigger--timer_property_expired_event)) + + +### Nested Schema for `automation_trigger.any_entity_change_event` + +Required: + +- `blueprint_identifier` (String) The blueprint identifier of the changed entity + + + +### Nested Schema for `automation_trigger.entity_created_event` + +Required: + +- `blueprint_identifier` (String) The blueprint identifier of the created entity + + + +### Nested Schema for `automation_trigger.entity_deleted_event` + +Required: + +- `blueprint_identifier` (String) The blueprint identifier of the deleted entity + + + +### Nested Schema for `automation_trigger.entity_updated_event` + +Required: + +- `blueprint_identifier` (String) The blueprint identifier of the updated entity + + + +### Nested Schema for `automation_trigger.jq_condition` + +Required: + +- `expressions` (List of String) The jq expressions of the condition + +Optional: + +- `combinator` (String) The combinator of the condition + + + +### Nested Schema for `automation_trigger.timer_property_expired_event` + +Required: + +- `blueprint_identifier` (String) The blueprint identifier of the expired timer property +- `property_identifier` (String) The property identifier of the expired timer property + + + ### Nested Schema for `azure_method` @@ -535,6 +646,23 @@ Optional: + +### Nested Schema for `upsert_entity_method` + +Required: + +- `blueprint_identifier` (String) Required when selecting type Upsert Entity. The blueprint identifier of the entity for the upsert +- `identifier` (String) Required when selecting type Upsert Entity. The entity identifier for the upsert + +Optional: + +- `icon` (String) The icon of the entity +- `properties` (String) The properties of the entity (key-value object encoded to a string) +- `relations` (String) The relations of the entity (key-value object encoded to a string) +- `teams` (List of String) The teams the entity belongs to +- `title` (String) The title of the entity + + ### Nested Schema for `webhook_method` diff --git a/internal/cli/models.go b/internal/cli/models.go index f5301b43..202442dc 100644 --- a/internal/cli/models.go +++ b/internal/cli/models.go @@ -146,6 +146,15 @@ type ( Required []string `json:"required,omitempty"` } + MappingSchema struct { + Identifier *string `json:"identifier,omitempty"` + Title *string `json:"title,omitempty"` + Team any `json:"team,omitempty"` + Icon *string `json:"icon,omitempty"` + Properties map[string]any `json:"properties,omitempty"` + Relations map[string]any `json:"relations,omitempty"` + } + InvocationMethod struct { Type string `json:"type"` Payload any `json:"payload,omitempty"` @@ -166,13 +175,8 @@ type ( DefaultRef *string `json:"defaultRef,omitempty"` PipelineVariables map[string]any `json:"pipelineVariables,omitempty"` Webhook *string `json:"webhook,omitempty"` - Identifier *string `json:"identifier,omitempty"` - Title *string `json:"title,omitempty"` BlueprintIdentifier *string `json:"blueprintIdentifier,omitempty"` - Team any `json:"team,omitempty"` - Icon *string `json:"icon,omitempty"` - Properties map[string]any `json:"properties,omitempty"` - Relations map[string]any `json:"relations,omitempty"` + Mapping *MappingSchema `json:"mapping,omitempty"` } ApprovalNotification struct { diff --git a/port/action/actionStateToPortBody.go b/port/action/actionStateToPortBody.go index 49b1f33b..2ee962d5 100644 --- a/port/action/actionStateToPortBody.go +++ b/port/action/actionStateToPortBody.go @@ -5,6 +5,7 @@ import ( "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/flex" "github.com/port-labs/terraform-provider-port-labs/v2/internal/utils" ) @@ -110,6 +111,58 @@ func triggerToBody(ctx context.Context, data *ActionModel) (*cli.Trigger, error) return selfServiceTrigger, nil } + if data.AutomationTrigger != nil { + automationTrigger := &cli.Trigger{ + Type: consts.Automation, + } + + if data.AutomationTrigger.JqCondition != nil { + automationTrigger.Condition = &cli.TriggerCondition{ + Type: consts.JqCondition, + Expressions: flex.TerraformStringListToGoArray(data.AutomationTrigger.JqCondition.Expressions), + Combinator: data.AutomationTrigger.JqCondition.Combinator.ValueStringPointer(), + } + } + + if data.AutomationTrigger.EntityCreatedEvent != nil { + automationTrigger.Event = &cli.TriggerEvent{ + Type: consts.EntityCreated, + BlueprintIdentifier: data.AutomationTrigger.EntityCreatedEvent.BlueprintIdentifier.ValueStringPointer(), + } + } + + if data.AutomationTrigger.EntityUpdatedEvent != nil { + automationTrigger.Event = &cli.TriggerEvent{ + Type: consts.EntityUpdated, + BlueprintIdentifier: data.AutomationTrigger.EntityUpdatedEvent.BlueprintIdentifier.ValueStringPointer(), + } + } + + if data.AutomationTrigger.EntityDeletedEvent != nil { + automationTrigger.Event = &cli.TriggerEvent{ + Type: consts.EntityDeleted, + BlueprintIdentifier: data.AutomationTrigger.EntityDeletedEvent.BlueprintIdentifier.ValueStringPointer(), + } + } + + if data.AutomationTrigger.AnyEntityChangeEvent != nil { + automationTrigger.Event = &cli.TriggerEvent{ + Type: consts.AnyEntityChange, + BlueprintIdentifier: data.AutomationTrigger.AnyEntityChangeEvent.BlueprintIdentifier.ValueStringPointer(), + } + } + + if data.AutomationTrigger.TimerPropertyExpiredEvent != nil { + automationTrigger.Event = &cli.TriggerEvent{ + Type: consts.TimerPropertyExpired, + BlueprintIdentifier: data.AutomationTrigger.TimerPropertyExpiredEvent.BlueprintIdentifier.ValueStringPointer(), + PropertyIdentifier: data.AutomationTrigger.TimerPropertyExpiredEvent.PropertyIdentifier.ValueStringPointer(), + } + } + + return automationTrigger, nil + } + return nil, nil } @@ -257,5 +310,41 @@ func invocationMethodToBody(ctx context.Context, data *ActionModel) (*cli.Invoca return azureInvocation, nil } + if data.UpsertEntityMethod != nil { + var mapping cli.MappingSchema + if data.UpsertEntityMethod.Mapping != nil { + var team interface{} + if data.UpsertEntityMethod.Mapping.Teams != nil { + team = flex.TerraformStringListToGoArray(data.UpsertEntityMethod.Mapping.Teams) + } + properties, err := utils.TerraformJsonStringToGoObject(data.UpsertEntityMethod.Mapping.Properties.ValueStringPointer()) + if err != nil { + return nil, err + } + + relations, err := utils.TerraformJsonStringToGoObject(data.UpsertEntityMethod.Mapping.Relations.ValueStringPointer()) + if err != nil { + return nil, err + } + + mapping = cli.MappingSchema{ + Team: team, + Identifier: data.UpsertEntityMethod.Mapping.Identifier.ValueStringPointer(), + Title: data.UpsertEntityMethod.Title.ValueStringPointer(), + Icon: data.UpsertEntityMethod.Mapping.Icon.ValueStringPointer(), + Properties: *properties, + Relations: *relations, + } + } + + upsertEntityInvocation := &cli.InvocationMethod{ + Type: consts.UpsertEntity, + BlueprintIdentifier: data.UpsertEntityMethod.BlueprintIdentifier.ValueStringPointer(), + Mapping: &mapping, + } + + return upsertEntityInvocation, nil + } + return nil, nil } diff --git a/port/action/model.go b/port/action/model.go index 81c76a35..db7180a0 100644 --- a/port/action/model.go +++ b/port/action/model.go @@ -304,6 +304,15 @@ type JqConditionModel struct { Combinator types.String `tfsdk:"combinator"` } +type AutomationTriggerModel struct { + EntityCreatedEvent *EntityCreatedEventModel `tfsdk:"entity_created_event"` + EntityUpdatedEvent *EntityUpdatedEventModel `tfsdk:"entity_updated_event"` + EntityDeletedEvent *EntityDeletedEventModel `tfsdk:"entity_deleted_event"` + AnyEntityChangeEvent *AnyEntityChangeEventModel `tfsdk:"any_entity_change_event"` + TimerPropertyExpiredEvent *TimerPropertyExpiredEventModel `tfsdk:"timer_property_expired_event"` + JqCondition *JqConditionModel `tfsdk:"jq_condition"` +} + type KafkaMethodModel struct { Payload types.String `tfsdk:"payload"` } @@ -338,6 +347,20 @@ type AzureMethodModel struct { Payload types.String `tfsdk:"payload"` } +type MappingModel struct { + Properties types.String `tfsdk:"properties"` + Relations types.String `tfsdk:"relations"` + Identifier types.String `tfsdk:"identifier"` + Teams []types.String `tfsdk:"teams"` + Icon types.String `tfsdk:"icon"` +} + +type UpsertEntityMethodModel struct { + Title types.String `tfsdk:"title"` + BlueprintIdentifier types.String `tfsdk:"blueprint_identifier"` + Mapping *MappingModel `tfsdk:"mapping"` +} + type ApprovalWebhookNotificationModel struct { Url types.String `tfsdk:"url"` Format types.String `tfsdk:"format"` @@ -351,11 +374,13 @@ type ActionModel struct { Icon types.String `tfsdk:"icon"` Description types.String `tfsdk:"description"` SelfServiceTrigger *SelfServiceTriggerModel `tfsdk:"self_service_trigger"` + AutomationTrigger *AutomationTriggerModel `tfsdk:"automation_trigger"` KafkaMethod *KafkaMethodModel `tfsdk:"kafka_method"` WebhookMethod *WebhookMethodModel `tfsdk:"webhook_method"` GithubMethod *GithubMethodModel `tfsdk:"github_method"` GitlabMethod *GitlabMethodModel `tfsdk:"gitlab_method"` AzureMethod *AzureMethodModel `tfsdk:"azure_method"` + UpsertEntityMethod *UpsertEntityMethodModel `tfsdk:"upsert_entity_method"` RequiredApproval types.Bool `tfsdk:"required_approval"` ApprovalWebhookNotification *ApprovalWebhookNotificationModel `tfsdk:"approval_webhook_notification"` ApprovalEmailNotification types.Object `tfsdk:"approval_email_notification"` @@ -371,11 +396,13 @@ type ActionValidationModel struct { Icon types.String `tfsdk:"icon"` Description types.String `tfsdk:"description"` SelfServiceTrigger types.Object `tfsdk:"self_service_trigger"` + AutomationTrigger types.Object `tfsdk:"automation_trigger"` KafkaMethod types.Object `tfsdk:"kafka_method"` WebhookMethod types.Object `tfsdk:"webhook_method"` GithubMethod types.Object `tfsdk:"github_method"` GitlabMethod types.Object `tfsdk:"gitlab_method"` AzureMethod types.Object `tfsdk:"azure_method"` + UpsertEntityMethod types.Object `tfsdk:"upsert_entity_method"` RequiredApproval types.Bool `tfsdk:"required_approval"` ApprovalWebhookNotification types.Object `tfsdk:"approval_webhook_notification"` ApprovalEmailNotification types.Object `tfsdk:"approval_email_notification"` diff --git a/port/action/refreshActionState.go b/port/action/refreshActionState.go index 4c022952..0c45a809 100644 --- a/port/action/refreshActionState.go +++ b/port/action/refreshActionState.go @@ -98,6 +98,39 @@ func writeInvocationMethodToResource(ctx context.Context, a *cli.Action, state * } } + if a.InvocationMethod.Type == consts.UpsertEntity { + var teams []types.String + switch team := a.InvocationMethod.Mapping.Team.(type) { + case string: + teams = append(teams, types.StringValue(team)) + case []interface{}: + teams = make([]types.String, 0) + for _, t := range team { + teams = append(teams, types.StringValue(t.(string))) + } + } + properties, err := utils.GoObjectToTerraformString(a.InvocationMethod.Mapping.Properties) + if err != nil { + return err + } + relations, err := utils.GoObjectToTerraformString(a.InvocationMethod.Mapping.Relations) + if err != nil { + return err + } + + state.UpsertEntityMethod = &UpsertEntityMethodModel{ + Title: flex.GoStringToFramework(a.InvocationMethod.Mapping.Title), + BlueprintIdentifier: types.StringValue(*a.InvocationMethod.BlueprintIdentifier), + Mapping: &MappingModel{ + Properties: properties, + Relations: relations, + Icon: flex.GoStringToFramework(a.InvocationMethod.Mapping.Icon), + Teams: teams, + Identifier: types.StringValue(*a.InvocationMethod.Mapping.Identifier), + }, + } + } + return nil } @@ -296,14 +329,62 @@ func writeTriggerToResource(ctx context.Context, a *cli.Action, state *ActionMod RequiredJqQuery: requiredJqQuery, OrderProperties: orderProperties, } + + if a.Trigger.Condition != nil { + triggerCondition, err := json.Marshal(a.Trigger.Condition) + if err != nil { + return err + } + state.SelfServiceTrigger.Condition = types.StringValue(string(triggerCondition)) + } } - if a.Trigger.Condition != nil { - triggerCondition, err := json.Marshal(a.Trigger.Condition) - if err != nil { - return err + if a.Trigger.Type == consts.Automation { + automationTrigger := &AutomationTriggerModel{} + + var expressions []types.String + if a.Trigger.Condition != nil { + for _, e := range a.Trigger.Condition.Expressions { + expressions = append(expressions, types.StringValue(e)) + } + automationTrigger.JqCondition = &JqConditionModel{ + Expressions: expressions, + Combinator: flex.GoStringToFramework(a.Trigger.Condition.Combinator), + } + } + + if a.Trigger.Event.Type == consts.EntityCreated { + automationTrigger.EntityCreatedEvent = &EntityCreatedEventModel{ + BlueprintIdentifier: types.StringValue(*a.Trigger.Event.BlueprintIdentifier), + } + } + + if a.Trigger.Event.Type == consts.EntityUpdated { + automationTrigger.EntityUpdatedEvent = &EntityUpdatedEventModel{ + BlueprintIdentifier: types.StringValue(*a.Trigger.Event.BlueprintIdentifier), + } + } + + if a.Trigger.Event.Type == consts.EntityDeleted { + automationTrigger.EntityDeletedEvent = &EntityDeletedEventModel{ + BlueprintIdentifier: types.StringValue(*a.Trigger.Event.BlueprintIdentifier), + } + } + + if a.Trigger.Event.Type == consts.AnyEntityChange { + automationTrigger.AnyEntityChangeEvent = &AnyEntityChangeEventModel{ + BlueprintIdentifier: types.StringValue(*a.Trigger.Event.BlueprintIdentifier), + } } - state.SelfServiceTrigger.Condition = types.StringValue(string(triggerCondition)) + + if a.Trigger.Event.Type == consts.TimerPropertyExpired { + automationTrigger.TimerPropertyExpiredEvent = &TimerPropertyExpiredEventModel{ + BlueprintIdentifier: types.StringValue(*a.Trigger.Event.BlueprintIdentifier), + PropertyIdentifier: types.StringValue(*a.Trigger.Event.PropertyIdentifier), + } + } + + state.AutomationTrigger = automationTrigger } return nil diff --git a/port/action/resource_test.go b/port/action/resource_test.go index ff49b227..e3f5da52 100644 --- a/port/action/resource_test.go +++ b/port/action/resource_test.go @@ -398,6 +398,56 @@ func TestAccPortActionAzureInvocation(t *testing.T) { }) } +func TestAccPortActionUpsertEntityInvocation(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 + } + upsert_entity_method = { + title = "Test Entity" + blueprint_identifier = port_blueprint.microservice.identifier + mapping = { + identifier = "test-entity" + teams = [] + icon = "Terraform" + properties = jsonencode({"text": "test"}) + relations = jsonencode({"test-rel": "target-bp"}) + } + } + }`, 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", "upsert_entity_method.title", "Test Entity"), + resource.TestCheckResourceAttr("port_action.create_microservice", "upsert_entity_method.blueprint_identifier", identifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "upsert_entity_method.mapping.identifier", "test-entity"), + resource.TestCheckResourceAttr("port_action.create_microservice", "upsert_entity_method.mapping.teams.#", "0"), + resource.TestCheckResourceAttr("port_action.create_microservice", "upsert_entity_method.mapping.icon", "Terraform"), + resource.TestCheckResourceAttr("port_action.create_microservice", "upsert_entity_method.mapping.properties", "{\"text\":\"test\"}"), + resource.TestCheckResourceAttr("port_action.create_microservice", "upsert_entity_method.mapping.relations", "{\"test-rel\":\"target-bp\"}"), + ), + }, + }, + }) +} + func TestAccPortActionImport(t *testing.T) { blueprintIdentifier := utils.GenID() actionIdentifier := utils.GenID() @@ -1355,6 +1405,168 @@ func TestAccPortActionRequiredFalseAndNull(t *testing.T) { }) } +func TestAccPortAutomationEntityCreated(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" + automation_trigger = { + entity_created_event = { + blueprint_identifier = port_blueprint.microservice.identifier + } + } + 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", "automation_trigger.entity_created_event.blueprint_identifier", identifier), + ), + }, + }, + }) +} + +func TestAccPortAutomationEntityUpdated(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" + automation_trigger = { + entity_updated_event = { + blueprint_identifier = port_blueprint.microservice.identifier + } + } + 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", "automation_trigger.entity_updated_event.blueprint_identifier", identifier), + ), + }, + }, + }) +} + +func TestAccPortAutomationEntityDeleted(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" + automation_trigger = { + entity_deleted_event = { + blueprint_identifier = port_blueprint.microservice.identifier + } + } + 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", "automation_trigger.entity_deleted_event.blueprint_identifier", identifier), + ), + }, + }, + }) +} + +func TestAccPortAutomationAnyEntityChange(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" + automation_trigger = { + any_entity_change_event = { + blueprint_identifier = port_blueprint.microservice.identifier + } + } + 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", "automation_trigger.any_entity_change_event.blueprint_identifier", identifier), + ), + }, + }, + }) +} + +func TestAccPortAutomationTimerPropertyExpired(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" + automation_trigger = { + timer_property_expired_event = { + blueprint_identifier = port_blueprint.microservice.identifier + property_identifier = "timer" + } + } + 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", "automation_trigger.timer_property_expired_event.blueprint_identifier", identifier), + resource.TestCheckResourceAttr("port_action.create_microservice", "automation_trigger.timer_property_expired_event.property_identifier", "timer"), + ), + }, + }, + }) +} + func TestAccPortWebhookApproval(t *testing.T) { identifier := utils.GenID() actionIdentifier := utils.GenID() diff --git a/port/action/schema.go b/port/action/schema.go index fc4c8c8b..a9b2f4b6 100644 --- a/port/action/schema.go +++ b/port/action/schema.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "regexp" @@ -136,9 +137,99 @@ func ActionSchema() map[string]schema.Attribute { Validators: []validator.Object{ objectvalidator.ExactlyOneOf( path.MatchRoot("self_service_trigger"), + path.MatchRoot("automation_trigger"), ), }, }, + "automation_trigger": schema.SingleNestedAttribute{ + MarkdownDescription: "Automation trigger for the action", + Optional: true, + Attributes: map[string]schema.Attribute{ + "entity_created_event": schema.SingleNestedAttribute{ + MarkdownDescription: "Entity created event trigger", + Optional: true, + Attributes: map[string]schema.Attribute{ + "blueprint_identifier": schema.StringAttribute{ + MarkdownDescription: "The blueprint identifier of the created entity", + Required: true, + }, + }, + Validators: []validator.Object{ + objectvalidator.ExactlyOneOf( + path.MatchRelative().AtParent().AtName("entity_created_event"), + path.MatchRelative().AtParent().AtName("entity_updated_event"), + path.MatchRelative().AtParent().AtName("entity_deleted_event"), + path.MatchRelative().AtParent().AtName("any_entity_change_event"), + path.MatchRelative().AtParent().AtName("timer_property_expired_event"), + ), + }, + }, + "entity_updated_event": schema.SingleNestedAttribute{ + MarkdownDescription: "Entity updated event trigger", + Optional: true, + Attributes: map[string]schema.Attribute{ + "blueprint_identifier": schema.StringAttribute{ + MarkdownDescription: "The blueprint identifier of the updated entity", + Required: true, + }, + }, + }, + "entity_deleted_event": schema.SingleNestedAttribute{ + MarkdownDescription: "Entity deleted event trigger", + Optional: true, + Attributes: map[string]schema.Attribute{ + "blueprint_identifier": schema.StringAttribute{ + MarkdownDescription: "The blueprint identifier of the deleted entity", + Required: true, + }, + }, + }, + "any_entity_change_event": schema.SingleNestedAttribute{ + MarkdownDescription: "Any entity change event trigger", + Optional: true, + Attributes: map[string]schema.Attribute{ + "blueprint_identifier": schema.StringAttribute{ + MarkdownDescription: "The blueprint identifier of the changed entity", + Required: true, + }, + }, + }, + "timer_property_expired_event": schema.SingleNestedAttribute{ + MarkdownDescription: "Timer property expired event trigger", + Optional: true, + Attributes: map[string]schema.Attribute{ + "blueprint_identifier": schema.StringAttribute{ + MarkdownDescription: "The blueprint identifier of the expired timer property", + Required: true, + }, + "property_identifier": schema.StringAttribute{ + MarkdownDescription: "The property identifier of the expired timer property", + Required: true, + }, + }, + }, + "jq_condition": schema.SingleNestedAttribute{ + MarkdownDescription: "JQ condition for automation trigger", + Optional: true, + Attributes: map[string]schema.Attribute{ + "expressions": schema.ListAttribute{ + MarkdownDescription: "The jq expressions of the condition", + ElementType: types.StringType, + Required: true, + }, + "combinator": schema.StringAttribute{ + MarkdownDescription: "The combinator of the condition", + Optional: true, + Computed: true, + Default: stringdefault.StaticString("and"), + Validators: []validator.String{ + stringvalidator.OneOf("and", "or"), + }, + }, + }, + }, + }, + }, "kafka_method": schema.SingleNestedAttribute{ MarkdownDescription: "Kafka invocation method", Optional: true, @@ -155,6 +246,7 @@ func ActionSchema() map[string]schema.Attribute { path.MatchRoot("github_method"), path.MatchRoot("gitlab_method"), path.MatchRoot("azure_method"), + path.MatchRoot("upsert_entity_method"), ), }, }, @@ -258,6 +350,47 @@ func ActionSchema() map[string]schema.Attribute { }, }, }, + "upsert_entity_method": schema.SingleNestedAttribute{ + MarkdownDescription: "Upsert Entity invocation method", + Optional: true, + Attributes: map[string]schema.Attribute{ + "title": schema.StringAttribute{ + MarkdownDescription: "The title of the entity", + Optional: true, + }, + "blueprint_identifier": schema.StringAttribute{ + MarkdownDescription: "Required when selecting type Upsert Entity. The blueprint identifier of the entity for the upsert", + Required: true, + }, + "mapping": schema.SingleNestedAttribute{ + MarkdownDescription: "Upsert Entity invocation method", + Optional: true, + Attributes: map[string]schema.Attribute{ + "identifier": schema.StringAttribute{ + MarkdownDescription: "Required when selecting type Upsert Entity. The entity identifier for the upsert", + Required: true, + }, + "teams": schema.ListAttribute{ + MarkdownDescription: "The teams the entity belongs to", + ElementType: types.StringType, + Optional: true, + }, + "icon": schema.StringAttribute{ + MarkdownDescription: "The icon of the entity", + Optional: true, + }, + "properties": schema.StringAttribute{ + MarkdownDescription: "The properties of the entity (key-value object encoded to a string)", + Optional: true, + }, + "relations": schema.StringAttribute{ + MarkdownDescription: "The relations of the entity (key-value object encoded to a string)", + Optional: true, + }, + }, + }, + }, + }, "required_approval": schema.BoolAttribute{ MarkdownDescription: "Require approval before invoking the action", Optional: true, @@ -972,6 +1105,28 @@ resource "port_action" "create_microservice" { runId: "{{"{{.run.id}}"}}" }) } +}` + "\n```" + ` + +## Example Usage with Automation trigger + +Port allows setting an automation trigger to an action, for executing an action based on event occurred to an entity in Port. + +` + "```hcl" + ` +resource "port_action" "delete_temporary_microservice" { + title = "Delete Temporary Microservice" + identifier = "delete-temp-microservice" + icon = "Terraform" + automation_trigger = { + timer_property_expired_event = { + blueprint_identifier = port_blueprint.microservice.identifier + property_identifier = "ttl" + } + } + kafka_method = { + payload = jsonencode({ + runId: "{{"{{.run.id}}"}}" + }) + } } ` + "\n```" + `