Merged in coar-notify-7_CST-12752 (pull request #1330)

[CST-12752] handled automatic pattern/services in the LDNConsumer

Approved-by: Stefano Maffei
This commit is contained in:
Mohamed Saber Eskander
2023-11-23 08:55:29 +00:00
committed by Stefano Maffei
11 changed files with 196 additions and 24 deletions

View File

@@ -29,12 +29,14 @@ import org.dspace.app.ldn.factory.NotifyServiceFactory;
import org.dspace.app.ldn.model.Notification; import org.dspace.app.ldn.model.Notification;
import org.dspace.app.ldn.service.LDNMessageService; import org.dspace.app.ldn.service.LDNMessageService;
import org.dspace.app.ldn.service.NotifyPatternToTriggerService; import org.dspace.app.ldn.service.NotifyPatternToTriggerService;
import org.dspace.app.ldn.service.NotifyServiceInboundPatternService;
import org.dspace.content.Bitstream; import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat; import org.dspace.content.BitstreamFormat;
import org.dspace.content.Bundle; import org.dspace.content.Bundle;
import org.dspace.content.Item; import org.dspace.content.Item;
import org.dspace.content.MetadataValue; import org.dspace.content.MetadataValue;
import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.logic.LogicalStatement;
import org.dspace.content.service.BitstreamService; import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.ItemService; import org.dspace.content.service.ItemService;
import org.dspace.core.Constants; import org.dspace.core.Constants;
@@ -45,6 +47,7 @@ import org.dspace.event.Consumer;
import org.dspace.event.Event; import org.dspace.event.Event;
import org.dspace.services.ConfigurationService; import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.utils.DSpace;
import org.dspace.web.ContextUtil; import org.dspace.web.ContextUtil;
/** /**
@@ -55,6 +58,7 @@ import org.dspace.web.ContextUtil;
public class LDNMessageConsumer implements Consumer { public class LDNMessageConsumer implements Consumer {
private NotifyPatternToTriggerService notifyPatternToTriggerService; private NotifyPatternToTriggerService notifyPatternToTriggerService;
private NotifyServiceInboundPatternService inboundPatternService;
private LDNMessageService ldnMessageService; private LDNMessageService ldnMessageService;
private ConfigurationService configurationService; private ConfigurationService configurationService;
private ItemService itemService; private ItemService itemService;
@@ -67,6 +71,7 @@ public class LDNMessageConsumer implements Consumer {
configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
itemService = ContentServiceFactory.getInstance().getItemService(); itemService = ContentServiceFactory.getInstance().getItemService();
bitstreamService = ContentServiceFactory.getInstance().getBitstreamService(); bitstreamService = ContentServiceFactory.getInstance().getBitstreamService();
inboundPatternService = NotifyServiceFactory.getInstance().getNotifyServiceInboundPatternService();
} }
@Override @Override
@@ -77,36 +82,53 @@ public class LDNMessageConsumer implements Consumer {
return; return;
} }
createLDNMessages(context, (Item) event.getSubject(context)); Item item = (Item) event.getSubject(context);
createManualLDNMessages(context, item);
createAutomaticLDNMessages(context, item);
} }
private void createLDNMessages(Context context, Item item) throws SQLException { private void createManualLDNMessages(Context context, Item item) throws SQLException, JsonProcessingException {
List<NotifyPatternToTrigger> patternsToTrigger = List<NotifyPatternToTrigger> patternsToTrigger =
notifyPatternToTriggerService.findByItem(context, item); notifyPatternToTriggerService.findByItem(context, item);
patternsToTrigger.forEach(patternToTrigger -> { for (NotifyPatternToTrigger patternToTrigger : patternsToTrigger) {
try { createLDNMessage(context,patternToTrigger.getItem(),
createLDNMessage(context, patternToTrigger); patternToTrigger.getNotifyService(), patternToTrigger.getPattern());
} catch (Exception e) { }
throw new RuntimeException(e);
}
});
} }
private void createLDNMessage(Context context, NotifyPatternToTrigger patternToTrigger) private void createAutomaticLDNMessages(Context context, Item item) throws SQLException, JsonProcessingException {
List<NotifyServiceInboundPattern> inboundPatterns = inboundPatternService.findAutomaticPatterns(context);
for (NotifyServiceInboundPattern inboundPattern : inboundPatterns) {
if (inboundPattern.getConstraint() == null ||
evaluateFilter(context, item, inboundPattern.getConstraint())) {
createLDNMessage(context, item, inboundPattern.getNotifyService(), inboundPattern.getPattern());
}
}
}
private boolean evaluateFilter(Context context, Item item, String constraint) {
LogicalStatement filter =
new DSpace().getServiceManager().getServiceByName(constraint, LogicalStatement.class);
return filter != null && filter.getResult(context, item);
}
private void createLDNMessage(Context context, Item item, NotifyServiceEntity service, String pattern)
throws SQLException, JsonMappingException, JsonProcessingException { throws SQLException, JsonMappingException, JsonProcessingException {
LDN ldn = getLDNMessage(patternToTrigger.getPattern()); LDN ldn = getLDNMessage(pattern);
LDNMessageEntity ldnMessage = LDNMessageEntity ldnMessage =
ldnMessageService.create(context, format("urn:uuid:%s", UUID.randomUUID())); ldnMessageService.create(context, format("urn:uuid:%s", UUID.randomUUID()));
ldnMessage.setObject(patternToTrigger.getItem()); ldnMessage.setObject(item);
ldnMessage.setTarget(patternToTrigger.getNotifyService()); ldnMessage.setTarget(service);
ldnMessage.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_QUEUED); ldnMessage.setQueueStatus(LDNMessageEntity.QUEUE_STATUS_QUEUED);
ldnMessage.setQueueTimeout(new Date()); ldnMessage.setQueueTimeout(new Date());
appendGeneratedMessage(ldn, ldnMessage, patternToTrigger.getPattern()); appendGeneratedMessage(ldn, ldnMessage, pattern);
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
Notification notification = mapper.readValue(ldnMessage.getMessage(), Notification.class); Notification notification = mapper.readValue(ldnMessage.getMessage(), Notification.class);

View File

@@ -8,6 +8,7 @@
package org.dspace.app.ldn.dao; package org.dspace.app.ldn.dao;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List;
import org.dspace.app.ldn.NotifyServiceEntity; import org.dspace.app.ldn.NotifyServiceEntity;
import org.dspace.app.ldn.NotifyServiceInboundPattern; import org.dspace.app.ldn.NotifyServiceInboundPattern;
@@ -35,4 +36,12 @@ public interface NotifyServiceInboundPatternDao extends GenericDAO<NotifyService
public NotifyServiceInboundPattern findByServiceAndPattern(Context context, public NotifyServiceInboundPattern findByServiceAndPattern(Context context,
NotifyServiceEntity notifyServiceEntity, NotifyServiceEntity notifyServiceEntity,
String pattern) throws SQLException; String pattern) throws SQLException;
/**
* find all automatic notifyServiceInboundPatterns
*
* @param context the context
* @return all automatic notifyServiceInboundPatterns
* @throws SQLException if database error
*/
List<NotifyServiceInboundPattern> findAutomaticPatterns(Context context) throws SQLException;
} }

