Repository: mrparkers/terraform-provider-keycloak
Pull request #124 (2019-06-13 14:06:54)

Realm internationalization

tomrutsaert: @mrparkers I do not see ant reason why test-password ci failed or how it is related to my changes.

Patch:
diff --git a/example/main.tf b/example/main.tf
index ae0d8e7..2ee92b3 100644
--- a/example/main.tf
+++ b/example/main.tf
@@ -27,6 +27,11 @@ resource "keycloak_realm" "test" {
   account_theme = "base"
 
   access_code_lifespan = "30m"
+
+	internationalization {
+		supported_locales = ["en", "de", "es"]
+		default_locale = "es"
+	}
 }
 
 resource "keycloak_group" "foo" {
diff --git a/keycloak/realm.go b/keycloak/realm.go
index b817ab7..2fd56fb 100644
--- a/keycloak/realm.go
+++ b/keycloak/realm.go
@@ -43,6 +43,11 @@ type Realm struct {
 	AccessCodeLifespanUserAction        int  `json:"accessCodeLifespanUserAction,omitempty"`
 	ActionTokenGeneratedByUserLifespan  int  `json:"actionTokenGeneratedByUserLifespan,omitempty"`
 	ActionTokenGeneratedByAdminLifespan int  `json:"actionTokenGeneratedByAdminLifespan,omitempty"`
+
+	//internationalization
+	InternationalizationEnabled bool     `json:"internationalizationEnabled"`
+	SupportLocales              []string `json:"supportedLocales"`
+	DefaultLocale               string   `json:"defaultLocale"`
 }
 
 type SmtpServer struct {
@@ -126,5 +131,18 @@ func (keycloakClient *KeycloakClient) ValidateRealm(realm *Realm) error {
 		return fmt.Errorf("validation error: theme \"%s\" does not exist on the server", realm.EmailTheme)
 	}
 
+	if realm.InternationalizationEnabled == true && !contains(realm.SupportLocales, realm.DefaultLocale) {
+		return fmt.Errorf("validation error: DefaultLocale should be in the SupportLocales")
+	}
+
 	return nil
 }
+
+func contains(s []string, e string) bool {
+	for _, a := range s {
+		if a == e {
+			return true
+		}
+	}
+	return false
+}
diff --git a/provider/resource_keycloak_realm.go b/provider/resource_keycloak_realm.go
index 2324690..9c91751 100644
--- a/provider/resource_keycloak_realm.go
+++ b/provider/resource_keycloak_realm.go
@@ -229,11 +229,46 @@ func resourceKeycloakRealm() *schema.Resource {
 				Computed:         true,
 				DiffSuppressFunc: suppressDurationStringDiff,
 			},
+
+			//internationalization
+			"internationalization": {
+				Type:     schema.TypeList,
+				Optional: true,
+				MaxItems: 1,
+				Elem: &schema.Resource{
+					Schema: map[string]*schema.Schema{
+						"supported_locales": {
+							Type:     schema.TypeSet,
+							Elem:     &schema.Schema{Type: schema.TypeString},
+							Set:      schema.HashString,
+							Required: true,
+						},
+						"default_locale": {
+							Type:     schema.TypeString,
+							Required: true,
+						},
+					},
+				},
+			},
 		},
 	}
 }
 
 func getRealmFromData(data *schema.ResourceData) (*keycloak.Realm, error) {
+	internationalizationEnabled := false
+	supportLocales := make([]string, 0)
+	defaultLocale := ""
+	if v, ok := data.GetOk("internationalization"); ok {
+		internationalizationEnabled = true
+		internationalizationSettings := v.([]interface{})[0].(map[string]interface{})
+		if v, ok := internationalizationSettings["supported_locales"]; ok {
+			for _, supportLocale := range v.(*schema.Set).List() {
+				supportLocales = append(supportLocales, supportLocale.(string))
+			}
+		}
+		defaultLocale = internationalizationSettings["default_locale"].(string)
+	}
+
 	realm := &keycloak.Realm{
 		Id:          data.Get("realm").(string),
 		Realm:       data.Get("realm").(string),
@@ -249,6 +284,11 @@ func getRealmFromData(data *schema.ResourceData) (*keycloak.Realm, error) {
 		VerifyEmail:                 data.Get("verify_email").(bool),
 		LoginWithEmailAllowed:       data.Get("login_with_email_allowed").(bool),
 		DuplicateEmailsAllowed:      data.Get("duplicate_emails_allowed").(bool),
+
+		//internationalization
+		InternationalizationEnabled: internationalizationEnabled,
+		SupportLocales:              supportLocales,
+		DefaultLocale:               defaultLocale,
 	}
 
 	//smtp
@@ -450,6 +490,16 @@ func setRealmData(data *schema.ResourceData, realm *keycloak.Realm) {
 	data.Set("access_code_lifespan_user_action", getDurationStringFromSeconds(realm.AccessCodeLifespanUserAction))
 	data.Set("action_token_generated_by_user_lifespan", getDurationStringFromSeconds(realm.ActionTokenGeneratedByUserLifespan))
 	data.Set("action_token_generated_by_admin_lifespan", getDurationStringFromSeconds(realm.ActionTokenGeneratedByAdminLifespan))
+
+	//internationalization
+	if realm.InternationalizationEnabled {
+		internationalizationSettings := make(map[string]interface{})
+		internationalizationSettings["supported_locales"] = realm.SupportLocales
+		internationalizationSettings["default_locale"] = realm.DefaultLocale
+		data.Set("internationalization", []interface{}{internationalizationSettings})
+	} else {
+		data.Set("internationalization", nil)
+	}
 }
 
 func resourceKeycloakRealmCreate(data *schema.ResourceData, meta interface{}) error {


Test patch:
diff --git a/provider/resource_keycloak_realm_test.go b/provider/resource_keycloak_realm_test.go
index aa1fc14..2fac175 100644
--- a/provider/resource_keycloak_realm_test.go
+++ b/provider/resource_keycloak_realm_test.go
@@ -210,6 +210,66 @@ func TestAccKeycloakRealm_themesValidation(t *testing.T) {
 	})
 }
 
+func TestAccKeycloakRealm_InternationalizationValidation(t *testing.T) {
+	realm := "terraform-" + acctest.RandString(10)
+
+	resource.Test(t, resource.TestCase{
+		Providers:    testAccProviders,
+		PreCheck:     func() { testAccPreCheck(t) },
+		CheckDestroy: testAccCheckKeycloakRealmDestroy(),
+		Steps: []resource.TestStep{
+			{
+				Config:      testKeycloakRealm_internationalizationValidationWithoutSupportedLocales(realm, "en"),
+				ExpectError: regexp.MustCompile("config is invalid: Missing required argument: The argument \"supported_locales\" is required, but no definition was found."),
+			},
+			{
+				Config:      testKeycloakRealm_internationalizationValidation(realm, "en", "de"),
+				ExpectError: regexp.MustCompile("validation error: DefaultLocale should be in the SupportLocales"),
+			},
+		},
+	})
+}
+
+func TestAccKeycloakRealm_Internationalization(t *testing.T) {
+	realm := "terraform-" + acctest.RandString(10)
+
+	resource.Test(t, resource.TestCase{
+		Providers:    testAccProviders,
+		PreCheck:     func() { testAccPreCheck(t) },
+		CheckDestroy: testAccCheckKeycloakRealmDestroy(),
+		Steps: []resource.TestStep{
+			{
+				Config: testKeycloakRealm_internationalizationValidation(realm, "en", "en"),
+				Check:  testAccCheckKeycloakRealmInternationalizationIsEnabled("keycloak_realm.realm", "en"),
+			},
+			{
+				Config: testKeycloakRealm_internationalizationValidation(realm, "es", "es"),
+				Check:  testAccCheckKeycloakRealmInternationalizationIsEnabled("keycloak_realm.realm", "es"),
+			},
+			{
+				Config: testKeycloakRealm_basic(realm, realm),
+				Check:  testAccCheckKeycloakRealmInternationalizationIsDisabled("keycloak_realm.realm"),
+			},
+		},
+	})
+}
+
+func TestAccKeycloakRealm_InternationalizationDisabled(t *testing.T) {
+	realm := "terraform-" + acctest.RandString(10)
+
+	resource.Test(t, resource.TestCase{
+		Providers:    testAccProviders,
+		PreCheck:     func() { testAccPreCheck(t) },
+		CheckDestroy: testAccCheckKeycloakRealmDestroy(),
+		Steps: []resource.TestStep{
+			{
+				Config: testKeycloakRealm_basic(realm, realm),
+				Check:  testAccCheckKeycloakRealmInternationalizationIsDisabled("keycloak_realm.realm"),
+			},
+		},
+	})
+}
+
 func TestAccKeycloakRealm_loginConfigBasic(t *testing.T) {
 	realm := &keycloak.Realm{
 		Realm:                       "terraform-" + acctest.RandString(10),
@@ -464,6 +524,51 @@ func testAccCheckKeycloakRealmSmtp(resourceName, host, from, user string) resour
 	}
 }
 
+func testAccCheckKeycloakRealmInternationalizationIsEnabled(resourceName string, defaultLocale string) resource.TestCheckFunc {
+	return func(s *terraform.State) error {
+		realm, err := getRealmFromState(s, resourceName)
+		if err != nil {
+			return err
+		}
+
+		if !realm.InternationalizationEnabled {
+			return fmt.Errorf("expected realm %s to have internationalization enabled but was disabled", realm.Realm)
+		}
+
+		if realm.DefaultLocale != defaultLocale {
+			return fmt.Errorf("expected realm %s to have defaultLocale set to %s, but was %s", realm.Realm, defaultLocale, realm.DefaultLocale)
+		}
+
+		if !contains(realm.SupportLocales, defaultLocale) {
+			return fmt.Errorf("expected realm %s to contain defaultLocale %s, but was %s", realm.Realm, defaultLocale, realm.SupportLocales)
+		}
+		return nil
+	}
+}
+
+func testAccCheckKeycloakRealmInternationalizationIsDisabled(resourceName string) resource.TestCheckFunc {
+	return func(s *terraform.State) error {
+		realm, err := getRealmFromState(s, resourceName)
+		if err != nil {
+			return err
+		}
+
+		if realm.InternationalizationEnabled {
+			return fmt.Errorf("expected realm %s to have internationalization disabled but was enabled", realm.Realm)
+		}
+		return nil
+	}
+}
+
+func contains(s []string, e string) bool {
+	for _, a := range s {
+		if a == e {
+			return true
+		}
+	}
+	return false
+}
+
 func testAccCheckKeycloakRealmDestroy() resource.TestCheckFunc {
 	return func(s *terraform.State) error {
 		for _, rs := range s.RootModule().Resources {
@@ -609,6 +714,34 @@ resource "keycloak_realm" "realm" {
 	`, realm, realm, theme, value)
 }
 
+func testKeycloakRealm_internationalizationValidation(realm, supportedLocale, defaultLocale string) string {
+	return fmt.Sprintf(`
+resource "keycloak_realm" "realm" {
+	realm        = "%s"
+	enabled      = true
+	display_name = "%s"
+	internationalization {
+		supported_locales	= ["nl", "%s", "fr"]
+		default_locale		= "%s"
+	}
+}
+	`, realm, realm, supportedLocale, defaultLocale)
+}
+
+func testKeycloakRealm_internationalizationValidationWithoutSupportedLocales(realm, defaultLocale string) string {
+	return fmt.Sprintf(`
+resource "keycloak_realm" "realm" {
+	realm        = "%s"
+	enabled      = true
+	display_name = "%s"
+	internationalization {
+		default_locale		= "%s"
+	}
+
+}
+	`, realm, realm, defaultLocale)
+}
+
 func testKeycloakRealm_notEnabled(realm, realmDisplayName string) string {
 	return fmt.Sprintf(`
 resource "keycloak_realm" "realm" {