Skip to content

Commit 7be6af8

Browse files
authored
Merge pull request #11696 from IQSS/11648-notifications-api-extension
Notifications API extension and new notifications generated by API operations
2 parents ba839a1 + 877e428 commit 7be6af8

File tree

16 files changed

+1248
-270
lines changed

16 files changed

+1248
-270
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# getAllNotificationsForUser API extension
2+
3+
- Extended endpoint getAllNotificationsForUser(``/notifications/all``), which now supports an optional query parameter ``inAppNotificationFormat`` which, if sent as ``true``, retrieves the fields needed to build the in-app notifications for the Notifications section of the Dataverse UI, omitting fields related to email notifications. See also #11648 and #11696.
4+
5+
# Notifications triggered by API endpoints
6+
7+
The addDataset and addDataverse API endpoints now trigger user notifications upon successful execution. See also #1342 and #11696.

doc/sphinx-guides/source/api/native-api.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6001,6 +6001,35 @@ The expected OK (200) response looks something like this:
60016001
}
60026002
...
60036003
6004+
This endpoint supports an optional query parameter ``inAppNotificationFormat`` which, if sent as ``true``, retrieves the fields needed to build the in-app notifications for the Notifications section of the Dataverse UI, omitting fields related to email notifications.
6005+
6006+
.. code-block:: bash
6007+
6008+
curl -H "X-Dataverse-key:$API_TOKEN" "$SERVER_URL/api/notifications/all?inAppNotificationFormat=true"
6009+
6010+
The expected OK (200) response looks something like this:
6011+
6012+
.. code-block:: text
6013+
6014+
{
6015+
"status": "OK",
6016+
"data": {
6017+
"notifications": [
6018+
{
6019+
"id": 79,
6020+
"type": "CREATEACC",
6021+
"displayAsRead": false,
6022+
"sentTimestamp": "2025-08-08T08:00:16Z",
6023+
"installationBrandName": "Your Installation Name",
6024+
"userGuidesBaseUrl": "https://guides.dataverse.org",
6025+
"userGuidesVersion": "6.7.1",
6026+
"userGuidesSectionPath": "user/index.html"
6027+
}
6028+
]
6029+
}
6030+
}
6031+
...
6032+
60046033
Get Unread Count
60056034
~~~~~~~~~~~~~~~~
60066035

src/main/java/edu/harvard/iq/dataverse/PermissionServiceBean.java

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,13 @@
1313
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
1414
import edu.harvard.iq.dataverse.authorization.users.User;
1515
import edu.harvard.iq.dataverse.engine.command.Command;
16-
import java.util.EnumSet;
17-
import java.util.Map;
18-
import java.util.Set;
16+
17+
import java.util.*;
1918
import java.util.logging.Logger;
2019
import jakarta.ejb.EJB;
2120
import jakarta.ejb.Stateless;
2221
import jakarta.inject.Inject;
2322
import jakarta.inject.Named;
24-
import java.util.HashSet;
25-
import java.util.List;
2623
import jakarta.persistence.EntityManager;
2724
import jakarta.persistence.PersistenceContext;
2825
import static edu.harvard.iq.dataverse.engine.command.CommandHelper.CH;
@@ -34,11 +31,9 @@
3431
import edu.harvard.iq.dataverse.workflow.PendingWorkflowInvocation;
3532
import edu.harvard.iq.dataverse.workflow.WorkflowServiceBean;
3633

37-
import java.util.Arrays;
38-
import java.util.Collections;
39-
import java.util.HashMap;
40-
import java.util.LinkedList;
4134
import java.util.stream.Collectors;
35+
import java.util.stream.Stream;
36+
4237
import static java.util.stream.Collectors.toList;
4338
import jakarta.persistence.Query;
4439
import jakarta.persistence.criteria.CriteriaBuilder;
@@ -926,6 +921,7 @@ private boolean hasUnrestrictedReleasedFiles(DatasetVersion targetDatasetVersion
926921
public List<Dataverse> findPermittedCollections(DataverseRequest request, AuthenticatedUser user, Permission permission) {
927922
return findPermittedCollections(request, user, 1 << permission.ordinal());
928923
}
924+
929925
public List<Dataverse> findPermittedCollections(DataverseRequest request, AuthenticatedUser user, int permissionBit) {
930926
if (user != null) {
931927
// IP Group - Only check IP if a User is calling for themself
@@ -963,5 +959,31 @@ public List<Dataverse> findPermittedCollections(DataverseRequest request, Authen
963959
}
964960
return null;
965961
}
962+
963+
/**
964+
* Calculates the complete list of role assignments for a given user on a DvObject.
965+
* This includes roles assigned directly to the user and roles inherited from any groups
966+
* the user is a member of.
967+
* <p>
968+
* This method's logic is based on the private method {@code getRoleStringFromUser}
969+
* in the {@code DataverseUserPage} class, which produces a concatenated string of
970+
* effective user role names required for displaying role-related user notifications.
971+
* The common logic from these two methods may be centralized in the future to
972+
* avoid code duplication.
973+
*
974+
* @param user The authenticated user whose roles are being checked.
975+
* @param dvObject The dataverse object to check for role assignments.
976+
* @return A List containing all effective RoleAssignments for the user. Never null.
977+
*/
978+
public List<RoleAssignment> getEffectiveRoleAssignments(AuthenticatedUser user, DvObject dvObject) {
979+
Stream<RoleAssignment> directAssignments = assignmentsFor(user, dvObject).stream();
980+
981+
Stream<RoleAssignment> groupAssignments = groupService.groupsFor(user, dvObject)
982+
.stream()
983+
.flatMap(group -> assignmentsFor(group, dvObject).stream());
984+
985+
return Stream.concat(directAssignments, groupAssignments)
986+
.collect(Collectors.toList());
987+
}
966988
}
967989

src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public Response addDataverse(@Context ContainerRequestContext crc, String body,
151151
}
152152

153153
AuthenticatedUser u = getRequestAuthenticatedUserOrDie(crc);
154-
newDataverse = execCommand(new CreateDataverseCommand(newDataverse, createDataverseRequest(u), facets, inputLevels, metadataBlocks));
154+
newDataverse = execCommand(new CreateDataverseCommand(newDataverse, createDataverseRequest(u), facets, inputLevels, metadataBlocks, true));
155155
return created("/dataverses/" + newDataverse.getAlias(), json(newDataverse));
156156

157157
} catch (WrappedResponse ww) {
@@ -407,9 +407,9 @@ public Response createDataset(@Context ContainerRequestContext crc, String jsonB
407407
ds.setIdentifier(null);
408408
ds.setProtocol(null);
409409
ds.setGlobalIdCreateTime(null);
410-
Dataset managedDs = null;
410+
Dataset managedDs;
411411
try {
412-
managedDs = execCommand(new CreateNewDatasetCommand(ds, createDataverseRequest(u), null, validate));
412+
managedDs = execCommand(new CreateNewDatasetCommand(ds, createDataverseRequest(u), validate, true));
413413
} catch (WrappedResponse ww) {
414414
Throwable cause = ww.getCause();
415415
StringBuilder sb = new StringBuilder();

0 commit comments

Comments
 (0)