From 8a694a585bd3cd60cf36a6aadc044188b24aeac3 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Fri, 11 Jul 2025 09:03:28 +0200 Subject: [PATCH] Add generic update methods for builders (#40312) Closes #40311 Signed-off-by: stianst --- .../realm/ClientConfigBuilder.java | 21 +++++ .../realm/RealmConfigBuilder.java | 20 +++++ .../realm/UserConfigBuilder.java | 21 +++++ .../examples/CustomConfigBuilderTest.java | 81 +++++++++++++++++++ 4 files changed, 143 insertions(+) create mode 100644 test-framework/examples/tests/src/test/java/org/keycloak/test/examples/CustomConfigBuilderTest.java diff --git a/test-framework/core/src/main/java/org/keycloak/testframework/realm/ClientConfigBuilder.java b/test-framework/core/src/main/java/org/keycloak/testframework/realm/ClientConfigBuilder.java index fa33fa210ca..1a52b0a5551 100644 --- a/test-framework/core/src/main/java/org/keycloak/testframework/realm/ClientConfigBuilder.java +++ b/test-framework/core/src/main/java/org/keycloak/testframework/realm/ClientConfigBuilder.java @@ -3,6 +3,7 @@ package org.keycloak.testframework.realm; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -142,8 +143,28 @@ public class ClientConfigBuilder { return this; } + /** + * Best practice is to use other convenience methods when configuring a client, but while the framework is under + * active development there may not be a way to perform all updates required. In these cases this method allows + * applying any changes to the underlying representation. + * + * @param update + * @return this + * @deprecated + */ + public ClientConfigBuilder update(ClientUpdate... update) { + Arrays.stream(update).forEach(u -> u.update(rep)); + return this; + } + public ClientRepresentation build() { return rep; } + public interface ClientUpdate { + + void update(ClientRepresentation client); + + } + } diff --git a/test-framework/core/src/main/java/org/keycloak/testframework/realm/RealmConfigBuilder.java b/test-framework/core/src/main/java/org/keycloak/testframework/realm/RealmConfigBuilder.java index 4be63451230..cd3306b99bb 100644 --- a/test-framework/core/src/main/java/org/keycloak/testframework/realm/RealmConfigBuilder.java +++ b/test-framework/core/src/main/java/org/keycloak/testframework/realm/RealmConfigBuilder.java @@ -210,8 +210,28 @@ public class RealmConfigBuilder { return this; } + /** + * Best practice is to use other convenience methods when configuring a realm, but while the framework is under + * active development there may not be a way to perform all updates required. In these cases this method allows + * applying any changes to the underlying representation. + * + * @param update + * @return this + * @deprecated + */ + public RealmConfigBuilder update(RealmUpdate... update) { + Arrays.stream(update).forEach(u -> u.update(rep)); + return this; + } + public RealmRepresentation build() { return rep; } + public interface RealmUpdate { + + void update(RealmRepresentation realm); + + } + } diff --git a/test-framework/core/src/main/java/org/keycloak/testframework/realm/UserConfigBuilder.java b/test-framework/core/src/main/java/org/keycloak/testframework/realm/UserConfigBuilder.java index db7798e2411..a4cf4058db7 100644 --- a/test-framework/core/src/main/java/org/keycloak/testframework/realm/UserConfigBuilder.java +++ b/test-framework/core/src/main/java/org/keycloak/testframework/realm/UserConfigBuilder.java @@ -3,6 +3,7 @@ package org.keycloak.testframework.realm; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -87,8 +88,28 @@ public class UserConfigBuilder { return this; } + /** + * Best practice is to use other convenience methods when configuring a user, but while the framework is under + * active development there may not be a way to perform all updates required. In these cases this method allows + * applying any changes to the underlying representation. + * + * @param update + * @return this + * @deprecated + */ + public UserConfigBuilder update(UserUpdate... update) { + Arrays.stream(update).forEach(u -> u.update(rep)); + return this; + } + public UserRepresentation build() { return rep; } + public interface UserUpdate { + + void update(UserRepresentation client); + + } + } diff --git a/test-framework/examples/tests/src/test/java/org/keycloak/test/examples/CustomConfigBuilderTest.java b/test-framework/examples/tests/src/test/java/org/keycloak/test/examples/CustomConfigBuilderTest.java new file mode 100644 index 00000000000..beb27690d3c --- /dev/null +++ b/test-framework/examples/tests/src/test/java/org/keycloak/test/examples/CustomConfigBuilderTest.java @@ -0,0 +1,81 @@ +package org.keycloak.test.examples; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.keycloak.representations.idm.GroupRepresentation; +import org.keycloak.testframework.annotations.InjectClient; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.InjectUser; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.realm.ClientConfig; +import org.keycloak.testframework.realm.ClientConfigBuilder; +import org.keycloak.testframework.realm.ManagedClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.realm.ManagedUser; +import org.keycloak.testframework.realm.RealmConfig; +import org.keycloak.testframework.realm.RealmConfigBuilder; +import org.keycloak.testframework.realm.UserConfig; +import org.keycloak.testframework.realm.UserConfigBuilder; + +import java.util.LinkedList; + +@KeycloakIntegrationTest +public class CustomConfigBuilderTest { + + @InjectRealm(config = CustomRealmConfig.class) + ManagedRealm realm; + + @InjectClient(config = CustomClientConfig.class) + ManagedClient client; + + @InjectUser(config = CustomUserConfig.class) + ManagedUser user; + + @Test + public void testRealm() { + Assertions.assertEquals(1, realm.admin().groups().query("mygroup").size()); + } + + @Test + public void testClient() { + Assertions.assertTrue(client.admin().toRepresentation().isBearerOnly()); + } + + @Test + public void testUser() { + Assertions.assertFalse(user.admin().toRepresentation().isEnabled()); + } + + public static class CustomRealmConfig implements RealmConfig { + + @Override + public RealmConfigBuilder configure(RealmConfigBuilder realm) { + return realm.update(r -> { + if (r.getGroups() == null) { + r.setGroups(new LinkedList<>()); + } + GroupRepresentation group = new GroupRepresentation(); + group.setName("mygroup"); + group.setPath("/mygroup"); + r.getGroups().add(group); + }); + } + } + + public static class CustomClientConfig implements ClientConfig { + + @Override + public ClientConfigBuilder configure(ClientConfigBuilder client) { + return client.update(u -> u.setBearerOnly(true)); + } + } + + public static class CustomUserConfig implements UserConfig { + + @Override + public UserConfigBuilder configure(UserConfigBuilder user) { + return user.update(u -> u.setEnabled(false)); + } + } + +}