diff --git a/.github/labeler.yml b/.github/labeler.yml index 4f50a63b..ecc7ab50 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -74,6 +74,10 @@ resource/auth0_tenant: - '**/*tenant.go' - '**/*tenant_test.go' +resource/auth0_trigger_binding: + - '**/*trigger_binding.go' + - '**/*trigger_binding_test.go' + resource/auth0_user: - '**/*user.go' - '**/*user_test.go' diff --git a/CHANGELOG.md b/CHANGELOG.md index fb948b60..ce840533 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ENHANCEMENTS: * **New Resource:** `auth0_trigger_binding` a.k.a Action Flow ([#481](https://github.com/alexkappa/terraform-provider-auth0/pull/481)) * resource/auth0_connection: Add `entity_id` field for SAMLP connections ([#468](https://github.com/alexkappa/terraform-provider-auth0/pull/468)) +* resource/auth0_client_grant: Update import documentation ([#471](https://github.com/alexkappa/terraform-provider-auth0/pull/471)) ## 0.24.3 diff --git a/auth0/provider.go b/auth0/provider.go index 3e1e69fe..cfc90998 100644 --- a/auth0/provider.go +++ b/auth0/provider.go @@ -66,6 +66,7 @@ func init() { "auth0_guardian": newGuardian(), "auth0_organization": newOrganization(), "auth0_action": newAction(), + "auth0_trigger_binding": newTriggerBinding(), }, ConfigureFunc: Configure, } diff --git a/auth0/resource_auth0_trigger_binding.go b/auth0/resource_auth0_trigger_binding.go new file mode 100644 index 00000000..cfbe975a --- /dev/null +++ b/auth0/resource_auth0_trigger_binding.go @@ -0,0 +1,142 @@ +package auth0 + +import ( + "net/http" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + + "gopkg.in/auth0.v5" + "gopkg.in/auth0.v5/management" +) + +func newTriggerBinding() *schema.Resource { + return &schema.Resource{ + + Create: createTriggerBinding, + Read: readTriggerBinding, + Update: updateTriggerBinding, + Delete: deleteTriggerBinding, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "trigger": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "post-login", + "credentials-exchange", + "pre-user-registration", + "post-user-registration", + "post-change-password", + "send-phone-message", + "iga-approval", + "iga-certification", + "iga-fulfillment-assignment", + "iga-fulfillment-execution", + }, false), + Description: "The id of the trigger to bind with", + }, + "actions": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + Description: "Trigger ID", + }, + "display_name": { + Type: schema.TypeString, + Required: true, + Description: "The name of an action", + }, + }, + }, + Description: "The actions bound to this trigger", + }, + }, + } +} + +func createTriggerBinding(d *schema.ResourceData, m interface{}) error { + api := m.(*management.Management) + id := d.Get("trigger").(string) + b := expandTriggerBindings(d) + err := api.Action.UpdateBindings(id, b) + if err != nil { + return err + } + d.SetId(id) + return readTriggerBinding(d, m) +} + +func readTriggerBinding(d *schema.ResourceData, m interface{}) error { + api := m.(*management.Management) + b, err := api.Action.Bindings(d.Id()) + if err != nil { + if mErr, ok := err.(management.Error); ok { + if mErr.Status() == http.StatusNotFound { + d.SetId("") + return nil + } + } + return err + } + + d.Set("actions", flattenTriggerBindingActions(b.Bindings)) + + return nil +} + +func updateTriggerBinding(d *schema.ResourceData, m interface{}) error { + b := expandTriggerBindings(d) + api := m.(*management.Management) + err := api.Action.UpdateBindings(d.Id(), b) + if err != nil { + return err + } + return readTriggerBinding(d, m) +} + +func deleteTriggerBinding(d *schema.ResourceData, m interface{}) error { + api := m.(*management.Management) + if err := api.Action.UpdateBindings(d.Id(), []*management.ActionBinding{}); err != nil { + if mErr, ok := err.(management.Error); ok { + if mErr.Status() == http.StatusNotFound { + d.SetId("") + return nil + } + } + return err + } + return nil +} + +func expandTriggerBindings(d *schema.ResourceData) (b []*management.ActionBinding) { + List(d, "actions").Elem(func(d ResourceData) { + b = append(b, &management.ActionBinding{ + Ref: &management.ActionBindingReference{ + Type: auth0.String("action_id"), + Value: String(d, "id"), + }, + DisplayName: String(d, "display_name"), + }) + }) + return +} + +func flattenTriggerBindingActions(bindings []*management.ActionBinding) (r []interface{}) { + for _, b := range bindings { + r = append(r, map[string]interface{}{ + "id": b.Action.GetID(), + "display_name": b.GetDisplayName(), + }) + } + return +} diff --git a/auth0/resource_auth0_trigger_binding_test.go b/auth0/resource_auth0_trigger_binding_test.go new file mode 100644 index 00000000..b8d35d14 --- /dev/null +++ b/auth0/resource_auth0_trigger_binding_test.go @@ -0,0 +1,103 @@ +package auth0 + +import ( + "testing" + + "github.com/alexkappa/terraform-provider-auth0/auth0/internal/random" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccTriggerBinding(t *testing.T) { + + rand := random.String(6) + + resource.Test(t, resource.TestCase{ + Providers: map[string]terraform.ResourceProvider{ + "auth0": Provider(), + }, + Steps: []resource.TestStep{ + { + Config: random.Template(testAccTriggerBindingConfigCreate, rand), + Check: resource.ComposeTestCheckFunc( + random.TestCheckResourceAttr("auth0_action.action_foo", "name", "Test Trigger Binding Foo {{.random}}", rand), + random.TestCheckResourceAttr("auth0_action.action_bar", "name", "Test Trigger Binding Bar {{.random}}", rand), + resource.TestCheckResourceAttr("auth0_trigger_binding.login_flow", "actions.#", "2"), + random.TestCheckResourceAttr("auth0_trigger_binding.login_flow", "actions.0.display_name", "Test Trigger Binding Foo {{.random}}", rand), + random.TestCheckResourceAttr("auth0_trigger_binding.login_flow", "actions.1.display_name", "Test Trigger Binding Bar {{.random}}", rand), + ), + }, + { + Config: random.Template(testAccTriggerBindingConfigUpdate, rand), + Check: resource.ComposeTestCheckFunc( + random.TestCheckResourceAttr("auth0_action.action_foo", "name", "Test Trigger Binding Foo {{.random}}", rand), + random.TestCheckResourceAttr("auth0_action.action_bar", "name", "Test Trigger Binding Bar {{.random}}", rand), + resource.TestCheckResourceAttr("auth0_trigger_binding.login_flow", "actions.#", "2"), + random.TestCheckResourceAttr("auth0_trigger_binding.login_flow", "actions.0.display_name", "Test Trigger Binding Bar {{.random}}", rand), + random.TestCheckResourceAttr("auth0_trigger_binding.login_flow", "actions.1.display_name", "Test Trigger Binding Foo {{.random}}", rand), + ), + }, + }, + }) +} + +const testAccTriggerBindingAction = ` + +resource auth0_action action_foo { + name = "Test Trigger Binding Foo {{.random}}" + supported_triggers { + id = "post-login" + version = "v2" + } + code = <<-EOT + exports.onContinuePostLogin = async (event, api) => { + console.log("foo") + };" + EOT + deploy = true +} + +resource auth0_action action_bar { + name = "Test Trigger Binding Bar {{.random}}" + supported_triggers { + id = "post-login" + version = "v2" + } + code = <<-EOT + exports.onContinuePostLogin = async (event, api) => { + console.log("bar") + };" + EOT + deploy = true +} +` + +const testAccTriggerBindingConfigCreate = testAccTriggerBindingAction + ` + +resource auth0_trigger_binding login_flow { + trigger = "post-login" + actions { + id = auth0_action.action_foo.id + display_name = auth0_action.action_foo.name + } + actions { + id = auth0_action.action_bar.id + display_name = auth0_action.action_bar.name + } +} +` + +const testAccTriggerBindingConfigUpdate = testAccTriggerBindingAction + ` + +resource auth0_trigger_binding login_flow { + trigger = "post-login" + actions { + id = auth0_action.action_bar.id # <----- change the order of the actions + display_name = auth0_action.action_bar.name + } + actions { + id = auth0_action.action_foo.id + display_name = auth0_action.action_foo.name + } +} +` diff --git a/docs/resources/trigger_binding.md b/docs/resources/trigger_binding.md new file mode 100644 index 00000000..6b436b53 --- /dev/null +++ b/docs/resources/trigger_binding.md @@ -0,0 +1,82 @@ +--- +layout: "auth0" +page_title: "Auth0: auth0_trigger_binding" +description: |- + With this resource, you can bind an action to a trigger. Once an action is + created and deployed, it can be attached (i.e. bound) to a trigger so that it + will be executed as part of a flow. +--- + +# auth0_trigger_binding + +With this resource, you can bind an action to a trigger. Once an action is +created and deployed, it can be attached (i.e. bound) to a trigger so that it +will be executed as part of a flow. + +The list of actions reflects the order in which they will be executed during the +appropriate flow. + +## Example Usage + +```hcl +resource auth0_action action_foo { + name = "Test Trigger Binding Foo {{.random}}" + supported_triggers { + id = "post-login" + version = "v2" + } + code = <<-EOT + exports.onContinuePostLogin = async (event, api) => { + console.log("foo") + };" + EOT + deploy = true +} + +resource auth0_action action_bar { + name = "Test Trigger Binding Bar {{.random}}" + supported_triggers { + id = "post-login" + version = "v2" + } + code = <<-EOT + exports.onContinuePostLogin = async (event, api) => { + console.log("bar") + };" + EOT + deploy = true +} + +resource auth0_trigger_binding login_flow { + trigger = "post-login" + actions { + id = auth0_action.action_foo.id + display_name = auth0_action.action_foo.name + } + actions { + id = auth0_action.action_bar.id + display_name = auth0_action.action_bar.name + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `trigger` - (Required) The id of the trigger to bind with +* `actions` - (Required) The actions bound to this trigger. For details, see + [Actions](#actions). + +### Actions + +* `id` - (Required) Trigger ID. +* `display_name` - (Required) The name of an action. + +## Import + +auth0_trigger_binding can be imported using the bindings trigger ID, e.g. + +``` +$ terraform import auth0_trigger_binding.example "post-login" +```