Skip to content

Commit

Permalink
Merge branch 'w2p-103837_GA4-exclude-non-content-bitstream-views-7.4'…
Browse files Browse the repository at this point in the history
… into w2p-103837_GA4-exclude-non-content-bitstream-views-main
  • Loading branch information
nona-luypaert committed Aug 17, 2023
2 parents abb5a58 + 22e2093 commit 49a3e98
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.core.Constants;
import org.dspace.core.Context;
Expand Down Expand Up @@ -176,26 +177,27 @@ private String getDocumentPath(HttpServletRequest request) {
* Verifies if the usage event is a content bitstream view event, by checking if:<ul>
* <li>the usage event is a view event</li>
* <li>the object of the usage event is a bitstream</li>
* <li>the bitstream belongs to the ORIGINAL bundle</li></ul>
* This last one can be skipped if 'google-analytics.exclude-non-content-bitstreams' is set to false.
* This will make it so the bundle name is completely ignored when sending events.
* <li>the bitstream belongs to one of the configured bundles (fallback: ORIGINAL bundle)</li></ul>
*/
private boolean isContentBitstream(UsageEvent usageEvent) {
// check if event is a VIEW event and object is a Bitstream
if (usageEvent.getAction() == UsageEvent.Action.VIEW
|| usageEvent.getObject().getType() == Constants.BITSTREAM) {
// check if config is set to true
if (configurationService.getBooleanProperty("google-analytics.exclude-non-content-bitstreams")) {
try {
// check if bitstream belongs to the ORIGINAL bundle
return ((Bitstream) usageEvent.getObject())
.getBundles().stream()
.anyMatch(bundle -> bundle.getName().equals(Constants.CONTENT_BUNDLE_NAME));
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
// check if bitstream belongs to a configured bundle
List<String> allowedBundles = List.of(configurationService
.getArrayProperty("google-analytics.bundles", new String[]{Constants.CONTENT_BUNDLE_NAME}));
if (allowedBundles.contains("none")) {
// GA events for bitstream views were turned off in config
return false;
}
return true;
List<String> bitstreamBundles;
try {
bitstreamBundles = ((Bitstream) usageEvent.getObject())
.getBundles().stream().map(Bundle::getName).collect(Collectors.toList());
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
return allowedBundles.stream().anyMatch(bitstreamBundles::contains);
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,17 +245,12 @@ public void testOnBitstreamContentDownloadWithTooManyEvents() throws Exception {
}

@Test
public void testOnBitstreamContentDownloadExcludeNonContentBitstreams() throws Exception {
configurationService.setProperty("google-analytics.exclude-non-content-bitstreams", true);

public void testOnBitstreamContentDownloadDefaultBundleConfig() throws Exception {
context.turnOffAuthorisationSystem();
Bundle licenseBundle = BundleBuilder.createBundle(context, item)
.withName(Constants.LICENSE_BUNDLE_NAME).build();
Bitstream license = BitstreamBuilder.createBitstream(context, licenseBundle,
toInputStream("License", defaultCharset())).build();
Bundle thumbnailBundle = BundleBuilder.createBundle(context, item).withName("THUMBNAIL").build();
Bitstream thumbnail = BitstreamBuilder.createBitstream(context, thumbnailBundle,
toInputStream("Thumbnail", defaultCharset())).build();
context.restoreAuthSystemState();

assertThat(getStoredEventsAsList(), empty());
Expand All @@ -264,14 +259,14 @@ public void testOnBitstreamContentDownloadExcludeNonContentBitstreams() throws E

downloadBitstreamContent("Postman", "123456", "REF");
downloadContent("Chrome", "ABCDEFG", "REF-1", license);
downloadContent("Chrome", "987654", "REF-2", thumbnail);

assertThat(getStoredEventsAsList(), hasSize(1));

List<GoogleAnalyticsEvent> storedEvents = getStoredEventsAsList();

assertThat(storedEvents, contains(
event("123456", "127.0.0.1", "Postman", "REF", bitstreamUrl, "Test item")));
event("123456", "127.0.0.1", "Postman", "REF", bitstreamUrl, "Test item"))
);

googleAsyncEventListener.sendCollectedEvents();

Expand All @@ -284,14 +279,14 @@ public void testOnBitstreamContentDownloadExcludeNonContentBitstreams() throws E
}

@Test
public void testOnBitstreamContentDownloadIncludeNonContentBitstreams() throws Exception {
configurationService.setProperty("google-analytics.exclude-non-content-bitstreams", false);
public void testOnBitstreamContentDownloadMultipleBundleConfig() throws Exception {
configurationService.setProperty("google-analytics.bundles",
List.of(Constants.DEFAULT_BUNDLE_NAME, "CONTENT"));

context.turnOffAuthorisationSystem();
Bundle licenseBundle = BundleBuilder.createBundle(context, item)
.withName(Constants.LICENSE_BUNDLE_NAME).build();
Bitstream license = BitstreamBuilder.createBitstream(context, licenseBundle,
toInputStream("License", defaultCharset())).build();
Bundle contentBundle = BundleBuilder.createBundle(context, item).withName("CONTENT").build();
Bitstream content = BitstreamBuilder.createBitstream(context, contentBundle,
toInputStream("Test Content", defaultCharset())).build();
Bundle thumbnailBundle = BundleBuilder.createBundle(context, item).withName("THUMBNAIL").build();
Bitstream thumbnail = BitstreamBuilder.createBitstream(context, thumbnailBundle,
toInputStream("Thumbnail", defaultCharset())).build();
Expand All @@ -300,21 +295,20 @@ public void testOnBitstreamContentDownloadIncludeNonContentBitstreams() throws E
assertThat(getStoredEventsAsList(), empty());

String bitstreamUrl = "/api/core/bitstreams/" + bitstream.getID() + "/content";
String licenseUrl = "/api/core/bitstreams/" + license.getID() + "/content";
String thumbnailUrl = "/api/core/bitstreams/" + thumbnail.getID() + "/content";
String contentUrl = "/api/core/bitstreams/" + content.getID() + "/content";

downloadBitstreamContent("Postman", "123456", "REF");
downloadContent("Chrome", "ABCDEFG", "REF-1", license);
downloadContent("Chrome", "ABCDEFG", "REF-1", content);
downloadContent("Chrome", "987654", "REF-2", thumbnail);

assertThat(getStoredEventsAsList(), hasSize(3));
assertThat(getStoredEventsAsList(), hasSize(2));

List<GoogleAnalyticsEvent> storedEvents = getStoredEventsAsList();

assertThat(storedEvents, contains(
event("123456", "127.0.0.1", "Postman", "REF", bitstreamUrl, "Test item"),
event("ABCDEFG", "127.0.0.1", "Chrome", "REF-1", licenseUrl, "Test item"),
event("987654", "127.0.0.1", "Chrome", "REF-2", thumbnailUrl, "Test item")));
event("ABCDEFG", "127.0.0.1", "Chrome", "REF-1", contentUrl, "Test item")
));

googleAsyncEventListener.sendCollectedEvents();

Expand All @@ -326,6 +320,28 @@ public void testOnBitstreamContentDownloadIncludeNonContentBitstreams() throws E
verifyNoMoreInteractions(firstGaClientMock, secondGaClientMock);
}

@Test
public void testOnBitstreamContentDownloadNoneBundleConfig() throws Exception {
configurationService.setProperty("google-analytics.bundles", "none");

context.turnOffAuthorisationSystem();
Bundle contentBundle = BundleBuilder.createBundle(context, item).withName("CONTENT").build();
Bitstream content = BitstreamBuilder.createBitstream(context, contentBundle,
toInputStream("Test Content", defaultCharset())).build();
Bundle thumbnailBundle = BundleBuilder.createBundle(context, item).withName("THUMBNAIL").build();
Bitstream thumbnail = BitstreamBuilder.createBitstream(context, thumbnailBundle,
toInputStream("Thumbnail", defaultCharset())).build();
context.restoreAuthSystemState();

assertThat(getStoredEventsAsList(), empty());

downloadBitstreamContent("Postman", "123456", "REF");
downloadContent("Chrome", "ABCDEFG", "REF-1", content);
downloadContent("Chrome", "987654", "REF-2", thumbnail);

assertThat(getStoredEventsAsList(), empty());
}

@SuppressWarnings("unchecked")
private List<GoogleAnalyticsEvent> getStoredEventsAsList() {
List<GoogleAnalyticsEvent> events = new ArrayList<>();
Expand Down
12 changes: 8 additions & 4 deletions dspace/config/dspace.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -1547,10 +1547,14 @@ log.report.dir = ${dspace.dir}/log
# For more details see https://developers.google.com/analytics/devguides/collection/protocol/ga4
# google.analytics.api-secret =

# Ensures only views of bitstreams in the 'ORIGINAL' bundle result in a GA4 event.
# Setting this to false may cause inflated bitstream view numbers, since requesting
# bitstreams in the 'THUMBNAIL' and 'LICENSE' bundles, will also result in GA4 events.
google-analytics.exclude-non-content-bitstreams=true
# Ensures only views of bitstreams in configured bundles result in a GA4 event.
# Config can contain multiple bundles for which the bitstream views will result in GA4 events, eg:
# google-analytics.bundles = ORIGINAL, CONTENT
# If config is not set or empty, the default fallback is Constants#CONTENT_BUNDLE_NAME bundle ('ORIGINAL').
# If config contains 'LICENSE' or 'THUMBNAIL' bundles, it may cause inflated bitstream view numbers.
# Set config to 'none' to disable GA4 bitstream events, eg:
# google-analytics.bundles = none
google-analytics.bundles = ORIGINAL

####################################################################
#---------------------------------------------------------------#
Expand Down

0 comments on commit 49a3e98

Please sign in to comment.