Skip to content

Commit 0496277

Browse files
committed
membership-request [inveniosoftware#855]: implement expiration flow
1 parent 7ad67f8 commit 0496277

File tree

6 files changed

+77
-20
lines changed

6 files changed

+77
-20
lines changed

Diff for: invenio_communities/config.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,11 @@
228228
}
229229
"""Available membership requests sort options."""
230230

231-
COMMUNITIES_INVITATIONS_EXPIRES_IN = timedelta(days=30)
232-
""""Default amount of time before an invitation expires."""
231+
COMMUNITIES_MEMBER_REQUESTS_EXPIRE_IN = timedelta(days=30)
232+
""""Default amount of time before a member request expires.
233+
234+
Replaces COMMUNITIES_INVITATIONS_EXPIRES_IN .
235+
"""
233236

234237
COMMUNITIES_LOGO_MAX_FILE_SIZE = 10**6
235238
"""Community logo size quota, in bytes."""

Diff for: invenio_communities/members/services/request.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,17 @@ def execute(self, identity, uow):
156156
super().execute(identity, uow)
157157

158158

159-
# TODO: Expiration flow: ExpireAction
159+
class ExpireMembershipRequestAction(actions.ExpireAction):
160+
"""Expire membership request action.
161+
162+
Triggered by task in invenio-requests.
163+
"""
164+
165+
def execute(self, identity, uow):
166+
"""Execute action."""
167+
service().close_member_request(system_identity, self.request.id, uow=uow)
168+
# TODO: Notification flow: Investigate notifications
169+
super().execute(identity, uow)
160170

161171

162172
class AcceptMembershipRequestAction(actions.AcceptAction):
@@ -181,6 +191,7 @@ class MembershipRequestRequestType(RequestType):
181191
"cancel": CancelMembershipRequestAction,
182192
"accept": AcceptMembershipRequestAction,
183193
"decline": DeclineMembershipRequestAction,
194+
"expire": ExpireMembershipRequestAction,
184195
}
185196

186197
creator_can_be_none = False

Diff for: invenio_communities/members/services/schemas.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,4 @@ def get_permissions(self, obj):
247247
community_id=obj.community_id,
248248
member=obj,
249249
)
250-
return {
251-
"can_update_role": is_open and can_update
252-
}
250+
return {"can_update_role": is_open and can_update}

Diff for: invenio_communities/members/services/service.py

+15-10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"""Members service."""
1111

1212
from datetime import datetime, timezone
13+
from warnings import warn
1314

1415
from flask import current_app
1516
from invenio_access.permissions import system_identity
@@ -52,12 +53,18 @@
5253
)
5354

5455

55-
def invite_expires_at():
56-
"""Get the invitation expiration date."""
57-
return (
58-
datetime.utcnow().replace(tzinfo=timezone.utc)
59-
+ current_app.config["COMMUNITIES_INVITATIONS_EXPIRES_IN"]
60-
)
56+
def member_request_expires_at():
57+
"""Get the request expiration date."""
58+
expires_in_delta = current_app.config.get("COMMUNITIES_INVITATIONS_EXPIRES_IN")
59+
if expires_in_delta:
60+
# Deprecated
61+
# TODO: Remove deprecation warning in v14
62+
warn(
63+
"COMMUNITIES_INVITATIONS_EXPIRES_IN is deprecated. Use COMMUNITIES_MEMBER_REQUESTS_EXPIRE_IN instead.", # noqa
64+
DeprecationWarning,
65+
)
66+
expires_in_delta = current_app.config["COMMUNITIES_MEMBER_REQUESTS_EXPIRE_IN"]
67+
return datetime.now(tz=timezone.utc) + expires_in_delta
6168

6269

6370
class MemberService(RecordService):
@@ -712,7 +719,7 @@ def _invite_factory(self, identity, community, role, visible, member, message, u
712719
# TODO: perhaps topic should be the actual membership record
713720
# instead
714721
topic=community,
715-
expires_at=invite_expires_at(),
722+
expires_at=member_request_expires_at(),
716723
uow=uow,
717724
)
718725

@@ -811,14 +818,12 @@ def request_membership(self, identity, community_id, data, uow=None):
811818
identity,
812819
data={
813820
"title": title,
814-
# "description": description,
815821
},
816822
request_type=MembershipRequestRequestType,
817823
receiver=community,
818824
creator={"user": str(identity.user.id)},
819825
topic=community, # user instead?
820-
# TODO: Expiration flow: Consider expiration
821-
# expires_at=invite_expires_at(),
826+
expires_at=member_request_expires_at(),
822827
uow=uow,
823828
)
824829

Diff for: tests/members/test_members_resource.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ def test_get_membership_requests(
467467
assert "id" in hit["request"]
468468
assert "status" in hit["request"]
469469
assert "expires_at" in hit["request"]
470+
assert hit["request"]["expires_at"] is not None
470471
# hits > hit > links
471472
request_id = hit["request"]["id"]
472473
expected_links = {
@@ -478,6 +479,3 @@ def test_get_membership_requests(
478479
assert expected_links == hit["links"]
479480
# hits > hit > permissions
480481
assert hit["permissions"]["can_update_role"] is True
481-
482-
# TODO: Expiration flow : assess if expiration makes sense for membership requests.
483-
# assert hit["request"]["expires_at"] is not None

Diff for: tests/members/test_members_services.py

+43-1
Original file line numberDiff line numberDiff line change
@@ -1345,7 +1345,7 @@ def test_request_membership_accept_flow(
13451345
request = requests_service.execute_action(
13461346
owner.identity, membership_request.id, "accept"
13471347
).to_dict()
1348-
ArchivedInvitation.index.refresh() # switch name?
1348+
ArchivedInvitation.index.refresh()
13491349
Member.index.refresh()
13501350

13511351
# Postconditions
@@ -1362,6 +1362,48 @@ def test_request_membership_accept_flow(
13621362
assert hit["request"]["is_open"] is False
13631363

13641364

1365+
def test_request_membership_expire_flow(
1366+
member_service,
1367+
community,
1368+
owner,
1369+
create_user,
1370+
requests_service,
1371+
db,
1372+
clean_index,
1373+
):
1374+
# Create membership request
1375+
user = create_user()
1376+
data = {
1377+
"message": "Can I join the club?",
1378+
}
1379+
community_uuid = community._record.id
1380+
membership_request = member_service.request_membership(
1381+
user.identity,
1382+
community_uuid,
1383+
data,
1384+
)
1385+
1386+
# Expire request
1387+
request = requests_service.execute_action(
1388+
system_identity, membership_request.id, "expire"
1389+
).to_dict()
1390+
ArchivedInvitation.index.refresh()
1391+
Member.index.refresh()
1392+
1393+
# Postconditions
1394+
# 1 member, the owner
1395+
res = member_service.search(owner.identity, community_uuid)
1396+
assert 1 == res.to_dict()["hits"]["total"]
1397+
1398+
# 1 "request", the expired membership request
1399+
res = member_service.search_membership_requests(owner.identity, community_uuid)
1400+
hits = res.to_dict()["hits"]
1401+
assert 1 == hits["total"]
1402+
hit = hits["hits"][0]
1403+
assert "expired" == hit["request"]["status"]
1404+
assert hit["request"]["is_open"] is False
1405+
1406+
13651407
#
13661408
# Change notifications
13671409
#

0 commit comments

Comments
 (0)