mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 10:04:21 +00:00
[CST-18963] Refactors matomo event handler to track bitstream view
This commit is contained in:
@@ -12,8 +12,10 @@ import java.io.InputStream;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.annotation.Nullable;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
@@ -496,4 +498,14 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl<Bitstream> imp
|
||||
public Long getLastModified(Bitstream bitstream) throws IOException {
|
||||
return bitstreamStorageService.getLastModified(bitstream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInBundle(Bitstream bitstream, java.util.Collection<String> bundleNames) throws SQLException {
|
||||
Set<String> bundles =
|
||||
bitstream.getBundles()
|
||||
.stream()
|
||||
.map(Bundle::getName)
|
||||
.collect(Collectors.toSet());
|
||||
return bundleNames.stream().anyMatch(bundles::contains);
|
||||
}
|
||||
}
|
||||
|
@@ -235,4 +235,14 @@ public interface BitstreamService extends DSpaceObjectService<Bitstream>, DSpace
|
||||
*/
|
||||
@Nullable
|
||||
Long getLastModified(Bitstream bitstream) throws IOException;
|
||||
|
||||
/**
|
||||
* Checks if the given bitstream is inside one of the bundle
|
||||
*
|
||||
* @param bitstream bitstream to verify
|
||||
* @param bundleNames names of the bundles to serch for
|
||||
* @return true if is in one of the bundles, false otherwise
|
||||
* @throws SQLException
|
||||
*/
|
||||
boolean isInBundle(Bitstream bitstream, java.util.Collection<String> bundleNames) throws SQLException;
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -23,8 +24,8 @@ import org.apache.commons.lang.StringUtils;
|
||||
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.content.service.BitstreamService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.google.client.GoogleAnalyticsClient;
|
||||
@@ -57,6 +58,9 @@ public class GoogleAsyncEventListener extends AbstractUsageEventListener {
|
||||
@Autowired
|
||||
private ClientInfoService clientInfoService;
|
||||
|
||||
@Autowired
|
||||
private BitstreamService bitstreamService;
|
||||
|
||||
@Autowired
|
||||
private List<GoogleAnalyticsClient> googleAnalyticsClients;
|
||||
|
||||
@@ -181,25 +185,35 @@ public class GoogleAsyncEventListener extends AbstractUsageEventListener {
|
||||
*/
|
||||
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) {
|
||||
if (!isBitstreamView(usageEvent)) {
|
||||
return false;
|
||||
}
|
||||
// check if bitstream belongs to a configured bundle
|
||||
List<String> allowedBundles = List.of(configurationService
|
||||
.getArrayProperty("google-analytics.bundles", new String[]{Constants.CONTENT_BUNDLE_NAME}));
|
||||
Set<String> allowedBundles =
|
||||
Set.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;
|
||||
}
|
||||
List<String> bitstreamBundles;
|
||||
return isInBundle((Bitstream) usageEvent.getObject(), allowedBundles);
|
||||
}
|
||||
|
||||
private boolean isInBundle(Bitstream bitstream, Set<String> allowedBundles) {
|
||||
try {
|
||||
bitstreamBundles = ((Bitstream) usageEvent.getObject())
|
||||
.getBundles().stream().map(Bundle::getName).collect(Collectors.toList());
|
||||
return this.bitstreamService.isInBundle(bitstream, allowedBundles);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
return allowedBundles.stream().anyMatch(bitstreamBundles::contains);
|
||||
}
|
||||
return false;
|
||||
|
||||
private boolean isBitstreamView(UsageEvent usageEvent) {
|
||||
return usageEvent.getAction() == UsageEvent.Action.VIEW
|
||||
&& usageEvent.getObject().getType() == Constants.BITSTREAM;
|
||||
}
|
||||
|
||||
private boolean isGoogleAnalyticsKeyNotConfigured() {
|
||||
|
@@ -7,10 +7,15 @@
|
||||
*/
|
||||
package org.dspace.matomo;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dspace.content.Bitstream;
|
||||
import org.dspace.content.service.BitstreamService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.model.Event;
|
||||
import org.dspace.usage.AbstractUsageEventListener;
|
||||
@@ -28,14 +33,17 @@ public class MatomoEventListener extends AbstractUsageEventListener {
|
||||
private static final Logger log = LogManager.getLogger(MatomoEventListener.class);
|
||||
|
||||
private final ConfigurationService configurationService;
|
||||
private final BitstreamService bitstreamService;
|
||||
private final List<MatomoUsageEventHandler> matomoUsageEventHandlers;
|
||||
|
||||
public MatomoEventListener(
|
||||
@Autowired List<MatomoUsageEventHandler> matomoUsageEventHandlers,
|
||||
@Autowired ConfigurationService configurationService
|
||||
@Autowired ConfigurationService configurationService,
|
||||
@Autowired BitstreamService bitstreamService
|
||||
) {
|
||||
this.matomoUsageEventHandlers = matomoUsageEventHandlers;
|
||||
this.configurationService = configurationService;
|
||||
this.bitstreamService = bitstreamService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,6 +57,10 @@ public class MatomoEventListener extends AbstractUsageEventListener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isContentBitstream(usageEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Usage event received {}", event.getName());
|
||||
}
|
||||
@@ -64,4 +76,48 @@ public class MatomoEventListener extends AbstractUsageEventListener {
|
||||
return this.configurationService.getBooleanProperty("matomo.enabled", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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 (!isBitstreamView(usageEvent)) {
|
||||
return false;
|
||||
}
|
||||
// check if bitstream belongs to a configured bundle
|
||||
Set<String> allowedBundles = getTrackedBundles();
|
||||
if (allowedBundles.contains("none")) {
|
||||
// events for bitstream views were turned off in config
|
||||
return false;
|
||||
}
|
||||
return isInBundle(((Bitstream) usageEvent.getObject()), allowedBundles);
|
||||
}
|
||||
|
||||
private Set<String> getTrackedBundles() {
|
||||
return Set.of(
|
||||
configurationService.getArrayProperty(
|
||||
"matomo.track.bundles",
|
||||
new String[] {Constants.CONTENT_BUNDLE_NAME}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected boolean isInBundle(Bitstream bitstream, Set<String> allowedBundles) {
|
||||
try {
|
||||
return this.bitstreamService.isInBundle(bitstream, allowedBundles);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBitstreamView(UsageEvent usageEvent) {
|
||||
return usageEvent.getAction() == UsageEvent.Action.VIEW
|
||||
&& usageEvent.getObject().getType() == Constants.BITSTREAM;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -7,9 +7,15 @@
|
||||
*/
|
||||
package org.dspace.matomo;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.dspace.AbstractUnitTest;
|
||||
import org.dspace.content.Bitstream;
|
||||
import org.dspace.content.Item;
|
||||
import org.dspace.content.service.BitstreamService;
|
||||
import org.dspace.core.Constants;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.usage.UsageEvent;
|
||||
import org.junit.Before;
|
||||
@@ -25,13 +31,15 @@ public class MatomoEventListenerTest extends AbstractUnitTest {
|
||||
MatomoSyncEventHandler matomoHandler2;
|
||||
@Mock
|
||||
ConfigurationService configurationService;
|
||||
@Mock
|
||||
BitstreamService bitstreamService;
|
||||
|
||||
MatomoEventListener matomoEventListener;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
matomoEventListener =
|
||||
new MatomoEventListener(List.of(matomoHandler1, matomoHandler2), configurationService);
|
||||
new MatomoEventListener(List.of(matomoHandler1, matomoHandler2), configurationService, bitstreamService);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -46,17 +54,60 @@ public class MatomoEventListenerTest extends AbstractUnitTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testHandleEvent() {
|
||||
public void testDontHandleGenericViewEventWithMatomoEnabled() {
|
||||
UsageEvent event = Mockito.mock(UsageEvent.class);
|
||||
Mockito.when(event.getAction()).thenReturn(UsageEvent.Action.VIEW);
|
||||
Mockito.when(event.getObject()).thenReturn(Mockito.spy(Item.class));
|
||||
|
||||
Mockito.when(configurationService.getBooleanProperty("matomo.enabled", false))
|
||||
.thenReturn(true);
|
||||
|
||||
matomoEventListener.receiveEvent(event);
|
||||
|
||||
Mockito.verifyNoInteractions(matomoHandler1);
|
||||
Mockito.verifyNoInteractions(matomoHandler2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testHandleBitstreamViewEvent() throws SQLException {
|
||||
UsageEvent event = Mockito.mock(UsageEvent.class);
|
||||
Mockito.when(event.getAction()).thenReturn(UsageEvent.Action.VIEW);
|
||||
|
||||
Bitstream bitstream = Mockito.spy(Bitstream.class);
|
||||
Mockito.when(bitstreamService.isInBundle(Mockito.eq(bitstream), Mockito.eq(Set.of(Constants.CONTENT_BUNDLE_NAME))))
|
||||
.thenReturn(true);
|
||||
|
||||
Mockito.when(event.getObject()).thenReturn(bitstream);
|
||||
|
||||
Mockito.when(configurationService.getBooleanProperty(Mockito.eq("matomo.enabled"), Mockito.eq(false)))
|
||||
.thenReturn(true);
|
||||
Mockito.when(configurationService.getArrayProperty(Mockito.eq("matomo.track.bundles"), Mockito.any()))
|
||||
.thenReturn(new String[] { });
|
||||
|
||||
matomoEventListener.receiveEvent(event);
|
||||
|
||||
Mockito.verifyNoInteractions(matomoHandler1);
|
||||
Mockito.verifyNoInteractions(matomoHandler2);
|
||||
|
||||
// none bundle, will skip processing
|
||||
Mockito.when(configurationService.getArrayProperty(Mockito.eq("matomo.track.bundles"), Mockito.any()))
|
||||
.thenReturn(new String[] {"none"});
|
||||
|
||||
matomoEventListener.receiveEvent(event);
|
||||
|
||||
Mockito.verifyNoMoreInteractions(matomoHandler1);
|
||||
Mockito.verifyNoMoreInteractions(matomoHandler2);
|
||||
|
||||
// default ( original bundle only ) then proceed with the invocation
|
||||
Mockito.when(configurationService.getArrayProperty(Mockito.eq("matomo.track.bundles"), Mockito.any()))
|
||||
.thenReturn(new String[] { Constants.CONTENT_BUNDLE_NAME });
|
||||
|
||||
matomoEventListener.receiveEvent(event);
|
||||
|
||||
Mockito.verify(matomoHandler1, Mockito.times(1)).handleEvent(event);
|
||||
Mockito.verify(matomoHandler2, Mockito.times(1)).handleEvent(event);
|
||||
Mockito.verifyNoMoreInteractions(matomoHandler1, matomoHandler2);
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -6,6 +6,9 @@
|
||||
matomo.enabled = false
|
||||
# Configured `siteid` inside the matomo dashboard
|
||||
matomo.request.siteid = 1
|
||||
# Specifies bitstream's bundle that will be tracked ( default is ORIGINAL )
|
||||
# Add 'none' to disable the tracking for bitstreams
|
||||
# matomo.track.bundles = ORIGINAL
|
||||
|
||||
#---------------------------------------------------------------#
|
||||
#----------------MATOMO CLIENTS CONFIGURATION-------------------#
|
||||
|
Reference in New Issue
Block a user