From 8fc48d39a5786b1ff585a8ea37bb8aad4ae6982b Mon Sep 17 00:00:00 2001 From: David Eckhard Date: Wed, 13 Sep 2023 14:43:03 +0200 Subject: [PATCH] access-requests: send notification on submit action --- invenio_rdm_records/notifications/builders.py | 126 ++++++++++++++++++ .../requests/access/requests.py | 13 +- .../guest-access-request.submit.jinja | 36 +++++ .../user-access-request.submit.jinja | 36 +++++ tests/conftest.py | 4 + tests/resources/test_access_requests.py | 17 ++- 6 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 invenio_rdm_records/templates/semantic-ui/invenio_notifications/guest-access-request.submit.jinja create mode 100644 invenio_rdm_records/templates/semantic-ui/invenio_notifications/user-access-request.submit.jinja diff --git a/invenio_rdm_records/notifications/builders.py b/invenio_rdm_records/notifications/builders.py index d2321654d..9bc9bf3f8 100644 --- a/invenio_rdm_records/notifications/builders.py +++ b/invenio_rdm_records/notifications/builders.py @@ -17,6 +17,7 @@ from invenio_users_resources.notifications.filters import UserPreferencesRecipientFilter from invenio_users_resources.notifications.generators import ( EmailRecipient, + IfUserRecipient, UserRecipient, ) @@ -98,6 +99,51 @@ def build(cls, record, email, verify_url): ] +class GuestAccessRequestSubmitNotificationBuilder(NotificationBuilder): + """Notification builder for guest access requests.""" + + type = "guest-access-request.submit" + + @classmethod + def build(cls, request): + """Build notification with request context.""" + return Notification( + type=cls.type, + context={ + "request": EntityResolverRegistry.reference_entity(request), + "receiver_entity": request.receiver.reference_dict, # will not be resolved for easier lookup for recipients + }, + ) + + context = [ + EntityResolve(key="request"), + EntityResolve(key="request.created_by"), + EntityResolve(key="request.topic"), + EntityResolve(key="request.receiver"), + ] + + recipients = [ + # Currently only these two are allowed. Adapt as needed. + IfUserRecipient( + key="receiver_entity", + then_=[UserRecipient(key="request.receiver")], + else_=[ + CommunityMembersRecipient( + key="request.receiver", roles=["curator", "owner"] + ) + ], + ) + ] + + recipient_filters = [ + UserPreferencesRecipientFilter(), + ] + + recipient_backends = [ + UserEmailBackend(), + ] + + class GuestAccessRequestAcceptNotificationBuilder(NotificationBuilder): """Notification builder for user access requests.""" @@ -131,6 +177,86 @@ def build(cls, request, access_url): ] +class UserAccessRequestSubmitNotificationBuilder(NotificationBuilder): + """Notification builder for user access requests.""" + + type = "user-access-request.submit" + + @classmethod + def build(cls, request): + """Build notification with request context.""" + return Notification( + type=cls.type, + context={ + "request": EntityResolverRegistry.reference_entity(request), + "receiver_entity": request.receiver.reference_dict, # will not be resolved for easier lookup for recipients + }, + ) + + context = [ + EntityResolve(key="request"), + EntityResolve(key="request.created_by"), + EntityResolve(key="request.topic"), + EntityResolve(key="request.receiver"), + ] + + recipients = [ + # Currently only these two are allowed. Adapt as needed. + IfUserRecipient( + key="receiver_entity", + then_=[UserRecipient(key="request.receiver")], + else_=[ + CommunityMembersRecipient( + key="request.receiver", roles=["curator", "owner"] + ) + ], + ) + ] + + recipient_filters = [ + UserPreferencesRecipientFilter(), + ] + + recipient_backends = [ + UserEmailBackend(), + ] + + +class UserAccessRequestAcceptNotificationBuilder(NotificationBuilder): + """Notification builder for user access requests.""" + + type = "user-access-request.accept" + + @classmethod + def build(cls, request): + """Build notification with request context.""" + return Notification( + type=cls.type, + context={ + "request": EntityResolverRegistry.reference_entity(request), + }, + ) + + context = [ + EntityResolve(key="request"), + EntityResolve(key="request.created_by"), + EntityResolve(key="request.topic"), + EntityResolve(key="request.receiver"), + ] + + recipients = [ + UserRecipient(key="request.created_by"), + ] + + recipient_filters = [ + UserPreferencesRecipientFilter(), + ] + + recipient_backends = [ + UserEmailBackend(), + ] + + class CommunityInclusionActionNotificationBuilder(NotificationBuilder): """Notification builder for inclusion actions.""" diff --git a/invenio_rdm_records/requests/access/requests.py b/invenio_rdm_records/requests/access/requests.py index 930636210..c316b9902 100644 --- a/invenio_rdm_records/requests/access/requests.py +++ b/invenio_rdm_records/requests/access/requests.py @@ -22,7 +22,9 @@ from invenio_rdm_records.notifications.builders import ( GuestAccessRequestAcceptNotificationBuilder, + GuestAccessRequestSubmitNotificationBuilder, UserAccessRequestAcceptNotificationBuilder, + UserAccessRequestSubmitNotificationBuilder, ) from ...proxies import current_rdm_records_service as service @@ -37,6 +39,11 @@ class UserSubmitAction(actions.SubmitAction): def execute(self, identity, uow): """Execute the submit action.""" self.request["title"] = self.request.topic.resolve().metadata["title"] + uow.register( + NotificationOp( + UserAccessRequestSubmitNotificationBuilder.build(request=self.request) + ) + ) super().execute(identity, uow) @@ -47,7 +54,11 @@ def execute(self, identity, uow): """Execute the submit action.""" record = self.request.topic.resolve() self.request["title"] = record.metadata["title"] - + uow.register( + NotificationOp( + GuestAccessRequestSubmitNotificationBuilder.build(request=self.request) + ) + ) super().execute(identity, uow) diff --git a/invenio_rdm_records/templates/semantic-ui/invenio_notifications/guest-access-request.submit.jinja b/invenio_rdm_records/templates/semantic-ui/invenio_notifications/guest-access-request.submit.jinja new file mode 100644 index 000000000..30c0b2ca1 --- /dev/null +++ b/invenio_rdm_records/templates/semantic-ui/invenio_notifications/guest-access-request.submit.jinja @@ -0,0 +1,36 @@ +{% set access_request = notification.context.request %} +{% set creator_email = access_request.created_by %} +{% set record = access_request.topic %} + +{% set record_title = record.metadata.title %} +{% set request_id = access_request.id %} +{# TODO: use request.links.self_html when issue issue is resolved: https://github.com/inveniosoftware/invenio-rdm-records/issues/1327 #} +{% set request_link = "{ui}/me/requests/{id}".format( + ui=config.SITE_UI_URL, id=request_id + ) +%} + +{%- block subject -%} + {{ _("New access request for '{record_title}'.").format(record_title=record_title) }} +{%- endblock subject -%} + +{%- block html_body -%} +

+ {{ _("New access request for '{record_title}' was submitted.").format(record_title=record_title) }} +

+ + {{ _("Check out the access request") }} +{%- endblock html_body -%} + +{%- block plain_body -%} +{{ _("New access request for '{record_title}' was submitted.").format(record_title=record_title) }} + +{{ _("Checkout the access request: {url}").format(url=request_link) }} +{%- endblock plain_body -%} + +{# Markdown for Slack/Mattermost/chat #} +{%- block md_body -%} +{{ _("New access request for *{record_title}* was submitted.").format(record_title=record_title) }} + +[{{ _("Checkout the access request") }}]({{ request_link }}) +{%- endblock md_body -%} diff --git a/invenio_rdm_records/templates/semantic-ui/invenio_notifications/user-access-request.submit.jinja b/invenio_rdm_records/templates/semantic-ui/invenio_notifications/user-access-request.submit.jinja new file mode 100644 index 000000000..8de308c74 --- /dev/null +++ b/invenio_rdm_records/templates/semantic-ui/invenio_notifications/user-access-request.submit.jinja @@ -0,0 +1,36 @@ +{% set access_request = notification.context.request %} +{% set creator = access_request.created_by %} +{% set record = access_request.topic %} + +{% set record_title = record.metadata.title %} +{% set request_id = access_request.id %} +{# TODO: use request.links.self_html when issue issue is resolved: https://github.com/inveniosoftware/invenio-rdm-records/issues/1327 #} +{% set request_link = "{ui}/me/requests/{id}".format( + ui=config.SITE_UI_URL, id=request_id + ) +%} + +{%- block subject -%} + {{ _("New access request for '{record_title}'.").format(record_title=record_title) }} +{%- endblock subject -%} + +{%- block html_body -%} +

+ {{ _("New access request for '{record_title}' was submitted.").format(record_title=record_title) }} +

+ + {{ _("Check out the access request") }} +{%- endblock html_body -%} + +{%- block plain_body -%} +{{ _("New access request for '{record_title}' was submitted.").format(record_title=record_title) }} + +{{ _("Checkout the access request: {url}").format(url=request_link) }} +{%- endblock plain_body -%} + +{# Markdown for Slack/Mattermost/chat #} +{%- block md_body -%} +{{ _("New access request for *{record_title}* was submitted.").format(record_title=record_title) }} + +[{{ _("Checkout the access request") }}]({{ request_link }}) +{%- endblock md_body -%} diff --git a/tests/conftest.py b/tests/conftest.py index a0c1d0ac1..58907be53 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -93,8 +93,10 @@ CommunityInclusionExpireNotificationBuilder, CommunityInclusionSubmittedNotificationBuilder, GuestAccessRequestAcceptNotificationBuilder, + GuestAccessRequestSubmitNotificationBuilder, GuestAccessRequestTokenCreateNotificationBuilder, UserAccessRequestAcceptNotificationBuilder, + UserAccessRequestSubmitNotificationBuilder, ) from invenio_rdm_records.proxies import current_rdm_records_service from invenio_rdm_records.records.api import RDMDraft, RDMParent, RDMRecord @@ -298,7 +300,9 @@ def app_config(app_config, mock_datacite_client): CommunityInvitationSubmittedNotificationBuilder.type: DummyNotificationBuilder, GuestAccessRequestTokenCreateNotificationBuilder.type: GuestAccessRequestTokenCreateNotificationBuilder, GuestAccessRequestAcceptNotificationBuilder.type: GuestAccessRequestAcceptNotificationBuilder, + GuestAccessRequestSubmitNotificationBuilder.type: GuestAccessRequestSubmitNotificationBuilder, UserAccessRequestAcceptNotificationBuilder.type: UserAccessRequestAcceptNotificationBuilder, + UserAccessRequestSubmitNotificationBuilder.type: UserAccessRequestSubmitNotificationBuilder, } # Specifying default resolvers. Will only be used in specific test cases. diff --git a/tests/resources/test_access_requests.py b/tests/resources/test_access_requests.py index bdcbc2a0e..561aa39cc 100644 --- a/tests/resources/test_access_requests.py +++ b/tests/resources/test_access_requests.py @@ -98,11 +98,16 @@ def test_simple_guest_access_request_flow(running_app, client, users, minimal_re identity=guest_identity, token=args["access_request_token"] ) + assert len(outbox) == 2 + submit_message = outbox[1] + # TODO: update to `req["links"]["self_html"]` when addressing https://github.com/inveniosoftware/invenio-rdm-records/issues/1327 + assert "/me/requests/{}".format(request.id) in submit_message.html + # Accept the request # This is expected to send out another email, containing the new secret link current_requests_service.execute_action(identity, request.id, "accept", data={}) - assert len(outbox) == 2 - success_message = outbox[1] + assert len(outbox) == 3 + success_message = outbox[2] match = link_regex.search(str(success_message.body)) assert match access_url = match.group(1) @@ -184,11 +189,15 @@ def test_simple_user_access_request_flow(running_app, client, users, minimal_rec ) request_id = response.json["id"] assert response.status_code == 200 + assert len(outbox) == 1 + submit_message = outbox[0] + # TODO: update to `req["links"]["self_html"]` when addressing https://github.com/inveniosoftware/invenio-rdm-records/issues/1327 + assert "/me/requests/{}".format(request_id) in submit_message.html # The record owner approves the access request current_requests_service.execute_action(identity, request_id, "accept", data={}) - assert len(outbox) == 1 - success_message = outbox[0] + assert len(outbox) == 2 + success_message = outbox[1] assert record.to_dict()["links"]["self_html"] in success_message.body # Now, the user has permission to view the record's files!