From 1b65a293fc57ac8d3365c791ee4c1d4c004f4785 Mon Sep 17 00:00:00 2001 From: Humberto Morera <31667212+hmoreras@users.noreply.github.com> Date: Tue, 1 Oct 2024 18:54:28 -0600 Subject: [PATCH 1/2] fix (ace editor): #29733 WYSIWYG code view styles messed up (#30209) ### Proposed Changes * Force the editor font size to be consistent at 12px to avoid cursor odd behavior. --- .../main/webapp/html/js/ace-builds-1.2.3/src-noconflict/ace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotCMS/src/main/webapp/html/js/ace-builds-1.2.3/src-noconflict/ace.js b/dotCMS/src/main/webapp/html/js/ace-builds-1.2.3/src-noconflict/ace.js index 1b8a0593c4f6..afd7ca8ef73e 100644 --- a/dotCMS/src/main/webapp/html/js/ace-builds-1.2.3/src-noconflict/ace.js +++ b/dotCMS/src/main/webapp/html/js/ace-builds-1.2.3/src-noconflict/ace.js @@ -14752,7 +14752,7 @@ var VScrollBar = require("./scrollbar").VScrollBar; var RenderLoop = require("./renderloop").RenderLoop; var FontMetrics = require("./layer/font_metrics").FontMetrics; var EventEmitter = require("./lib/event_emitter").EventEmitter; -var editorCss = ".ace_editor {\ +var editorCss = ".ace_editor, .ace_editor * {\ position: relative;\ overflow: hidden;\ font: 12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;\ From 1eae24830310dce7cfdbcdab821075a5d2e71dd3 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Tue, 1 Oct 2024 21:21:35 -0600 Subject: [PATCH 2/2] Issue 30148 raw cube endpoint (#30159) Adding endpoint to send a cube js raw json query --------- Co-authored-by: Jose Castro --- .../content/ContentAnalyticsAPI.java | 7 ++ .../content/ContentAnalyticsAPIImpl.java | 7 ++ .../content/ContentAnalyticsFactory.java | 8 ++ .../content/ContentAnalyticsFactoryImpl.java | 15 +++ .../listener/AnalyticsAppListener.java | 15 +++ .../WebEventsCollectorServiceFactory.java | 2 +- .../business/SystemTableInitializer.java | 4 +- .../java/com/dotcms/cube/CubeJSClient.java | 34 ++++-- .../content/ContentAnalyticsResource.java | 62 +++++++++- .../content/ReportResponseEntityView.java | 10 +- .../filters/InterceptorFilter.java | 5 +- .../java/com/dotmarketing/util/Config.java | 4 + .../content/ContentAnalyticsAPITest.java | 78 +++++++++++++ .../content/ContentAnalyticsFactoryTest.java | 109 ++++++++++++++++++ .../analytics/viewtool/AnalyticsToolTest.java | 6 +- .../com/dotcms/cube/CubeJSClientTest.java | 6 +- 16 files changed, 349 insertions(+), 23 deletions(-) diff --git a/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsAPI.java b/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsAPI.java index 26531579feae..aeec11a84a33 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsAPI.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsAPI.java @@ -31,4 +31,11 @@ public interface ContentAnalyticsAPI { */ ReportResponse runRawReport(CubeJSQuery cubeJSQuery, User user); + /** + * Runs a raw report based on a cubeJS json string query + * @param cubeJsQueryJson + * @param user + * @return ReportResponse + */ + ReportResponse runRawReport(String cubeJsQueryJson, User user); } diff --git a/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsAPIImpl.java b/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsAPIImpl.java index 2417314de079..b13f75bc2098 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsAPIImpl.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsAPIImpl.java @@ -39,4 +39,11 @@ public ReportResponse runRawReport(CubeJSQuery cubeJSQuery, User user) { return this.contentAnalyticsFactory.getRawReport(cubeJSQuery, user); } + @Override + public ReportResponse runRawReport(final String cubeJsQueryJson, final User user) { + + Logger.debug(this, ()-> "Running the report for the raw json query: " + cubeJsQueryJson); + // note: should check any permissions for an user. + return this.contentAnalyticsFactory.getRawReport(cubeJsQueryJson, user); + } } diff --git a/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsFactory.java b/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsFactory.java index 2821e6de5009..ef793f300673 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsFactory.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsFactory.java @@ -30,4 +30,12 @@ public interface ContentAnalyticsFactory { */ ReportResponse getRawReport(final CubeJSQuery query, final User user); + /** + * Runs the raw report based on the cube js json string query and user. + * + * @param cubeJsQueryJson the query to run the report. + * @param user the user to run the report. + * @return the report response. + */ + ReportResponse getRawReport(String cubeJsQueryJson, User user); } diff --git a/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsFactoryImpl.java b/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsFactoryImpl.java index be1e7f999d5e..b17cacd3ad98 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsFactoryImpl.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/content/ContentAnalyticsFactoryImpl.java @@ -64,6 +64,21 @@ public ReportResponse getRawReport(final CubeJSQuery cubeJSQuery, final User use } } + @Override + public ReportResponse getRawReport(final String cubeJsQueryJson, final User user) { + + try { + + Logger.debug(this, ()-> "Getting the report for the raw query: " + cubeJsQueryJson); + final CubeJSClient cubeClient = cubeJSClientFactory.create(user); + return toReportResponse(cubeClient.send(cubeJsQueryJson)); + } catch (DotDataException| DotSecurityException e) { + + Logger.error(this, e.getMessage(), e); + throw new DotRuntimeException(e); + } + } + private ReportResponse toReportResponse(final CubeJSResultSet cubeJSResultSet) { return new ReportResponse(StreamSupport.stream(cubeJSResultSet.spliterator(), false).collect(Collectors.toList())); diff --git a/dotCMS/src/main/java/com/dotcms/analytics/listener/AnalyticsAppListener.java b/dotCMS/src/main/java/com/dotcms/analytics/listener/AnalyticsAppListener.java index f29812d673ee..cf44bd07c4fb 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/listener/AnalyticsAppListener.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/listener/AnalyticsAppListener.java @@ -8,6 +8,7 @@ import com.dotcms.api.system.event.SystemEventType; import com.dotcms.api.system.event.message.MessageSeverity; import com.dotcms.api.system.event.message.SystemMessageEventUtil; +import com.dotcms.api.system.event.message.builder.SystemMessage; import com.dotcms.api.system.event.message.builder.SystemMessageBuilder; import com.dotcms.exception.AnalyticsException; import com.dotcms.security.apps.AbstractProperty; @@ -22,9 +23,11 @@ import com.dotmarketing.util.DateUtil; import com.dotmarketing.util.Logger; import com.liferay.portal.language.LanguageUtil; +import com.liferay.portal.model.User; import io.vavr.control.Try; import org.apache.commons.lang3.StringUtils; +import java.util.Collections; import java.util.Objects; import java.util.Optional; @@ -99,6 +102,7 @@ public void notify(final AppSecretSavedEvent event) { Logger.error( this, String.format("Cannot process event for app update due to: %s", e.getMessage()), e); + notifyErrorToTheUser(e, event); } }); }); @@ -112,6 +116,17 @@ public void notify(final AppSecretSavedEvent event) { } } + private void notifyErrorToTheUser(final AnalyticsException e, final AppSecretSavedEvent event) { + + final SystemMessage systemMessage = new SystemMessageBuilder() + .setMessage(String.format("Cannot do the app update due to: %s", e.getMessage())) + .setSeverity(MessageSeverity.ERROR) + .setLife(DateUtil.TEN_SECOND_MILLIS) + .create(); + + SystemMessageEventUtil.getInstance().pushMessage(systemMessage, Collections.singletonList(event.getUserId())); + } + /** * Pushes message to notify that even though analytics app env-var properties were changed though the Analytics * App, those changes were ignored since they are govern by env-vars. diff --git a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceFactory.java b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceFactory.java index 00054371f094..3218b8610d56 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceFactory.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceFactory.java @@ -116,7 +116,7 @@ private void fireCollectorsAndEmitEvent(final HttpServletRequest request, .collect(java.util.stream.Collectors.toList()); })); } catch (Exception e) { - Logger.debug(WebEventsCollectorServiceFactory.class, () -> "Error saving Analitycs Events:" + e.getMessage()); + Logger.debug(WebEventsCollectorServiceFactory.class, () -> "Error saving Analytics Events:" + e.getMessage()); } } diff --git a/dotCMS/src/main/java/com/dotcms/business/SystemTableInitializer.java b/dotCMS/src/main/java/com/dotcms/business/SystemTableInitializer.java index fe332adae7f5..689739a90681 100644 --- a/dotCMS/src/main/java/com/dotcms/business/SystemTableInitializer.java +++ b/dotCMS/src/main/java/com/dotcms/business/SystemTableInitializer.java @@ -13,7 +13,9 @@ public class SystemTableInitializer implements DotInitializer { @Override public void init() { - Config.initSystemTableConfigSource(); + if (!Config.isSystemTableConfigSourceInit()) { + Config.initSystemTableConfigSource(); + } // Load the all system table into the system cache APILocator.getSystemAPI().getSystemTable().all(); } diff --git a/dotCMS/src/main/java/com/dotcms/cube/CubeJSClient.java b/dotCMS/src/main/java/com/dotcms/cube/CubeJSClient.java index ea8662e28db0..6a1d168fa085 100644 --- a/dotCMS/src/main/java/com/dotcms/cube/CubeJSClient.java +++ b/dotCMS/src/main/java/com/dotcms/cube/CubeJSClient.java @@ -80,18 +80,27 @@ public CubeJSResultSet send(final CubeJSQuery query) { DotPreconditions.notNull(query, "Query not must be NULL"); DotPreconditions.notNull(accessToken, "Access token not must be NULL"); + final String queryAsString = query.toString(); + return send(queryAsString); + } + + public CubeJSResultSet send(final String queryAsString) { + + DotPreconditions.notNull(queryAsString, "Query not must be NULL"); + DotPreconditions.notNull(accessToken, "Access token not must be NULL"); + final CircuitBreakerUrl cubeJSClient; final String cubeJsUrl = String.format("%s/cubejs-api/v1/load", url); - final String queryAsString = query.toString(); + try { cubeJSClient = CircuitBreakerUrl.builder() - .setMethod(Method.GET) - .setHeaders(cubeJsHeaders(accessToken)) - .setUrl(cubeJsUrl) - .setParams(new HashMap<>(Map.of("query", queryAsString))) - .setTimeout(4000) - .setThrowWhenNot2xx(false) - .build(); + .setMethod(Method.GET) + .setHeaders(cubeJsHeaders(accessToken)) + .setUrl(cubeJsUrl) + .setParams(new HashMap<>(Map.of("query", queryAsString))) + .setTimeout(4000) + .setThrowWhenNot2xx(false) + .build(); } catch (AnalyticsException e) { throw new RuntimeException(e); } @@ -101,7 +110,7 @@ public CubeJSResultSet send(final CubeJSQuery query) { try { final String responseAsString = response.getResponse(); final Map responseAsMap = UtilMethods.isSet(responseAsString) && !responseAsString.equals("[]") ? - JsonUtil.getJsonFromString(responseAsString) : new HashMap<>(); + JsonUtil.getJsonFromString(responseAsString) : new HashMap<>(); final List> data = (List>) responseAsMap.get("data"); return new CubeJSResultSetImpl(UtilMethods.isSet(data) ? data : Collections.emptyList()); @@ -113,7 +122,7 @@ public CubeJSResultSet send(final CubeJSQuery query) { private Response getStringResponse(final CircuitBreakerUrl cubeJSClient, final String cubeJsUrl, final String queryAsString) { - final TimeMetric timeMetric = TimeMetric.mark(getClass().getSimpleName()); + final TimeMetric timeMetric = TimeMetric.mark(getClass().getSimpleName()); Logger.debug(this, String.format("Getting results from CubeJs [%s] with query [%s]", cubeJsUrl, queryAsString)); final Response response = cubeJSClient.doResponse(); @@ -121,6 +130,10 @@ private Response getStringResponse(final CircuitBreakerUrl cubeJSClient, timeMetric.stop(); if (!CircuitBreakerUrl.isWithin2xx(response.getStatusCode())) { + + if (400 == response.getStatusCode()) { + throw new IllegalArgumentException(response.getResponse()); + } throw new RuntimeException("CubeJS Server is not available"); } @@ -139,4 +152,5 @@ private Map cubeJsHeaders(final AccessToken accessToken) throws .build(); } + } diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ContentAnalyticsResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ContentAnalyticsResource.java index 49761e1249bc..8a4aa4204fe9 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ContentAnalyticsResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ContentAnalyticsResource.java @@ -2,6 +2,7 @@ import com.dotcms.analytics.content.ContentAnalyticsAPI; import com.dotcms.analytics.content.ReportResponse; +import com.dotcms.analytics.model.ResultSetItem; import com.dotcms.cdi.CDIUtils; import com.dotcms.rest.InitDataObject; import com.dotcms.rest.WebResource; @@ -26,6 +27,8 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import java.util.List; +import java.util.stream.Collectors; /** * Resource class that exposes endpoints to query content analytics data. @@ -126,7 +129,64 @@ public ReportResponseEntityView query(@Context final HttpServletRequest request, Logger.debug(this, () -> "Querying content analytics data with the form: " + queryForm); final ReportResponse reportResponse = this.contentAnalyticsAPI.runReport(queryForm.getQuery(), user); - return new ReportResponseEntityView(reportResponse); + return new ReportResponseEntityView(reportResponse.getResults().stream().map(ResultSetItem::getAll).collect(Collectors.toList())); + } + + /** + * Query Content Analytics data. + * + * @param request the HTTP request. + * @param response the HTTP response. + * @param queryForm the query form. + * @return the report response entity view. + */ + @Operation( + operationId = "postContentAnalyticsQuery", + summary = "Retrieve Content Analytics data", + description = "Returns information of specific dotCMS objects whose health and engagement data is tracked.", + tags = {"Content Analytics"}, + responses = { + @ApiResponse(responseCode = "200", description = "Content Analytics data being queried", + content = @Content(mediaType = "application/json", + examples = { + @ExampleObject( + value = "{\n" + + " \"dimensions\": [\n" + + " \"Events.experiment\",\n" + + " \"Events.variant\"\n" + + " ]\n" + + "}" + ) + } + ) + ), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "403", description = "Forbidden"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + } + ) + @POST + @Path("/_query/cube") + @JSONP + @NoCache + @Consumes(MediaType.APPLICATION_JSON) + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public ReportResponseEntityView queryCubeJs(@Context final HttpServletRequest request, + @Context final HttpServletResponse response, + final String cubeJsQueryJson) { + + final InitDataObject initDataObject = new WebResource.InitBuilder(this.webResource) + .requestAndResponse(request, response) + .requiredBackendUser(true) + .rejectWhenNoUser(true) + .init(); + + final User user = initDataObject.getUser(); + DotPreconditions.checkNotNull(cubeJsQueryJson, IllegalArgumentException.class, "The 'query' JSON data cannot be null"); + Logger.debug(this, ()->"Querying content analytics data with the cube query json: " + cubeJsQueryJson); + final ReportResponse reportResponse = + this.contentAnalyticsAPI.runRawReport(cubeJsQueryJson, user); + return new ReportResponseEntityView(reportResponse.getResults().stream().map(ResultSetItem::getAll).collect(Collectors.toList())); } } diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ReportResponseEntityView.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ReportResponseEntityView.java index 99f49cf66647..ab5d5f05b07c 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ReportResponseEntityView.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ReportResponseEntityView.java @@ -1,17 +1,19 @@ package com.dotcms.rest.api.v1.analytics.content; -import com.dotcms.analytics.content.ReportResponse; import com.dotcms.rest.ResponseEntityView; +import java.util.List; +import java.util.Map; + /** - * + * View for the analytics report response entity. * * @author Jose Castro * @since Sep 13th, 2024 */ -public class ReportResponseEntityView extends ResponseEntityView { +public class ReportResponseEntityView extends ResponseEntityView>> { - public ReportResponseEntityView(final ReportResponse entity) { + public ReportResponseEntityView(final List> entity) { super(entity); } diff --git a/dotCMS/src/main/java/com/dotmarketing/filters/InterceptorFilter.java b/dotCMS/src/main/java/com/dotmarketing/filters/InterceptorFilter.java index 020d6cd2a19f..b1d69259343a 100644 --- a/dotCMS/src/main/java/com/dotmarketing/filters/InterceptorFilter.java +++ b/dotCMS/src/main/java/com/dotmarketing/filters/InterceptorFilter.java @@ -10,9 +10,9 @@ import com.dotcms.jitsu.EventLogWebInterceptor; import com.dotcms.prerender.PreRenderSEOWebInterceptor; import com.dotcms.security.multipart.MultiPartRequestSecurityWebInterceptor; -import com.dotcms.system.event.local.business.LocalSystemEventsAPI; import com.dotcms.variant.business.web.CurrentVariantWebInterceptor; import com.dotmarketing.business.APILocator; +import com.dotmarketing.util.Config; import javax.servlet.FilterConfig; import javax.servlet.ServletException; @@ -27,6 +27,9 @@ public class InterceptorFilter extends AbstractWebInterceptorSupportFilter { @Override public void init(final FilterConfig config) throws ServletException { + if (!Config.isSystemTableConfigSourceInit()) { + Config.initSystemTableConfigSource(); + } this.addInterceptors(config); super.init(config); } // init. diff --git a/dotCMS/src/main/java/com/dotmarketing/util/Config.java b/dotCMS/src/main/java/com/dotmarketing/util/Config.java index 7821269f03f2..d49861a0a1d0 100644 --- a/dotCMS/src/main/java/com/dotmarketing/util/Config.java +++ b/dotCMS/src/main/java/com/dotmarketing/util/Config.java @@ -60,6 +60,10 @@ public class Config { @VisibleForTesting public static boolean enableSystemTableConfigSource = "true".equalsIgnoreCase(EnvironmentVariablesService.getInstance().getenv().getOrDefault("DOT_ENABLE_SYSTEM_TABLE_CONFIG_SOURCE", "true")); + public static boolean isSystemTableConfigSourceInit() { + return null != systemTableConfigSource; + } + public static void initSystemTableConfigSource() { systemTableConfigSource = new SystemTableConfigSource(); } diff --git a/dotCMS/src/test/java/com/dotcms/analytics/content/ContentAnalyticsAPITest.java b/dotCMS/src/test/java/com/dotcms/analytics/content/ContentAnalyticsAPITest.java index 0dbd9f98bcda..15dd62e8aa57 100644 --- a/dotCMS/src/test/java/com/dotcms/analytics/content/ContentAnalyticsAPITest.java +++ b/dotCMS/src/test/java/com/dotcms/analytics/content/ContentAnalyticsAPITest.java @@ -89,4 +89,82 @@ public void getContentAnalyticsReport() { } } + /** + *
    + *
  • Method to test: {@link ContentAnalyticsAPI#runRawReport(String, User)} + *
  • + *
  • Given Scenario: Run a report for a given query.
  • + *
  • Expected Result: The simulated results must adhere to the data specified in + * the query.
  • + *
+ */ + @Test + public void getContentAnalyticsReportRaw() { + // ╔════════════════════════╗ + // ║ Generating Test Data ║ + // ╚════════════════════════╝ + final List dataList = List.of( + new ResultSetItem(Map.of( + "request.count", "4", + "request.pageId", "9c5f42da-31b1-4935-9df6-153f5de1bdf2", + "request.pageTitle", "Blogs", + "request.url", "/blog/" + )), + new ResultSetItem(Map.of( + "request.count", "3", + "request.pageId", "44a076ad-affa-49d4-97b3-6caa3824e7e8", + "request.pageTitle", "Destinations", + "request.url", "/destinations/" + )), + new ResultSetItem(Map.of( + "request.count", "2", + "request.pageId", "a9f30020-54ef-494e-92ed-645e757171c2", + "request.pageTitle", "home", + "request.url", "/" + )) + ); + + // ╔══════════════════╗ + // ║ Initialization ║ + // ╚══════════════════╝ + final String analyticsQuery = "{\n" + + " \"order\": {\n" + + " \"request.createdAt\": \"asc\"\n" + + " },\n" + + " \"dimensions\": [\n" + + " \"request.requestId\"\n" + + " ],\n" + + " \"measures\": [\n" + + " \"request.count\"\n" + + " ],\n" + + " \"timeDimensions\": [\n" + + " {\n" + + " \"dimension\": \"request.createdAt\",\n" + + " \"granularity\": \"day\"\n" + + " }\n" + + " ]\n" + + "}"; + + final User systemUser = new User(); + ContentAnalyticsFactory mockContentAnalyticsFactory = Mockito.mock(ContentAnalyticsFactory.class); + Mockito.when(mockContentAnalyticsFactory.getRawReport(analyticsQuery, systemUser)).thenReturn(new ReportResponse(dataList)); + final ContentAnalyticsAPI contentAnalyticsAPI = new ContentAnalyticsAPIImpl(mockContentAnalyticsFactory); + final ReportResponse report = contentAnalyticsAPI.runRawReport(analyticsQuery, systemUser); + + // ╔══════════════╗ + // ║ Assertions ║ + // ╚══════════════╝ + assertNotNull("The result list must never be empty", report.getResults()); + assertEquals("There must be " + dataList.size() + " results in the list", dataList.size(), report.getResults().size()); + int i = 0; + for (final ResultSetItem resultSetItem : report.getResults()) { + final ResultSetItem expectedResultSetItem = dataList.get(i); + assertEquals("Value of request.count is not the expected one", expectedResultSetItem.get("request.count").get(), resultSetItem.get("request.count").get()); + assertEquals("Value of request.pageId is not the expected one", expectedResultSetItem.get("request.pageId").get(), resultSetItem.get("request.pageId").get()); + assertEquals("Value of request.pageTitle is not the expected one", expectedResultSetItem.get("request.pageTitle").get(), resultSetItem.get("request.pageTitle").get()); + assertEquals("Value of request.url is not the expected one", expectedResultSetItem.get("request.url").get(), resultSetItem.get("request.url").get()); + i++; + } + } + } diff --git a/dotCMS/src/test/java/com/dotcms/analytics/content/ContentAnalyticsFactoryTest.java b/dotCMS/src/test/java/com/dotcms/analytics/content/ContentAnalyticsFactoryTest.java index f7ca49ef12c4..6a9698cf3cbf 100644 --- a/dotCMS/src/test/java/com/dotcms/analytics/content/ContentAnalyticsFactoryTest.java +++ b/dotCMS/src/test/java/com/dotcms/analytics/content/ContentAnalyticsFactoryTest.java @@ -142,6 +142,115 @@ public void getContentAnalyticsReport() throws DotDataException, DotSecurityExce } } + /** + *
    + *
  • Method to test: + * {@link ContentAnalyticsFactory#getRawReport(String, User)} + *
  • + *
  • Given Scenario: Run a report for a given query.
  • + *
  • Expected Result: The simulated results must adhere to the data specified in + * the query. A mock HTTP Server is created to simulate the data request.
  • + *
+ * + * @throws DotDataException Failed to create the CubeJS Client. + * @throws DotSecurityException Failed to create the CubeJS Client. + */ + @Test + public void getContentAnalyticsReportRaw() throws DotDataException, DotSecurityException { + // ╔════════════════════════╗ + // ║ Generating Test Data ║ + // ╚════════════════════════╝ + final List> dataList = List.of( + Map.of( + "request.count", "4", + "request.pageId", "9c5f42da-31b1-4935-9df6-153f5de1bdf2", + "request.pageTitle", "Blogs", + "request.url", "/blog/" + ), + Map.of( + "request.count", "3", + "request.pageId", "44a076ad-affa-49d4-97b3-6caa3824e7e8", + "request.pageTitle", "Destinations", + "request.url", "/destinations/" + ), + Map.of( + "request.count", "2", + "request.pageId", "a9f30020-54ef-494e-92ed-645e757171c2", + "request.pageTitle", "home", + "request.url", "/" + ) + ); + + // ╔══════════════════╗ + // ║ Initialization ║ + // ╚══════════════════╝ + final String cubeServerIp = "127.0.0.1"; + final int cubeJsServerPort = 5000; + final MockHttpServer mockhttpServer = new MockHttpServer(cubeServerIp, cubeJsServerPort); + IPUtils.disabledIpPrivateSubnet(true); + final Map>> dataExpected = Map.of("data", dataList); + + final String analyticsQuery = "{\n" + + " \"order\": {\n" + + " \"request.createdAt\": \"asc\"\n" + + " },\n" + + " \"dimensions\": [\n" + + " \"request.requestId\"\n" + + " ],\n" + + " \"measures\": [\n" + + " \"request.count\"\n" + + " ],\n" + + " \"timeDimensions\": [\n" + + " {\n" + + " \"dimension\": \"request.createdAt\",\n" + + " \"granularity\": \"day\"\n" + + " }\n" + + " ]\n" + + "}"; + + + final MockHttpServerContext mockHttpServerContext = new MockHttpServerContext.Builder() + .uri("/cubejs-api/v1/load") + .responseStatus(HttpURLConnection.HTTP_OK) + .responseBody(JsonUtil.getJsonStringFromObject(dataExpected)) + .build(); + + mockhttpServer.addContext(mockHttpServerContext); + try { + mockhttpServer.start(); + final CubeJSClient cubeClient = new CubeJSClient( + String.format("http://%s:%s", cubeServerIp, cubeJsServerPort), + getAccessToken()); + + final User systemUser = new User(); + CubeJSClientFactory mockCubeJsClientFactory = Mockito.mock(CubeJSClientFactory.class); + Mockito.when(mockCubeJsClientFactory.create(systemUser)).thenReturn(cubeClient); + final ContentAnalyticsFactory contentAnalyticsFactory = new ContentAnalyticsFactoryImpl(new AnalyticsQueryParser(), mockCubeJsClientFactory); + final ReportResponse report = contentAnalyticsFactory.getRawReport(analyticsQuery, systemUser); + + // ╔══════════════╗ + // ║ Assertions ║ + // ╚══════════════╝ + assertNotNull("The result list must never be empty", report.getResults()); + assertEquals("There must be " + dataList.size() + " results in the list", dataList.size(), report.getResults().size()); + int i = 0; + for (final ResultSetItem resultSetItem : report.getResults()) { + final Map objectMap = dataList.get(i); + assertEquals("Value of request.count is not the expected one", objectMap.get("request.count"), resultSetItem.get("request.count").orElse(StringPool.BLANK)); + assertEquals("Value of request.pageId is not the expected one", objectMap.get("request.pageId"), resultSetItem.get("request.pageId").orElse(StringPool.BLANK)); + assertEquals("Value of request.pageTitle is not the expected one", objectMap.get("request.pageTitle"), resultSetItem.get("request.pageTitle").orElse(StringPool.BLANK)); + assertEquals("Value of request.url is not the expected one", objectMap.get("request.url"), resultSetItem.get("request.url").orElse(StringPool.BLANK)); + i++; + } + } finally { + // ╔═══════════╗ + // ║ Cleanup ║ + // ╚═══════════╝ + IPUtils.disabledIpPrivateSubnet(false); + mockhttpServer.stop(); + } + } + private AccessToken getAccessToken() { return AnalyticsTestUtils.createAccessToken( "a1b2c3d4e5f6", diff --git a/dotCMS/src/test/java/com/dotcms/analytics/viewtool/AnalyticsToolTest.java b/dotCMS/src/test/java/com/dotcms/analytics/viewtool/AnalyticsToolTest.java index 2c587385a8c7..a7b87dfe6e83 100644 --- a/dotCMS/src/test/java/com/dotcms/analytics/viewtool/AnalyticsToolTest.java +++ b/dotCMS/src/test/java/com/dotcms/analytics/viewtool/AnalyticsToolTest.java @@ -188,7 +188,7 @@ public void test_run_report_from_map_good_map() { } /** - * Method to test: {@link AnalyticsTool#runRawReportFromJson(String)} + * Method to test: {@link AnalyticsTool#runRawReport(CubeJSQuery)} * Given Scenario: Sending a null json * ExpectedResult: Should throw {@link IllegalArgumentException} */ @@ -215,7 +215,7 @@ public void test_run_raw_report_from_json_npe() { } /** - * Method to test: {@link AnalyticsTool#runRawReportFromJson(String)} + * Method to test: {@link AnalyticsTool#runRawReport(CubeJSQuery)} * Given Scenario: Sending a good json * ExpectedResult: Should return not null ReportResponse */ @@ -235,7 +235,7 @@ public void test_run_raw_report_from_json_good_job() { Mockito.when(viewContext.getRequest()).thenReturn(request); Mockito.when(request.getSession(false)).thenReturn(session); Mockito.when(userWebAPI.getLoggedInUser(request)).thenReturn(user); - Mockito.when(contentAnalyticsAPI.runRawReport(Mockito.any(), Mockito.eq(user))).thenReturn(new ReportResponse(List.of())); + Mockito.when(contentAnalyticsAPI.runRawReport(Mockito.any(CubeJSQuery.class), Mockito.eq(user))).thenReturn(new ReportResponse(List.of())); analyticsTool.init(viewContext); final CubeJSQuery.Builder builder = analyticsTool.createCubeJSQueryBuilder(); diff --git a/dotCMS/src/test/java/com/dotcms/cube/CubeJSClientTest.java b/dotCMS/src/test/java/com/dotcms/cube/CubeJSClientTest.java index 14cd53bd83f5..ad7d429ba418 100644 --- a/dotCMS/src/test/java/com/dotcms/cube/CubeJSClientTest.java +++ b/dotCMS/src/test/java/com/dotcms/cube/CubeJSClientTest.java @@ -157,14 +157,16 @@ public void sendWrongQuery() { getAccessToken()); try { - cubeClient.send(null); + CubeJSQuery query = null; + cubeClient.send(query); throw new AssertionError("IllegalArgumentException Expected"); } catch (IllegalArgumentException e) { mockhttpServer.mustNeverCalled("/cubejs-api/v1/load"); } try { - cubeClient.send(null); + CubeJSQuery query = null; + cubeClient.send(query); throw new AssertionError("IllegalArgumentException Expected"); } catch (IllegalArgumentException e) { mockhttpServer.mustNeverCalled("/cubejs-api/v1/load");