From 61651347a7f183bf27c0973c27482fe716211f5a Mon Sep 17 00:00:00 2001 From: razvantufisi Date: Mon, 24 Jun 2024 18:35:26 +0300 Subject: [PATCH] Remove MetricsEventsListener --- README.md | 6 +- pom.xml | 2 +- .../metrics/event/MetricsEventListener.java | 75 ---- .../event/MetricsEventListenerFactory.java | 63 --- .../store/MicrometerEventStoreProvider.java | 17 +- .../MicrometerEventStoreProviderFactory.java | 25 +- ...ycloak.events.EventListenerProviderFactory | 1 - .../event/MetricsEventListenerTest.java | 369 ------------------ .../metrics/junit/KeycloakClient.java | 3 +- .../metrics/junit/KeycloakExtension.java | 1 + 10 files changed, 23 insertions(+), 539 deletions(-) delete mode 100644 src/main/java/io/kokuwa/keycloak/metrics/event/MetricsEventListener.java delete mode 100644 src/main/java/io/kokuwa/keycloak/metrics/event/MetricsEventListenerFactory.java delete mode 100644 src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory delete mode 100644 src/test/java/io/kokuwa/keycloak/metrics/event/MetricsEventListenerTest.java diff --git a/README.md b/README.md index 8ecb716..15d587e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Provides metrics for Keycloak user/admin events and user/client/session count. T [aerogear/keycloak-metrics-spi](https://github.com/aerogear/keycloak-metrics-spi) is an alternative to this plugin but is not well maintained. This implementation is different: -* no Prometheus push (event listener only adds counter to Micrometer) +* no Prometheus push (event store custom implementation only adds counter to Micrometer) * no realm specific Prometheus endpoint, only `/metrics` (from Quarkus) * no jvm/http metrics, this is [already](https://www.keycloak.org/server/configuration-metrics#_available_metrics) included in Keycloak * different metric names, can relace model ids with name (see [configuration](#kc_metrics_event_replace_ids)) @@ -56,6 +56,10 @@ keycloak_event_admin_total{error="",operation="CREATE",realm="9039a0b5-e8c9-437a ## Configuration +### `KC_COMMUNITY_EVENTS_METRICS_ENABLED` + +Set to `true` (the default false) than the events metrics gets counted using micrometer + ### `KC_METRICS_EVENT_REPLACE_IDS` Set to `true` (the default value) than replace model ids from events with names: diff --git a/pom.xml b/pom.xml index 2c54613..0c1c122 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ - + 17 24.0.5 ${version.org.keycloak} 1.18.3 diff --git a/src/main/java/io/kokuwa/keycloak/metrics/event/MetricsEventListener.java b/src/main/java/io/kokuwa/keycloak/metrics/event/MetricsEventListener.java deleted file mode 100644 index 9c2128d..0000000 --- a/src/main/java/io/kokuwa/keycloak/metrics/event/MetricsEventListener.java +++ /dev/null @@ -1,75 +0,0 @@ -package io.kokuwa.keycloak.metrics.event; - -import java.util.Optional; - - -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.events.Event; -import org.keycloak.events.EventListenerProvider; -import org.keycloak.events.admin.AdminEvent; -import org.keycloak.models.KeycloakContext; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.provider.EnvironmentDependentProviderFactory; - -import io.micrometer.core.instrument.Metrics; -import io.kokuwa.keycloak.metrics.CommunityProfiles; -/** - * Listener for {@link Event} and {@link AdminEvent}. - * - * @author Stephan Schnabel - */ -public class MetricsEventListener implements EventListenerProvider, AutoCloseable { - - private static final Logger log = Logger.getLogger(MetricsEventListener.class); - private final boolean replaceIds; - private final KeycloakSession session; - - MetricsEventListener(boolean replaceIds, KeycloakSession session) { - this.replaceIds = replaceIds; - this.session = session; - } - - @Override - public void onEvent(Event event) { - Metrics.counter("keycloak_event_user", - "realm", toBlank(replaceIds ? getRealmName(event.getRealmId()) : event.getRealmId()), - "type", toBlank(event.getType()), - "client", toBlank(event.getClientId()), - "error", toBlank(event.getError())) - .increment(); - } - - @Override - public void onEvent(AdminEvent event, boolean includeRepresentation) { - Metrics.counter("keycloak_event_admin", - "realm", toBlank(replaceIds ? getRealmName(event.getRealmId()) : event.getRealmId()), - "resource", toBlank(event.getResourceType()), - "operation", toBlank(event.getOperationType()), - "error", toBlank(event.getError())) - .increment(); - } - - @Override - public void close() {} - - private String getRealmName(String id) { - return Optional.ofNullable(session.getContext()).map(KeycloakContext::getRealm) - .filter(realm -> id == null || id.equals(realm.getId())) - .or(() -> { - log.tracev("Context realm was empty with id {0}", id); - return Optional.ofNullable(id).map(session.realms()::getRealm); - }) - .map(RealmModel::getName) - .orElseGet(() -> { - log.warnv("Failed to find realm with id {0}", id); - return id; - }); - } - - private String toBlank(Object value) { - return value == null ? "" : value.toString(); - } - -} diff --git a/src/main/java/io/kokuwa/keycloak/metrics/event/MetricsEventListenerFactory.java b/src/main/java/io/kokuwa/keycloak/metrics/event/MetricsEventListenerFactory.java deleted file mode 100644 index fadbee1..0000000 --- a/src/main/java/io/kokuwa/keycloak/metrics/event/MetricsEventListenerFactory.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.kokuwa.keycloak.metrics.event; - -import io.kokuwa.keycloak.metrics.CommunityProfiles; -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.Config.Scope; -import org.keycloak.events.EventListenerProvider; -import org.keycloak.events.EventListenerProviderFactory; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.provider.EnvironmentDependentProviderFactory; - -/** - * Factory for {@link MetricsEventListener}. - * - * @author Stephan Schnabel - */ -public class MetricsEventListenerFactory implements EventListenerProviderFactory, EnvironmentDependentProviderFactory { - - private static final Logger log = Logger.getLogger(MetricsEventListenerFactory.class); - private boolean replaceIds; - public static final String ID = "metrics-listener"; - - @Override - public String getId() { - return ID; - } - - @Override - public void init(Scope config) { - replaceIds = "true".equals(System.getenv().getOrDefault("KC_METRICS_EVENT_REPLACE_IDS", "true")); - log.info(replaceIds ? "Configured with model names." : "Configured with model ids."); - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - } - - @Override - public EventListenerProvider create(KeycloakSession session) { - return new MetricsEventListener(replaceIds, session); - } - - @Override - public void close() { - } - - - //to avoid double metric registration - @Override - public boolean isSupported() { - var defaultEventsMetricsDisabled = !CommunityProfiles.isEventsMetricsEnabled(); - log.info("MetricsEventListenerFactory is enabled:" + defaultEventsMetricsDisabled); - return defaultEventsMetricsDisabled; - } - - @Override - public boolean isSupported(Config.Scope config) { - var defaultEventsMetricsDisabled = !CommunityProfiles.isEventsMetricsEnabled(); - log.info("MetricsEventListenerFactory is enabled:" + defaultEventsMetricsDisabled); - return defaultEventsMetricsDisabled; - } -} diff --git a/src/main/java/io/kokuwa/keycloak/metrics/store/MicrometerEventStoreProvider.java b/src/main/java/io/kokuwa/keycloak/metrics/store/MicrometerEventStoreProvider.java index b456f53..659c212 100644 --- a/src/main/java/io/kokuwa/keycloak/metrics/store/MicrometerEventStoreProvider.java +++ b/src/main/java/io/kokuwa/keycloak/metrics/store/MicrometerEventStoreProvider.java @@ -1,6 +1,5 @@ package io.kokuwa.keycloak.metrics.store; -import io.kokuwa.keycloak.metrics.CommunityProfiles; import io.micrometer.core.instrument.Metrics; import jakarta.persistence.EntityManager; import org.jboss.logging.Logger; @@ -20,30 +19,32 @@ public class MicrometerEventStoreProvider extends JpaEventStoreProvider { private final boolean replaceIds; private final KeycloakSession session; + private final boolean isEventsMetricsEnabled; public MicrometerEventStoreProvider(KeycloakSession session, EntityManager em, - boolean replaceIds) { + boolean replaceIds, + boolean isEventsMetricsEnabled) { super(session, em); this.replaceIds = replaceIds; this.session = session; + this.isEventsMetricsEnabled = isEventsMetricsEnabled; } @Override public void onEvent(Event event) { super.onEvent(event); - var isEventsMetricsEnabled = CommunityProfiles.isEventsMetricsEnabled(); if (isEventsMetricsEnabled) { - addEvent(event); + log.info("User Event registered "); + countUserEvent(event); } } @Override public void onEvent(AdminEvent event, boolean includeRepresentation) { super.onEvent(event, includeRepresentation); - var isEventsMetricsEnabled = CommunityProfiles.isEventsMetricsEnabled(); if (isEventsMetricsEnabled) { - addAdminEvent(event); + countAdminEvent(event); } } @@ -69,7 +70,7 @@ private String toBlank(Object value) { return value == null ? "" : value.toString(); } - void addEvent(Event event) { + void countUserEvent(Event event) { session .getTransactionManager() .enlistAfterCompletion( @@ -94,7 +95,7 @@ protected void rollbackImpl() { }); } - void addAdminEvent(AdminEvent event) { + void countAdminEvent(AdminEvent event) { session .getTransactionManager() .enlistAfterCompletion( diff --git a/src/main/java/io/kokuwa/keycloak/metrics/store/MicrometerEventStoreProviderFactory.java b/src/main/java/io/kokuwa/keycloak/metrics/store/MicrometerEventStoreProviderFactory.java index 2b83bd8..f68b188 100644 --- a/src/main/java/io/kokuwa/keycloak/metrics/store/MicrometerEventStoreProviderFactory.java +++ b/src/main/java/io/kokuwa/keycloak/metrics/store/MicrometerEventStoreProviderFactory.java @@ -1,7 +1,6 @@ package io.kokuwa.keycloak.metrics.store; import io.kokuwa.keycloak.metrics.CommunityProfiles; -import io.kokuwa.keycloak.metrics.event.MetricsEventListenerFactory; import org.jboss.logging.Logger; import org.keycloak.Config; import org.keycloak.connections.jpa.JpaConnectionProvider; @@ -9,43 +8,29 @@ import org.keycloak.events.jpa.JpaEventStoreProviderFactory; import org.keycloak.models.KeycloakSession; -import java.util.stream.Collectors; - public class MicrometerEventStoreProviderFactory extends JpaEventStoreProviderFactory { private static final Logger log = Logger.getLogger(MicrometerEventStoreProviderFactory.class); public static final String ID = "jpa"; // Override default event store provider private boolean replaceIds; + private boolean isEventsMetricsEnabled; @Override public EventStoreProvider create(KeycloakSession session) { - //removeMetricsEventsListenerIfExists(session); - JpaConnectionProvider connection = session.getProvider(JpaConnectionProvider.class); - return new MicrometerEventStoreProvider(session, connection.getEntityManager(), replaceIds); + return new MicrometerEventStoreProvider(session, connection.getEntityManager(), replaceIds, isEventsMetricsEnabled); } public void init(Config.Scope config) { replaceIds = "true".equals(System.getenv().getOrDefault("KC_METRICS_EVENT_REPLACE_IDS", "true")); log.info(replaceIds ? "Configured with model names." : "Configured with model ids."); + + isEventsMetricsEnabled = CommunityProfiles.isEventsMetricsEnabled(); + log.info("Admin event metrics enabled: " + isEventsMetricsEnabled); } @Override public String getId() { return ID; } - - // we need to remove 'metrics-listener' if already exists in realm, and KC_COMMUNITY_EVENTS_METRICS_ENABLED=true otherwise the keycloak_1 | 2024-06-22 06:37:29,059 ERROR [org.keycloak.services] (executor-thread-3) KC-SERVICES0083: Event listener 'metrics-listener' registered, but provider not found - //Todo: This approach gives Unique index or primary key violation: "PUBLIC.PRIMARY_KEY_C38 ON PUBLIC.REALM_EVENTS_LISTENERS(REALM_ID, ""VALUE"") VALUES ( /* key:4 */ '32497f09-6079-42d3-8f56-9e4654b53e5e', 'jboss-logging')"; SQL statement: - //Todo: Approach is same on AdminRealmEvents.updateRealmEventsConfig. xgp any idea why? - private static void removeMetricsEventsListenerIfExists(KeycloakSession session) { - var eventsMetricsEnabled = CommunityProfiles.isEventsMetricsEnabled(); - if (eventsMetricsEnabled) { - var events = session.getContext().getRealm().getEventsListenersStream() - .filter(eventListener -> !MetricsEventListenerFactory.ID.equals(eventListener)) - .collect(Collectors.toSet()); - - session.getContext().getRealm().setEventsListeners(events); - } - } } diff --git a/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory b/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory deleted file mode 100644 index 9984e54..0000000 --- a/src/main/resources/META-INF/services/org.keycloak.events.EventListenerProviderFactory +++ /dev/null @@ -1 +0,0 @@ -io.kokuwa.keycloak.metrics.event.MetricsEventListenerFactory diff --git a/src/test/java/io/kokuwa/keycloak/metrics/event/MetricsEventListenerTest.java b/src/test/java/io/kokuwa/keycloak/metrics/event/MetricsEventListenerTest.java deleted file mode 100644 index 16684c5..0000000 --- a/src/test/java/io/kokuwa/keycloak/metrics/event/MetricsEventListenerTest.java +++ /dev/null @@ -1,369 +0,0 @@ -package io.kokuwa.keycloak.metrics.event; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; - -import java.util.UUID; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.keycloak.events.Event; -import org.keycloak.events.EventType; -import org.keycloak.events.admin.AdminEvent; -import org.keycloak.events.admin.OperationType; -import org.keycloak.events.admin.ResourceType; -import org.keycloak.models.KeycloakContext; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RealmProvider; -import org.mockito.Mock; - -import io.kokuwa.keycloak.metrics.junit.AbstractMockitoTest; -import io.micrometer.core.instrument.Metrics; - -/** - * Test for {@link MetricsEventListener} with Mockito. - * - * @author Stephan Schnabel - */ -@DisplayName("events: listener") -public class MetricsEventListenerTest extends AbstractMockitoTest { - - @Mock - KeycloakSession session; - @Mock - RealmModel realmModel; - @Mock - RealmProvider realmProvider; - @Mock - KeycloakContext context; - - @DisplayName("onEvent(true)") - @Nested - class onEvent { - - @DisplayName("replace(true) - without error") - @Test - void replaceWithoutError() { - - var realmId = UUID.randomUUID().toString(); - var realmName = UUID.randomUUID().toString(); - var clientId = UUID.randomUUID().toString(); - var type = EventType.LOGIN; - - when(session.getContext()).thenReturn(context); - when(context.getRealm()).thenReturn(realmModel); - when(realmModel.getId()).thenReturn(realmId); - when(realmModel.getName()).thenReturn(realmName); - - listener(true).onEvent(toEvent(realmId, clientId, type, null)); - assertEvent(realmName, clientId, type.toString(), ""); - } - - @DisplayName("replace(true) - with error") - @Test - void replaceWithError() { - - var realmId = UUID.randomUUID().toString(); - var realmName = UUID.randomUUID().toString(); - var clientId = UUID.randomUUID().toString(); - var type = EventType.LOGIN_ERROR; - var error = UUID.randomUUID().toString(); - - when(session.getContext()).thenReturn(context); - when(context.getRealm()).thenReturn(realmModel); - when(realmModel.getId()).thenReturn(realmId); - when(realmModel.getName()).thenReturn(realmName); - - listener(true).onEvent(toEvent(realmId, clientId, type, error)); - assertEvent(realmName, clientId, type.toString(), error); - } - - @DisplayName("replace(true) - all fields empty") - @Test - void replaceFieldsEmpty() { - - var realmName = UUID.randomUUID().toString(); - - when(session.getContext()).thenReturn(context); - when(context.getRealm()).thenReturn(realmModel); - when(realmModel.getName()).thenReturn(realmName); - - listener(true).onEvent(toEvent(null, null, null, null)); - assertEvent(realmName, "", "", ""); - } - - @DisplayName("replace(true) - context is null") - @Test - void replaceFieldsContextNull() { - - var realmId = UUID.randomUUID().toString(); - var realmName = UUID.randomUUID().toString(); - var clientId = UUID.randomUUID().toString(); - var type = EventType.LOGIN_ERROR; - - when(session.realms()).thenReturn(realmProvider); - when(realmProvider.getRealm(realmId)).thenReturn(realmModel); - when(realmModel.getName()).thenReturn(realmName); - - listener(true).onEvent(toEvent(realmId, clientId, type, null)); - assertEvent(realmName, clientId, type.toString(), ""); - } - - @DisplayName("replace(true) - context is empty") - @Test - void replaceFieldsContextEmpty() { - - var realmId = UUID.randomUUID().toString(); - var realmName = UUID.randomUUID().toString(); - var clientId = UUID.randomUUID().toString(); - var type = EventType.LOGIN_ERROR; - - when(session.getContext()).thenReturn(context); - when(session.realms()).thenReturn(realmProvider); - when(realmProvider.getRealm(realmId)).thenReturn(realmModel); - when(realmModel.getName()).thenReturn(realmName); - - listener(true).onEvent(toEvent(realmId, clientId, type, null)); - assertEvent(realmName, clientId, type.toString(), ""); - } - - @DisplayName("replace(true) - realmId is unknown") - @Test - void replaceFieldsRealmIdUnknown() { - - var realmId = UUID.randomUUID().toString(); - var clientId = UUID.randomUUID().toString(); - var type = EventType.LOGIN_ERROR; - - when(session.getContext()).thenReturn(context); - when(session.realms()).thenReturn(realmProvider); - when(context.getRealm()).thenReturn(realmModel); - when(realmModel.getId()).thenReturn(UUID.randomUUID().toString()); - - listener(true).onEvent(toEvent(realmId, clientId, type, null)); - assertEvent(realmId, clientId, type.toString(), ""); - } - - @DisplayName("replace(false) - without error") - @Test - void notReplaceWithoutError() { - - var realmId = UUID.randomUUID().toString(); - var clientId = UUID.randomUUID().toString(); - var type = EventType.LOGIN; - - listener(false).onEvent(toEvent(realmId, clientId, type, null)); - assertEvent(realmId, clientId, type.toString(), ""); - } - - @DisplayName("replace(false) - with error") - @Test - void notReplaceWithError() { - - var realmId = UUID.randomUUID().toString(); - var clientId = UUID.randomUUID().toString(); - var type = EventType.LOGIN_ERROR; - var error = UUID.randomUUID().toString(); - - listener(false).onEvent(toEvent(realmId, clientId, type, error)); - assertEvent(realmId, clientId, type.toString(), error); - } - - @DisplayName("replace(false) - all fields empty") - @Test - void notReplaceFieldsEmpty() { - listener(false).onEvent(toEvent(null, null, null, null)); - assertEvent("", "", "", ""); - } - - private Event toEvent(String realmId, String clientId, EventType type, String error) { - var event = new Event(); - event.setRealmId(realmId); - event.setClientId(clientId); - event.setType(type); - event.setError(error); - return event; - } - - private void assertEvent(String realm, String client, String type, String error) { - assertCounter("keycloak_event_user", - "realm", realm, - "client", client, - "type", type, - "error", error); - } - } - - @DisplayName("onEvent(AdminEvent,boolean)") - @Nested - class onAdminEvent { - - @DisplayName("replace(true) - without error") - @Test - void replaceWithoutError() { - - var realmId = UUID.randomUUID().toString(); - var realmName = UUID.randomUUID().toString(); - var resource = ResourceType.USER; - var operation = OperationType.CREATE; - - when(session.getContext()).thenReturn(context); - when(context.getRealm()).thenReturn(realmModel); - when(realmModel.getId()).thenReturn(realmId); - when(realmModel.getName()).thenReturn(realmName); - - listener(true).onEvent(toAdminEvent(realmId, resource, operation, null), false); - assertAdminEvent(realmName, resource.toString(), operation.toString(), ""); - } - - @DisplayName("replace(true) - with error") - @Test - void replaceWithError() { - - var realmId = UUID.randomUUID().toString(); - var realmName = UUID.randomUUID().toString(); - var resource = ResourceType.USER; - var operation = OperationType.CREATE; - var error = UUID.randomUUID().toString(); - - when(session.getContext()).thenReturn(context); - when(context.getRealm()).thenReturn(realmModel); - when(realmModel.getId()).thenReturn(realmId); - when(realmModel.getName()).thenReturn(realmName); - - listener(true).onEvent(toAdminEvent(realmId, resource, operation, error), false); - assertAdminEvent(realmName, resource.toString(), operation.toString(), error); - } - - @DisplayName("replace(true) - all fields empty") - @Test - void replaceFieldsEmpty() { - - var realmName = UUID.randomUUID().toString(); - - when(session.getContext()).thenReturn(context); - when(context.getRealm()).thenReturn(realmModel); - when(realmModel.getName()).thenReturn(realmName); - - listener(true).onEvent(toAdminEvent(null, null, null, null), false); - assertAdminEvent(realmName, "", "", ""); - } - - @DisplayName("replace(true) - context is null") - @Test - void replaceFieldsContextNull() { - - var realmId = UUID.randomUUID().toString(); - var realmName = UUID.randomUUID().toString(); - var resource = ResourceType.USER; - var operation = OperationType.CREATE; - - when(session.realms()).thenReturn(realmProvider); - when(realmProvider.getRealm(realmId)).thenReturn(realmModel); - when(realmModel.getName()).thenReturn(realmName); - - listener(true).onEvent(toAdminEvent(realmId, resource, operation, null), false); - assertAdminEvent(realmName, resource.toString(), operation.toString(), ""); - } - - @DisplayName("replace(true) - context is empty") - @Test - void replaceFieldsContextEmpty() { - - var realmId = UUID.randomUUID().toString(); - var realmName = UUID.randomUUID().toString(); - var resource = ResourceType.USER; - var operation = OperationType.CREATE; - - when(session.getContext()).thenReturn(context); - when(session.realms()).thenReturn(realmProvider); - when(realmProvider.getRealm(realmId)).thenReturn(realmModel); - when(realmModel.getName()).thenReturn(realmName); - - listener(true).onEvent(toAdminEvent(realmId, resource, operation, null), false); - assertAdminEvent(realmName, resource.toString(), operation.toString(), ""); - } - - @DisplayName("replace(true) - realmId is unknown") - @Test - void replaceFieldsRealmIdUnknown() { - - var realmId = UUID.randomUUID().toString(); - var resource = ResourceType.USER; - var operation = OperationType.CREATE; - - when(session.getContext()).thenReturn(context); - when(session.realms()).thenReturn(realmProvider); - when(context.getRealm()).thenReturn(realmModel); - when(realmModel.getId()).thenReturn(UUID.randomUUID().toString()); - - listener(true).onEvent(toAdminEvent(realmId, resource, operation, null), false); - assertAdminEvent(realmId, resource.toString(), operation.toString(), ""); - } - - @DisplayName("replace(false) - without error") - @Test - void noReplaceWithoutError() { - - var realmId = UUID.randomUUID().toString(); - var resource = ResourceType.USER; - var operation = OperationType.CREATE; - - listener(false).onEvent(toAdminEvent(realmId, resource, operation, null), false); - assertAdminEvent(realmId, resource.toString(), operation.toString(), ""); - } - - @DisplayName("replace(false) - with error") - @Test - void noReplaceWithError() { - - var realmId = UUID.randomUUID().toString(); - var resource = ResourceType.USER; - var operation = OperationType.CREATE; - var error = UUID.randomUUID().toString(); - - listener(false).onEvent(toAdminEvent(realmId, resource, operation, error), false); - assertAdminEvent(realmId, resource.toString(), operation.toString(), error); - } - - @DisplayName("replace(false) - all fields empty") - @Test - void noReplaceFieldsEmpty() { - listener(false).onEvent(toAdminEvent(null, null, null, null), false); - assertAdminEvent("", "", "", ""); - } - - private AdminEvent toAdminEvent(String realmId, ResourceType resource, OperationType operation, String error) { - var event = new AdminEvent(); - event.setRealmId(realmId); - event.setResourceType(resource); - event.setOperationType(operation); - event.setError(error); - return event; - } - - private void assertAdminEvent(String realm, String resource, String operation, String error) { - assertCounter("keycloak_event_admin", - "realm", realm, - "resource", resource, - "operation", operation, - "error", error); - } - } - - private MetricsEventListener listener(boolean replace) { - return new MetricsEventListener(replace, session); - } - - private static void assertCounter(String metric, String... tags) { - var counter = Metrics.globalRegistry.counter(metric, tags); - assertEquals(1D, counter.count(), "micrometer.counter.count"); - assertEquals(0, Metrics.globalRegistry - .getMeters().stream() - .filter(meter -> meter != counter) - .count(), - "other meter found"); - } -} diff --git a/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakClient.java b/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakClient.java index 029ffde..06f9fb4 100644 --- a/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakClient.java +++ b/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakClient.java @@ -55,7 +55,8 @@ public void createRealm(String realmName) { realm.setId(UUID.randomUUID().toString()); realm.setEnabled(true); realm.setRealm(realmName); - realm.setEventsListeners(List.of("metrics-listener")); + realm.setEventsEnabled(true); + realm.setAdminEventsEnabled(true); keycloak.realms().create(realm); } diff --git a/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakExtension.java b/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakExtension.java index c06c8f3..752e1c3 100644 --- a/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakExtension.java +++ b/src/test/java/io/kokuwa/keycloak/metrics/junit/KeycloakExtension.java @@ -58,6 +58,7 @@ public void beforeAll(ExtensionContext context) throws Exception { .withEnv("KC_LOG_LEVEL", "io.kokuwa:trace") .withEnv("KC_HEALTH_ENABLED", "true") .withEnv("KC_METRICS_ENABLED", "true") + .withEnv("KC_COMMUNITY_EVENTS_METRICS_ENABLED", "true") .withEnv("KC_METRICS_STATS_ENABLED", "true") .withEnv("KC_METRICS_STATS_INTERVAL", "PT1s") .withCopyFileToContainer(MountableFile.forHostPath(jar), "/opt/keycloak/providers/metrics.jar")