Commit 284cf6b7 authored by Sébastiaan Versteeg's avatar Sébastiaan Versteeg

Add Groups Settings API support

parent a0c9098e
......@@ -32,6 +32,14 @@ mailinglists.apps module
:undoc-members:
:show-inheritance:
mailinglists.gsuite module
--------------------------
.. automodule:: mailinglists.gsuite
:members:
:undoc-members:
:show-inheritance:
mailinglists.models module
--------------------------
......@@ -48,3 +56,11 @@ mailinglists.services module
:undoc-members:
:show-inheritance:
mailinglists.signals module
---------------------------
.. automodule:: mailinglists.signals
:members:
:undoc-members:
:show-inheritance:
......@@ -43,6 +43,14 @@ utils.exception\_filter module
:undoc-members:
:show-inheritance:
utils.google\_api module
------------------------
.. automodule:: utils.google_api
:members:
:undoc-members:
:show-inheritance:
utils.snippets module
---------------------
......
"""GSuite syncing helpers defined by the mailinglists package"""
import threading
from time import sleep
from django.conf import settings
from django.utils.datastructures import ImmutableList
from googleapiclient.errors import HttpError
from mailinglists.models import MailingList
from mailinglists.services import get_automatic_lists
from utils.google_api import get_directory_api
from django.conf import settings
from utils.google_api import get_directory_api, get_groups_settings_api
class GroupData:
def __init__(self, name, description, moderated=False, archived=False,
def __init__(self, name, description='', moderated=False, archived=False,
prefix='', aliases=ImmutableList([]),
addresses=ImmutableList([])):
super().__init__()
......@@ -31,6 +31,7 @@ def create_group(group):
Create a new group based on the provided data
:param group: group data
"""
groups_settings_api = get_groups_settings_api()
directory_api = get_directory_api()
directory_api.groups().insert(
body={
......@@ -43,7 +44,30 @@ def create_group(group):
# Docs say we need to wait a minute, but since we always update lists
# an error in the list members update is not a problem
sleep(0.5)
update_group_members(group)
groups_settings_api.groups().update(
groupUniqueId=f'{group.name}@{settings.GSUITE_DOMAIN}',
body={
"allowExternalMembers": "true",
"allowWebPosting": "false",
"isArchived": str(group.archived).lower(),
"membersCanPostAsTheGroup": "false",
"messageModerationLevel": "MODERATE_ALL_MESSAGES"
if group.moderated else "MODERATE_NONE",
"replyTo": "REPLY_TO_LIST",
"whoCanAssistContent": "NONE",
"whoCanContactOwner": "ALL_MANAGERS_CAN_CONTACT",
"whoCanDiscoverGroup": "ALL_MEMBERS_CAN_DISCOVER",
"whoCanJoin": "INVITED_CAN_JOIN",
"whoCanLeaveGroup": "NONE_CAN_LEAVE",
"whoCanModerateContent": "OWNERS_AND_MANAGERS",
"whoCanModerateMembers": "NONE",
"whoCanPostMessage": "ANYONE_CAN_POST",
"whoCanViewGroup": "ALL_MANAGERS_CAN_VIEW",
"whoCanViewMembership": "ALL_MANAGERS_CAN_VIEW"
}
).execute()
_update_group_members(group)
_update_group_aliases(group)
def update_group(old_name, group):
......@@ -52,6 +76,7 @@ def update_group(old_name, group):
:param old_name: old group name
:param group: new group data
"""
groups_settings_api = get_groups_settings_api()
directory_api = get_directory_api()
directory_api.groups().update(
groupKey=f'{old_name}@{settings.GSUITE_DOMAIN}',
......@@ -61,11 +86,19 @@ def update_group(old_name, group):
'description': group.description,
}
)
update_group_aliases(group)
update_group_members(group)
groups_settings_api.groups().patch(
groupUniqueId=f'{group.name}@{settings.GSUITE_DOMAIN}',
body={
"isArchived": str(group.archived).lower(),
"messageModerationLevel": "MODERATE_ALL_MESSAGES"
if group.moderated else "MODERATE_NONE",
}
).execute()
_update_group_aliases(group)
_update_group_members(group)
def update_group_aliases(group: GroupData):
def _update_group_aliases(group: GroupData):
"""
Update the aliases of a group based on existing values
:param group: group data
......@@ -103,18 +136,18 @@ def update_group_aliases(group: GroupData):
pass # Ignore error, API returned failing value
def delete_group(group: GroupData):
def delete_group(name: str):
"""
Removes the specified list from
:param group: group data
Removes the specified list
:param name: Group name
"""
directory_api = get_directory_api()
directory_api.groups().delete(
groupKey=f'{group.name}@{settings.GSUITE_DOMAIN}',
groupKey=f'{name}@{settings.GSUITE_DOMAIN}',
).execute()
def update_group_members(group: GroupData):
def _update_group_members(group: GroupData):
"""
Update the group members of the specified group based
on the existing members
......@@ -133,13 +166,17 @@ def update_group_members(group: GroupData):
).execute()
members_list += members_response.get('members', [])
existing_members = [m['email'] for m in members_list]
existing_members = [m['email'] for m in members_list
if m['role'] == 'MEMBER']
existing_managers = [m['email'] for m in members_list
if m['role'] == 'MANAGER']
except HttpError:
return # the list does not exist or something else is wrong
new_members = list(group.addresses)
remove_list = [x for x in existing_members if x not in new_members]
insert_list = [x for x in new_members if x not in existing_members]
insert_list = [x for x in new_members if x not in existing_members
and x not in existing_managers]
for remove_member in remove_list:
try:
......@@ -224,10 +261,25 @@ def sync_mailinglists(lists=None):
remove_list = [x for x in existing_groups if x not in new_groups]
insert_list = [x for x in new_groups if x not in existing_groups]
threads = []
for l in lists:
if l.name in remove_list:
delete_group(l)
elif l.name in insert_list:
create_group(l)
if l.name in insert_list:
thread = threading.Thread(target=create_group,
args=(l,))
threads.append(thread)
thread.start()
else:
update_group(l.name, l)
thread = threading.Thread(target=update_group,
args=(l.name, l))
threads.append(thread)
thread.start()
for l in remove_list:
thread = threading.Thread(target=delete_group,
args=(l,))
threads.append(thread)
thread.start()
for th in threads:
th.join()
......@@ -40,42 +40,41 @@ def get_automatic_lists():
if 0 < timezone.now().month < 9:
lectureyear += 1
active_mentorships = Mentorship.objects.filter(
year=lectureyear)
year=lectureyear).prefetch_related('member')
mentors = [x.member for x in active_mentorships]
lists = []
lists += _create_automatic_list(
['members', 'leden'], '[THALIA]',
Member.all_with_membership('member'), True, True, True)
Member.all_with_membership('member'), '', True, True, True)
lists += _create_automatic_list(
['benefactors', 'begunstigers'],
'[THALIA]',
Member.all_with_membership(Membership.BENEFACTOR),
Member.all_with_membership(Membership.BENEFACTOR), '',
multilingual=True)
lists += _create_automatic_list(
['honorary', 'ereleden'], '[THALIA]', Member.all_with_membership(
'honorary'), multilingual=True)
'honorary'), '', multilingual=True)
lists += _create_automatic_list(
['mentors'], '[THALIA] [MENTORS]', mentors, moderated=False)
['mentors'], '[THALIA] [MENTORS]', mentors, '', moderated=False)
lists += _create_automatic_list(
['activemembers'], '[THALIA] [COMMITTEES]',
active_members)
active_members, '')
lists += _create_automatic_list(
['committeechairs', 'commissievoorzitters'], '[THALIA] [CHAIRS]',
committee_chair_emails, moderated=False)
committee_chair_emails, '', moderated=False)
lists += _create_automatic_list(
['societychairs', 'gezelschapvoorzitters'], '[THALIA] [SOCIETY]',
society_chair_emails, moderated=False)
society_chair_emails, '', moderated=False)
lists += _create_automatic_list(
['optin'], '[THALIA] [OPTIN]', Member.current_members.filter(
profile__receive_optin=True),
multilingual=True)
profile__receive_optin=True), '', multilingual=True)
all_previous_board_members = []
for board in Board.objects.filter(
since__year__lte=lectureyear).order_by('since__year'):
since__year__lte=lectureyear).order_by('since__year'):
board_members = [board.member for board in
MemberGroupMembership.objects.filter
(group=board).prefetch_related('member')]
......
from django.db.models.signals import post_save, post_delete
from django.db.models.signals import pre_save, \
pre_delete
from django.dispatch import receiver
from googleapiclient.errors import HttpError
from mailinglists.gsuite import mailinglist_to_group, sync_mailinglists
from mailinglists.gsuite import (mailinglist_to_group, sync_mailinglists,
update_group, create_group)
from mailinglists.models import MailingList
@receiver(post_save, sender='mailinglists.MailingList')
def post_mailinglist_save(instance, **kwargs):
sync_mailinglists([mailinglist_to_group(instance)])
@receiver(pre_save, sender='mailinglists.MailingList')
def post_mailinglist_save(instance, created, **kwargs):
group = mailinglist_to_group(instance)
old_list = MailingList.objects.get(pk=instance.pk)
try:
if created:
create_group(group)
else:
update_group(old_list.name, group)
except HttpError:
# Cannot do direct create or update, do full sync for list
sync_mailinglists([group])
@receiver(post_delete, sender='mailinglists.MailingList')
@receiver(pre_delete, sender='mailinglists.MailingList')
def post_mailinglist_delete(instance, **kwargs):
sync_mailinglists([mailinglist_to_group(instance)])
......@@ -43,15 +43,6 @@ class MailingListTest(TestCase):
mailinglist.name = "activemembers1"
mailinglist.clean()
def test_autoresponse_has_text(self):
self.mailinglist.autoresponse_enabled = True
with self.assertRaises(ValidationError):
self.mailinglist.clean()
self.mailinglist.autoresponse_text = "Hello World"
self.mailinglist.clean()
class ListAliasTest(TestCase):
"""Tests list aliases"""
......
......@@ -43,8 +43,9 @@ if FIREBASE_CREDENTIALS != {}:
logger.error('Firebase application failed to initialise')
GSUITE_ADMIN_CREDENTIALS = (
service_account.Credentials.from_service_account_info(
GSUITE_ADMIN_CREDENTIALS, scopes=GSUITE_ADMIN_SCOPES
).with_subject(GSUITE_ADMIN_USER)
)
if GSUITE_ADMIN_CREDENTIALS != {}:
GSUITE_ADMIN_CREDENTIALS = (
service_account.Credentials.from_service_account_info(
GSUITE_ADMIN_CREDENTIALS, scopes=GSUITE_ADMIN_SCOPES
).with_subject(GSUITE_ADMIN_USER)
)
......@@ -25,11 +25,9 @@ def get_directory_api():
)
def get_group_settings_api():
def get_groups_settings_api():
return build(
'groups', 'v1',
'groupssettings', 'v1',
credentials=settings.GSUITE_ADMIN_CREDENTIALS,
cache=memory_cache
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment