diff --git a/docker/docker-compose-examples/analytics/setup/db/clickhouse/init-scripts/init.sql b/docker/docker-compose-examples/analytics/setup/db/clickhouse/init-scripts/init.sql index 419a5a60ce07..1802cd6c964d 100644 --- a/docker/docker-compose-examples/analytics/setup/db/clickhouse/init-scripts/init.sql +++ b/docker/docker-compose-examples/analytics/setup/db/clickhouse/init-scripts/init.sql @@ -4,6 +4,10 @@ CREATE TABLE IF NOT EXISTS clickhouse_test_db.events _timestamp DateTime, api_key String, cluster_id String, + customer_name String, + customer_category String, + environment_name String, + environment_version String, customer_id String, doc_encoding String, doc_host String, @@ -110,3 +114,9 @@ ALTER TABLE clickhouse_test_db.events DROP COLUMN IF EXISTS object_detail_page_u ALTER TABLE clickhouse_test_db.events DROP COLUMN IF EXISTS object_url; ALTER TABLE clickhouse_test_db.events DROP COLUMN IF EXISTS object_forward_to; ALTER TABLE clickhouse_test_db.events DROP COLUMN IF EXISTS comefromvanityurl; + + +ALTER TABLE clickhouse_test_db.events ADD COLUMN IF NOT EXISTS customer_name String; +ALTER TABLE clickhouse_test_db.events ADD COLUMN IF NOT EXISTS customer_category String; +ALTER TABLE clickhouse_test_db.events ADD COLUMN IF NOT EXISTS environment_name String; +ALTER TABLE clickhouse_test_db.events ADD COLUMN IF NOT EXISTS environment_version String; \ No newline at end of file diff --git a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/BasicProfileCollector.java b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/BasicProfileCollector.java index 6057c93b9221..b9d25248479a 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/BasicProfileCollector.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/BasicProfileCollector.java @@ -1,13 +1,15 @@ package com.dotcms.analytics.track.collectors; import com.dotcms.enterprise.cluster.ClusterFactory; +import com.dotcms.exception.ExceptionUtil; +import com.dotcms.telemetry.business.MetricsAPI; import com.dotcms.util.FunctionUtils; import com.dotmarketing.business.APILocator; import com.dotmarketing.business.web.WebAPILocator; -import com.dotmarketing.util.PageMode; +import com.dotmarketing.exception.DotDataException; +import com.dotmarketing.util.Logger; import com.dotmarketing.util.UtilMethods; import com.liferay.portal.model.User; -import com.liferay.util.StringPool; import javax.servlet.http.HttpServletRequest; import java.time.Instant; @@ -15,12 +17,14 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; -import java.util.Map; import java.util.Objects; /** - * Collects the basic profile information for a collector payload bean. + * Collects the basic profile information for a collector payload bean. It's worth noting that + * ALLALL data collectors will include the information added by this one. + * * @author jsanca + * @since Sep 17th, 2024 */ public class BasicProfileCollector implements Collector { @@ -56,6 +60,8 @@ public CollectorPayloadBean collect(final CollectorContextMap collectorContextMa collectorPayloadBean.put(SESSION_ID, sessionId); collectorPayloadBean.put(SESSION_NEW, sessionNew); + this.setCustomerTelemetryData(collectorPayloadBean); + if (UtilMethods.isSet(collectorContextMap.get(CollectorContextMap.REFERER))) { collectorPayloadBean.put(REFERER, collectorContextMap.get(CollectorContextMap.REFERER).toString()); } @@ -83,13 +89,34 @@ public CollectorPayloadBean collect(final CollectorContextMap collectorContextMa return collectorPayloadBean; } + /** + * Sets the customer Telemetry data as part of the information that will be persisted to the + * Content Analytics database. + * + * @param collectorPayloadBean The {@link CollectorPayloadBean} that will be persisted to the + * Content Analytics database. + */ + private void setCustomerTelemetryData(final CollectorPayloadBean collectorPayloadBean) { + final MetricsAPI metricsAPI = APILocator.getMetricsAPI(); + try { + final MetricsAPI.Client client = metricsAPI.getClient(); + collectorPayloadBean.put(CUSTOMER_NAME, client.getClientName()); + collectorPayloadBean.put(CUSTOMER_CATEGORY, client.getCategory()); + collectorPayloadBean.put(ENVIRONMENT_NAME, client.getEnvironment()); + collectorPayloadBean.put(ENVIRONMENT_VERSION, client.getVersion()); + } catch (final DotDataException e) { + Logger.warnAndDebug(BasicProfileCollector.class, String.format("Failed to retrieve customer Telemetry data: " + + "%s", ExceptionUtil.getErrorMessage(e)), e); + } + } + private void setUserInfo(final HttpServletRequest request, final CollectorPayloadBean collectorPayloadBean) { final User user = WebAPILocator.getUserWebAPI().getUser(request); if (Objects.nonNull(user)) { final HashMap userObject = new HashMap<>(); - userObject.put(ID, user.getUserId().toString()); + userObject.put(ID, user.getUserId()); userObject.put(EMAIL, user.getEmailAddress()); collectorPayloadBean.put(USER_OBJECT, userObject); } diff --git a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/Collector.java b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/Collector.java index f77ed70d7aed..0e783e1cf5bf 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/Collector.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/Collector.java @@ -47,11 +47,9 @@ public interface Collector { String USER_AGENT = "userAgent"; String UTC_TIME = "utc_time"; - String RESPONSE = "action"; String RESPONSE_CODE = "response_code"; - String DETAIL_PAGE_URL = "detail_page_url"; String IS_EXPERIMENT_PAGE = "isexperimentpage"; String IS_TARGET_PAGE = "istargetpage"; @@ -61,6 +59,12 @@ public interface Collector { String EMAIL = "email"; String USER_OBJECT = "user"; + + String CUSTOMER_NAME = "customer_name"; + String CUSTOMER_CATEGORY = "customer_category"; + String ENVIRONMENT_NAME = "environment_name"; + String ENVIRONMENT_VERSION = "environment_version"; + /** * Test if the collector should run * @param collectorContextMap diff --git a/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/BasicProfileCollectorTest.java b/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/BasicProfileCollectorTest.java index 16b72e4d2d4d..0149cea7f13b 100644 --- a/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/BasicProfileCollectorTest.java +++ b/dotcms-integration/src/test/java/com/dotcms/analytics/track/collectors/BasicProfileCollectorTest.java @@ -4,6 +4,7 @@ import com.dotcms.LicenseTestUtil; import com.dotcms.analytics.track.matchers.PagesAndUrlMapsRequestMatcher; import com.dotcms.enterprise.cluster.ClusterFactory; +import com.dotcms.telemetry.business.MetricsAPI; import com.dotcms.util.IntegrationTestInitService; import com.dotmarketing.business.APILocator; import com.dotmarketing.exception.DotDataException; @@ -17,11 +18,18 @@ import java.net.UnknownHostException; import java.util.Map; +import static com.dotcms.analytics.track.collectors.Collector.CUSTOMER_CATEGORY; +import static com.dotcms.analytics.track.collectors.Collector.CUSTOMER_NAME; +import static com.dotcms.analytics.track.collectors.Collector.ENVIRONMENT_NAME; +import static com.dotcms.analytics.track.collectors.Collector.ENVIRONMENT_VERSION; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; /** + * Verifies that the {@link BasicProfileCollector} is able to collect the basic profile data and + * works as expected. * * @author Jose Castro * @since Oct 9th, 2024 @@ -43,9 +51,13 @@ public static void prepare() throws Exception { /** * */ @Test @@ -54,7 +66,7 @@ public void collectBasicProfileData() throws DotDataException, UnknownHostExcept final String requestId = UUIDUtil.uuid(); final HttpServletRequest request = Util.mockHttpRequestObj(response, "/", requestId, APILocator.getUserAPI().getAnonymousUser()); - final Map expectedDataMap = Map.of( + final Map expectedDataMap = new java.util.HashMap<>(Map.of( Collector.CLUSTER, CLUSTER_ID, Collector.SERVER, SERVER_ID, Collector.PERSONA, "dot:default", @@ -63,7 +75,22 @@ public void collectBasicProfileData() throws DotDataException, UnknownHostExcept Collector.USER_AGENT, Util.USER_AGENT, Collector.SESSION_ID, "DAA3339CD687D9ABD4101CF9EDDD42DB", Collector.REQUEST_ID, requestId - ); + )); + expectedDataMap.putAll(Map.of( + Collector.EVENT_SOURCE, EventSource.DOT_CMS.getName(), + Collector.IS_TARGET_PAGE, false, + Collector.IS_EXPERIMENT_PAGE, false, + Collector.USER_OBJECT, Map.of( + "identifier", "anonymous", + "email", "anonymous@dotcms.anonymoususer"))); + // The values returned when running the Integration Tests are random. So, in this case, + // we'll just verify that the attributes are present, and add any values in here + expectedDataMap.putAll(Map.of( + CUSTOMER_NAME, "", + CUSTOMER_CATEGORY, "", + ENVIRONMENT_NAME, "", + ENVIRONMENT_VERSION, 0 + )); final Collector collector = new BasicProfileCollector(); final CollectorPayloadBean collectedData = Util.getCollectorPayloadBean(request, collector, new PagesAndUrlMapsRequestMatcher(), null); @@ -74,13 +101,25 @@ public void collectBasicProfileData() throws DotDataException, UnknownHostExcept if (collectedData.toMap().containsKey(key)) { final Object expectedValue = expectedDataMap.get(key); final Object collectedValue = collectedData.toMap().get(key); - if (!Collector.UTC_TIME.equalsIgnoreCase(key)) { + if (CUSTOMER_NAME.equalsIgnoreCase(key) || CUSTOMER_CATEGORY.equalsIgnoreCase(key) || + ENVIRONMENT_NAME.equalsIgnoreCase(key) || ENVIRONMENT_VERSION.equalsIgnoreCase(key)) { + assertNotNull(String.format("Collected value '%s' cannot be null", key), collectedValue); + } else if (!Collector.UTC_TIME.equalsIgnoreCase(key)) { assertEquals("Collected value must be equal to expected value for key: " + key, expectedValue, collectedValue); } counter++; } } - assertEquals("Number of returned expected properties doesn't match", counter, expectedDataMap.size()); + final MetricsAPI metricsAPI = APILocator.getMetricsAPI(); + final MetricsAPI.Client client = metricsAPI.getClient(); + // In local envs, the 'category_name' attribute maybe null, and is NOT added to the + // collected data map, so the assertion below would fail. This hack is just to make this + // test run locally without devs having to tweak it + final boolean areAllAttrsPresent = client.getVersion() >= 0 && UtilMethods.isSet(client.getEnvironment()) && + UtilMethods.isSet(client.getCategory()) && UtilMethods.isSet(client.getClientName()); + if (areAllAttrsPresent) { + assertEquals("Number of returned expected properties doesn't match", counter, expectedDataMap.size()); + } } }