alexkappa/terraform-provider-auth0

View on GitHub
auth0/resource_auth0_organization.go

Summary

Maintainability
A
3 hrs
Test Coverage
B
86%
package auth0

import (
    "fmt"
    "log"
    "net/http"

    "github.com/alexkappa/terraform-provider-auth0/auth0/internal/hash"
    "github.com/hashicorp/terraform-plugin-sdk/helper/schema"
    "gopkg.in/auth0.v5/management"
)

func newOrganization() *schema.Resource {
    return &schema.Resource{

        Create: createOrganization,
        Read:   readOrganization,
        Update: updateOrganization,
        Delete: deleteOrganization,

        Importer: &schema.ResourceImporter{
            State: schema.ImportStatePassthrough,
        },

        Schema: map[string]*schema.Schema{
            "name": {
                Type:        schema.TypeString,
                Required:    true,
                Description: "The name of this organization",
            },
            "display_name": {
                Type:        schema.TypeString,
                Optional:    true,
                Description: "Friendly name of this organization",
            },
            "branding": {
                Type:        schema.TypeList,
                Optional:    true,
                Computed:    true,
                MaxItems:    1,
                MinItems:    1,
                Description: "Defines how to style the login pages",
                Elem: &schema.Resource{
                    Schema: map[string]*schema.Schema{
                        "logo_url": {
                            Type:     schema.TypeString,
                            Optional: true,
                        },
                        "colors": {
                            Type:     schema.TypeMap,
                            Optional: true,
                            Elem:     &schema.Schema{Type: schema.TypeString},
                        },
                    },
                },
            },
            "metadata": {
                Type:        schema.TypeMap,
                Optional:    true,
                Elem:        &schema.Schema{Type: schema.TypeString},
                Description: "Metadata associated with the organization, Maximum of 10 metadata properties allowed",
            },
            "connections": {
                Type:     schema.TypeSet,
                Optional: true,
                Computed: true,
                Elem: &schema.Resource{
                    Schema: map[string]*schema.Schema{
                        "connection_id": {
                            Type:     schema.TypeString,
                            Required: true,
                        },
                        "assign_membership_on_login": {
                            Type:     schema.TypeBool,
                            Optional: true,
                        },
                    },
                },
                Set: hash.StringKey("connection_id"),
            },
        },
    }
}

func createOrganization(d *schema.ResourceData, m interface{}) error {
    o := expandOrganization(d)
    api := m.(*management.Management)
    if err := api.Organization.Create(o); err != nil {
        return err
    }
    d.SetId(o.GetID())

    d.Partial(true)
    err := assignOrganizationConnections(d, m)
    if err != nil {
        return fmt.Errorf("failed assigning organization connections. %w", err)
    }
    d.Partial(false)

    return readOrganization(d, m)
}

func assignOrganizationConnections(d *schema.ResourceData, m interface{}) (err error) {

    api := m.(*management.Management)

    add, rm := Diff(d, "connections")

    add.Elem(func(dd ResourceData) {
        c := &management.OrganizationConnection{
            ConnectionID:            String(dd, "connection_id"),
            AssignMembershipOnLogin: Bool(dd, "assign_membership_on_login"),
        }
        log.Printf("[DEBUG] (+) auth0_organization.%s.connections.%s", d.Id(), c.GetConnectionID())
        err = api.Organization.AddConnection(d.Id(), c)
        if err != nil {
            return
        }
    })

    rm.Elem(func(dd ResourceData) {
        // Take connectionID before it changed (i.e. removed). Therefore we use
        // GetChange() instead of the typical Get().
        connectionID, _ := dd.GetChange("connection_id")
        log.Printf("[DEBUG] (-) auth0_organization.%s.connections.%s", d.Id(), connectionID.(string))
        err = api.Organization.DeleteConnection(d.Id(), connectionID.(string))
        if err != nil {
            return
        }
    })

    // Update existing connections if any mutable properties have changed.
    Set(d, "connections", HasChange()).Elem(func(dd ResourceData) {
        connectionID := dd.Get("connection_id").(string)
        c := &management.OrganizationConnection{
            AssignMembershipOnLogin: Bool(dd, "assign_membership_on_login"),
        }
        log.Printf("[DEBUG] (~) auth0_organization.%s.connections.%s", d.Id(), connectionID)
        err = api.Organization.UpdateConnection(d.Id(), connectionID, c)
        if err != nil {
            return
        }
    })

    return nil
}

func readOrganization(d *schema.ResourceData, m interface{}) error {
    api := m.(*management.Management)
    o, err := api.Organization.Read(d.Id())
    if err != nil {
        if mErr, ok := err.(management.Error); ok {
            if mErr.Status() == http.StatusNotFound {
                d.SetId("")
                return nil
            }
        }
        return err
    }

    d.SetId(o.GetID())
    d.Set("name", o.Name)
    d.Set("display_name", o.DisplayName)
    d.Set("branding", flattenOrganizationBranding(o.Branding))
    d.Set("metadata", o.Metadata)

    l, err := api.Organization.Connections(d.Id())
    if err != nil {
        return err
    }

    d.Set("connections", func() (v []interface{}) {
        for _, connection := range l.OrganizationConnections {
            v = append(v, &map[string]interface{}{
                "connection_id":              connection.ConnectionID,
                "assign_membership_on_login": connection.AssignMembershipOnLogin,
            })
        }
        return
    }())

    return nil
}

func updateOrganization(d *schema.ResourceData, m interface{}) error {
    o := expandOrganization(d)
    api := m.(*management.Management)
    err := api.Organization.Update(d.Id(), o)
    if err != nil {
        return err
    }

    d.Partial(true)
    err = assignOrganizationConnections(d, m)
    if err != nil {
        return fmt.Errorf("failed updating organization connections. %w", err)
    }
    d.Partial(false)

    return readOrganization(d, m)
}

func deleteOrganization(d *schema.ResourceData, m interface{}) error {
    api := m.(*management.Management)
    err := api.Organization.Delete(d.Id())
    if err != nil {
        if mErr, ok := err.(management.Error); ok {
            if mErr.Status() == http.StatusNotFound {
                d.SetId("")
                return nil
            }
        }
    }
    return err
}

func expandOrganization(d *schema.ResourceData) *management.Organization {
    o := &management.Organization{
        Name:        String(d, "name"),
        DisplayName: String(d, "display_name"),
        Metadata:    Map(d, "metadata"),
    }
    List(d, "branding").Elem(func(d ResourceData) {
        o.Branding = &management.OrganizationBranding{
            LogoURL: String(d, "logo_url"),
            Colors:  Map(d, "colors"),
        }
    })
    return o
}

func flattenOrganizationBranding(b *management.OrganizationBranding) []interface{} {
    m := make(map[string]interface{})
    if b != nil {
        m["logo_url"] = b.LogoURL
        m["colors"] = b.Colors
    }
    return []interface{}{m}
}