View File

@@ -8,6 +8,7 @@
package org.dspace.app.ldn.dao.impl; package org.dspace.app.ldn.dao.impl;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
@@ -42,4 +43,17 @@ public class NotifyServiceInboundPatternDaoImpl
)); ));
return uniqueResult(context, criteriaQuery, false, NotifyServiceInboundPattern.class); return uniqueResult(context, criteriaQuery, false, NotifyServiceInboundPattern.class);
} }
@Override
public List<NotifyServiceInboundPattern> findAutomaticPatterns(Context context) throws SQLException {
CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context);
CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, NotifyServiceInboundPattern.class);
Root<NotifyServiceInboundPattern> inboundPatternRoot = criteriaQuery.from(NotifyServiceInboundPattern.class);
criteriaQuery.select(inboundPatternRoot);
criteriaQuery.where(
criteriaBuilder.equal(
inboundPatternRoot.get(NotifyServiceInboundPattern_.automatic), true)
);
return list(context, criteriaQuery, false, NotifyServiceInboundPattern.class, -1, -1);
}
} }

View File

@@ -8,6 +8,7 @@
package org.dspace.app.ldn.service; package org.dspace.app.ldn.service;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List;
import org.dspace.app.ldn.NotifyServiceEntity; import org.dspace.app.ldn.NotifyServiceEntity;
import org.dspace.app.ldn.NotifyServiceInboundPattern; import org.dspace.app.ldn.NotifyServiceInboundPattern;
@@ -35,6 +36,15 @@ public interface NotifyServiceInboundPatternService {
NotifyServiceEntity notifyServiceEntity, NotifyServiceEntity notifyServiceEntity,
String pattern) throws SQLException; String pattern) throws SQLException;
/**
* find all automatic notifyServiceInboundPatterns
*
* @param context the context
* @return all automatic notifyServiceInboundPatterns
* @throws SQLException if database error
*/
public List<NotifyServiceInboundPattern> findAutomaticPatterns(Context context) throws SQLException;
/** /**
* create new notifyServiceInboundPattern * create new notifyServiceInboundPattern
* *

View File

@@ -8,6 +8,7 @@
package org.dspace.app.ldn.service.impl; package org.dspace.app.ldn.service.impl;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List;
import org.dspace.app.ldn.NotifyServiceEntity; import org.dspace.app.ldn.NotifyServiceEntity;
import org.dspace.app.ldn.NotifyServiceInboundPattern; import org.dspace.app.ldn.NotifyServiceInboundPattern;
@@ -33,6 +34,11 @@ public class NotifyServiceInboundPatternServiceImpl implements NotifyServiceInbo
return inboundPatternDao.findByServiceAndPattern(context, notifyServiceEntity, pattern); return inboundPatternDao.findByServiceAndPattern(context, notifyServiceEntity, pattern);
} }
@Override
public List<NotifyServiceInboundPattern> findAutomaticPatterns(Context context) throws SQLException {
return inboundPatternDao.findAutomaticPatterns(context);
}
@Override @Override
public NotifyServiceInboundPattern create(Context context, NotifyServiceEntity notifyServiceEntity) public NotifyServiceInboundPattern create(Context context, NotifyServiceEntity notifyServiceEntity)
throws SQLException { throws SQLException {

View File

@@ -8,7 +8,6 @@
package org.dspace.content; package org.dspace.content;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.dspace.app.ldn.ItemFilter; import org.dspace.app.ldn.ItemFilter;
@@ -38,16 +37,12 @@ public class ItemFilterServiceImpl implements ItemFilterService {
@Override @Override
public List<ItemFilter> findAll() { public List<ItemFilter> findAll() {
return serviceManager.getServicesNames() return serviceManager.getServicesWithNamesByType(LogicalStatement.class)
.keySet()
.stream() .stream()
.filter(id -> isLogicalStatement(id)) .sorted()
.map(id -> new ItemFilter(id)) .map(ItemFilter::new)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private boolean isLogicalStatement(String id) {
return Objects.nonNull(
serviceManager.getServiceByName(id, LogicalStatement.class)
);
}
} }

View File

@@ -30,6 +30,7 @@ import org.dspace.builder.CollectionBuilder;
import org.dspace.builder.CommunityBuilder; import org.dspace.builder.CommunityBuilder;
import org.dspace.builder.EPersonBuilder; import org.dspace.builder.EPersonBuilder;
import org.dspace.builder.NotifyServiceBuilder; import org.dspace.builder.NotifyServiceBuilder;
import org.dspace.builder.NotifyServiceInboundPatternBuilder;
import org.dspace.builder.WorkspaceItemBuilder; import org.dspace.builder.WorkspaceItemBuilder;
import org.dspace.content.Collection; import org.dspace.content.Collection;
import org.dspace.content.Item; import org.dspace.content.Item;
@@ -159,6 +160,83 @@ public class LDNMessageConsumerIT extends AbstractIntegrationTestWithDatabase {
} }
@Test
public void testLDNMessageConsumerRequestReviewAutomatic() throws Exception {
context.turnOffAuthorisationSystem();
NotifyServiceEntity notifyService =
NotifyServiceBuilder.createNotifyServiceBuilder(context)
.withName("service name")
.withDescription("service description")
.withUrl("https://service.ldn.org/about")
.withLdnUrl("https://service.ldn.org/inbox")
.build();
NotifyServiceInboundPatternBuilder.createNotifyServiceInboundPatternBuilder(context, notifyService)
.withPattern("request-review")
.withConstraint("simple-demo_filter")
.isAutomatic(true)
.build();
//3. a workspace item ready to go
WorkspaceItem workspaceItem =
WorkspaceItemBuilder.createWorkspaceItem(context, collection)
.withTitle("demo Item")
.withIssueDate("2023-11-20")
.withFulltext("test.txt", "test", InputStream.nullInputStream())
.grantLicense()
.build();
WorkflowItem workflowItem = workflowService.start(context, workspaceItem);
Item item = workflowItem.getItem();
context.dispatchEvents();
context.restoreAuthSystemState();
LDNMessageEntity ldnMessage =
ldnMessageService.findAll(context).stream().findFirst().orElse(null);
assertThat(notifyService, matchesNotifyServiceEntity(ldnMessage.getTarget()));
assertEquals(workflowItem.getItem().getID(), ldnMessage.getObject().getID());
assertEquals(QUEUE_STATUS_QUEUED, ldnMessage.getQueueStatus());
assertNull(ldnMessage.getOrigin());
assertNotNull(ldnMessage.getMessage());
ObjectMapper mapper = new ObjectMapper();
Notification notification = mapper.readValue(ldnMessage.getMessage(), Notification.class);
// check id
assertThat(notification.getId(), containsString("urn:uuid:"));
// check object
assertEquals(notification.getObject().getId(),
configurationService.getProperty("dspace.ui.url") + "/handle/" + item.getHandle());
assertEquals(notification.getObject().getIetfCiteAs(),
itemService.getMetadataByMetadataString(item, "dc.identifier.uri").get(0).getValue());
assertEquals(notification.getObject().getUrl().getId(),
configurationService.getProperty("dspace.ui.url") + "/bitstreams/" +
item.getBundles(Constants.CONTENT_BUNDLE_NAME).get(0).getBitstreams().get(0).getID() + "/download");
// check target
assertEquals(notification.getTarget().getId(), notifyService.getUrl());
assertEquals(notification.getTarget().getInbox(), notifyService.getLdnUrl());
assertEquals(notification.getTarget().getType(), Set.of("Service"));
// check origin
assertEquals(notification.getOrigin().getId(), configurationService.getProperty("dspace.ui.url"));
assertEquals(notification.getOrigin().getInbox(), configurationService.getProperty("ldn.notify.inbox"));
assertEquals(notification.getOrigin().getType(), Set.of("Service"));
// check actor
assertEquals(notification.getActor().getId(), configurationService.getProperty("dspace.ui.url"));
assertEquals(notification.getActor().getName(), configurationService.getProperty("dspace.name"));
assertEquals(notification.getOrigin().getType(), Set.of("Service"));
// check types
assertEquals(notification.getType(), Set.of("coar-notify:ReviewAction", "Offer"));
}
@Test @Test
public void testLDNMessageConsumerRequestEndorsement() throws Exception { public void testLDNMessageConsumerRequestEndorsement() throws Exception {
context.turnOffAuthorisationSystem(); context.turnOffAuthorisationSystem();

View File

@@ -76,6 +76,19 @@ public interface ServiceManager {
*/ */
public List<String> getServicesNames(); public List<String> getServicesNames();
/**
* Allows developers to get the desired service singleton by the provided type. <br>
* This should return all instantiated objects of the type specified with their names
* (may not all be singletons).
*
* @param <T> Class type
* @param type the type for the requested service (this will typically be the interface class but can be concrete
* as well)
* @return map with service's name and service singletons
* @return the list of all current registered services
*/
public <T> Map<String, T> getServicesWithNamesByType(Class<T> type);
/** /**
* Allows adding singleton services and providers in at runtime or * Allows adding singleton services and providers in at runtime or
* after the service manager has started up. * after the service manager has started up.

View File

@@ -504,6 +504,21 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
return beanNames; return beanNames;
} }
@Override
public <T> Map<String, T> getServicesWithNamesByType(Class<T> type) {
checkRunning();
if (type == null) {
throw new IllegalArgumentException("type cannot be null");
}
try {
return applicationContext.getBeansOfType(type, true, true);
} catch (BeansException e) {
throw new RuntimeException("Failed to get beans of type (" + type + "): " + e.getMessage(), e);
}
}
@Override @Override
public boolean isServiceExists(String name) { public boolean isServiceExists(String name) {
checkRunning(); checkRunning();

View File

@@ -80,6 +80,11 @@ public class MockServiceManagerSystem implements ServiceManagerSystem {
return this.sms.getServicesNames(); return this.sms.getServicesNames();
} }
@Override
public <T> Map<String, T> getServicesWithNamesByType(Class<T> type) {
return this.sms.getServicesWithNamesByType(type);
}
/* (non-Javadoc) /* (non-Javadoc)
* @see org.dspace.kernel.ServiceManager#isServiceExists(java.lang.String) * @see org.dspace.kernel.ServiceManager#isServiceExists(java.lang.String)
*/ */

View File

@@ -13,6 +13,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -84,6 +85,10 @@ public class ProviderStackTest {
return new ArrayList<String>(); return new ArrayList<String>();
} }
public <T> Map<String, T> getServicesWithNamesByType(Class<T> type) {
return new HashMap<>();
}
public boolean isServiceExists(String name) { public boolean isServiceExists(String name) {
return false; return false;
} }