diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmLocalizationResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmLocalizationResource.java index 51b84915c5..6e94da5cce 100755 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmLocalizationResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmLocalizationResource.java @@ -23,6 +23,7 @@ import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @@ -59,4 +60,9 @@ public interface RealmLocalizationResource { @PUT @Consumes(MediaType.TEXT_PLAIN) void saveRealmLocalizationText(@PathParam("locale") String locale, @PathParam("key") String key, String text); + + @Path("{locale}") + @POST + @Consumes("application/json") + void createOrUpdateRealmLocalizationTexts(@PathParam("locale") String locale, Map localizationTexts); } diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java index af08724645..69508c3e8d 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java @@ -1698,9 +1698,9 @@ public class RealmAdapter implements CachedRealmModel { } @Override - public void patchRealmLocalizationTexts(String locale, Map localizationTexts) { + public void createOrUpdateRealmLocalizationTexts(String locale, Map localizationTexts) { getDelegateForUpdate(); - updated.patchRealmLocalizationTexts(locale, localizationTexts); + updated.createOrUpdateRealmLocalizationTexts(locale, localizationTexts); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index 2c87393d10..5beb63ec4d 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -2173,11 +2173,13 @@ public class RealmAdapter implements RealmModel, JpaModel { } @Override - public void patchRealmLocalizationTexts(String locale, Map localizationTexts) { + public void createOrUpdateRealmLocalizationTexts(String locale, Map localizationTexts) { Map currentLocalizationTexts = realm.getRealmLocalizationTexts(); if(currentLocalizationTexts.containsKey(locale)) { RealmLocalizationTextsEntity localizationTextsEntity = currentLocalizationTexts.get(locale); - localizationTextsEntity.getTexts().putAll(localizationTexts); + Map updatedTexts = new HashMap<>(localizationTextsEntity.getTexts()); + updatedTexts.putAll(localizationTexts); + localizationTextsEntity.setTexts(updatedTexts); em.persist(localizationTextsEntity); } diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java index 4222bfeaa9..2b0fd3e24b 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmAdapter.java @@ -1294,7 +1294,7 @@ public class MapRealmAdapter extends AbstractRealmModel implemen } @Override - public void patchRealmLocalizationTexts(String locale, Map localizationTexts) { + public void createOrUpdateRealmLocalizationTexts(String locale, Map localizationTexts) { Map> realmLocalizationTexts = entity.getLocalizationTexts(); if (realmLocalizationTexts.containsKey(locale)) { diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java index f42dc791db..4def40064e 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java @@ -175,7 +175,7 @@ public class MapRealmProvider implements RealmProvider { if (! updateLocalizationText(realm, locale, key, text)) { Map texts = new HashMap<>(); texts.put(key, text); - realm.patchRealmLocalizationTexts(locale, texts); + realm.createOrUpdateRealmLocalizationTexts(locale, texts); } } @@ -183,7 +183,7 @@ public class MapRealmProvider implements RealmProvider { @Override public void saveLocalizationTexts(RealmModel realm, String locale, Map localizationTexts) { if (locale == null || localizationTexts == null) return; - realm.patchRealmLocalizationTexts(locale, localizationTexts); + realm.createOrUpdateRealmLocalizationTexts(locale, localizationTexts); } //TODO move the following method to adapter @@ -192,7 +192,7 @@ public class MapRealmProvider implements RealmProvider { if (locale == null || key == null || text == null || (! realm.getRealmLocalizationTextsByLocale(locale).containsKey(key))) return false; Map texts = new HashMap<>(realm.getRealmLocalizationTextsByLocale(locale)); texts.replace(key, text); - realm.patchRealmLocalizationTexts(locale, texts); + realm.createOrUpdateRealmLocalizationTexts(locale, texts); return true; } @@ -210,7 +210,7 @@ public class MapRealmProvider implements RealmProvider { Map texts = new HashMap<>(realm.getRealmLocalizationTextsByLocale(locale)); texts.remove(key); realm.removeRealmLocalizationTexts(locale); - realm.patchRealmLocalizationTexts(locale, texts); + realm.createOrUpdateRealmLocalizationTexts(locale, texts); return true; } diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java index 4145a78e71..087b14024c 100755 --- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java +++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java @@ -992,10 +992,11 @@ public interface RealmModel extends RoleContainerModel { void removeDefaultClientScope(ClientScopeModel clientScope); /** - * Patches the realm-specific localization texts. This method will not delete any text. + * Creates or updates the realm-specific localization texts for the given locale. + * This method will not delete any text. * It updates texts, which are already stored or create new ones if the key does not exist yet. */ - void patchRealmLocalizationTexts(String locale, Map localizationTexts); + void createOrUpdateRealmLocalizationTexts(String locale, Map localizationTexts); boolean removeRealmLocalizationTexts(String locale); Map> getRealmLocalizationTexts(); Map getRealmLocalizationTextsByLocale(String locale); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmLocalizationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmLocalizationResource.java index 0ab2f99a62..ad5a46d37b 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmLocalizationResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmLocalizationResource.java @@ -84,8 +84,8 @@ public class RealmLocalizationResource { @POST @Path("{locale}") @Consumes(MediaType.MULTIPART_FORM_DATA) - public void patchRealmLocalizationTextsFromFile(@PathParam("locale") String locale, MultipartFormDataInput input) - throws IOException { + public void createOrUpdateRealmLocalizationTextsFromFile(@PathParam("locale") String locale, + MultipartFormDataInput input) { this.auth.realm().requireManageRealm(); Map> formDataMap = input.getFormDataMap(); @@ -97,18 +97,19 @@ public class RealmLocalizationResource { TypeReference> typeRef = new TypeReference>() { }; Map rep = JsonSerialization.readValue(inputStream, typeRef); - realm.patchRealmLocalizationTexts(locale, rep); + realm.createOrUpdateRealmLocalizationTexts(locale, rep); } catch (IOException e) { throw new BadRequestException("Could not read file."); } } - @PATCH + @POST @Path("{locale}") @Consumes(MediaType.APPLICATION_JSON) - public void patchRealmLocalizationTexts(@PathParam("locale") String locale, Map loclizationTexts) { + public void createOrUpdateRealmLocalizationTexts(@PathParam("locale") String locale, + Map localizationTexts) { this.auth.realm().requireManageRealm(); - realm.patchRealmLocalizationTexts(locale, loclizationTexts); + realm.createOrUpdateRealmLocalizationTexts(locale, localizationTexts); } @Path("{locale}") diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RealmLocalizationResourceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RealmLocalizationResourceTest.java index 151082a089..b49c40ae43 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RealmLocalizationResourceTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/RealmLocalizationResourceTest.java @@ -17,15 +17,16 @@ package org.keycloak.testsuite.admin; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; import org.hamcrest.CoreMatchers; import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.RealmLocalizationResource; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -44,6 +45,7 @@ public class RealmLocalizationResourceTest extends AbstractAdminTest { getCleanup().addLocalization("en"); getCleanup().addLocalization("de"); + getCleanup().addLocalization("es"); resource = adminClient.realm(REALM_NAME).localization(); } @@ -127,4 +129,31 @@ public class RealmLocalizationResourceTest extends AbstractAdminTest { assertThat(localizations, CoreMatchers.hasItems("de")); } + + @Test + public void createOrUpdateRealmLocalizationWhenLocaleDoesNotYetExist() { + final Map newLocalizationTexts = new HashMap<>(); + newLocalizationTexts.put("key-a", "text-a_es"); + newLocalizationTexts.put("key-b", "text-b_es"); + + resource.createOrUpdateRealmLocalizationTexts("es", newLocalizationTexts); + + final Map persistedLocalizationTexts = resource.getRealmLocalizationTexts("es"); + assertEquals(newLocalizationTexts, persistedLocalizationTexts); + } + + @Test + public void createOrUpdateRealmLocalizationWhenLocaleAlreadyExists() { + final Map newLocalizationTexts = new HashMap<>(); + newLocalizationTexts.put("key-b", "text-b_changed_en"); + newLocalizationTexts.put("key-c", "text-c_en"); + + resource.createOrUpdateRealmLocalizationTexts("en", newLocalizationTexts); + + final Map expectedLocalizationTexts = new HashMap<>(); + expectedLocalizationTexts.put("key-a", "text-a_en"); + expectedLocalizationTexts.putAll(newLocalizationTexts); + final Map persistedLocalizationTexts = resource.getRealmLocalizationTexts("en"); + assertEquals(expectedLocalizationTexts, persistedLocalizationTexts); + } }