Skip to content

Commit

Permalink
add remote site update logic (#817)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikkonie committed Jun 14, 2024
1 parent e53e545 commit 1872804
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 11 deletions.
1 change: 0 additions & 1 deletion projectroles/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,6 @@ def clean(self):
self.cleaned_data[key] = value
for field, error in errors:
self.add_error(field, error)
# TODO: Validate remote site fields here
return self.cleaned_data


Expand Down
113 changes: 104 additions & 9 deletions projectroles/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,6 @@ def test_get_not_found(self):
self.assertEqual(response.status_code, 404)


# TODO: Add remote site field in GET tests
class TestProjectCreateView(
ProjectMixin, RoleAssignmentMixin, RemoteSiteMixin, ViewTestBase
):
Expand All @@ -571,6 +570,7 @@ def _get_post_data(cls, title, project_type, parent, owner):
'owner': owner.sodar_uuid,
'description': 'description',
'public_guest_access': False,
REMOTE_SITE_FIELD: False,
}
# Add settings values
ret.update(
Expand Down Expand Up @@ -725,11 +725,10 @@ def test_get_parent_owner(self):
form = response.context['form']
self.assertEqual(form.initial['owner'], user_new)

# TODO: Test remote site in POST requests once implemented

def test_post_top_level_category(self):
"""Test POST for top level category"""
self.assertEqual(Project.objects.all().count(), 1)
self.assertEqual(Project.objects.count(), 1)
self.assertEqual(RemoteProject.objects.count(), 0)
data = self._get_post_data(
title='NewTestCategory',
project_type=PROJECT_TYPE_CATEGORY,
Expand All @@ -740,7 +739,7 @@ def test_post_top_level_category(self):
response = self.client.post(reverse('projectroles:create'), data)

self.assertEqual(response.status_code, 302)
self.assertEqual(Project.objects.all().count(), 2)
self.assertEqual(Project.objects.count(), 2)
category = Project.objects.filter(title='NewTestCategory').first()
self.assertIsNotNone(category)

Expand All @@ -760,6 +759,8 @@ def test_post_top_level_category(self):
model_dict.pop('readme', None)
self.assertEqual(model_dict, expected)

# Assert remote projects
self.assertEqual(RemoteProject.objects.count(), 0)
# Assert settings
settings = AppSetting.objects.filter(project=category)
self.assertEqual(settings.count(), 1)
Expand Down Expand Up @@ -804,7 +805,7 @@ def test_post_project(self):
response = self.client.post(self.url, data)

self.assertEqual(response.status_code, 302)
self.assertEqual(Project.objects.all().count(), 2)
self.assertEqual(Project.objects.count(), 2)
project = Project.objects.get(type=PROJECT_TYPE_PROJECT)

expected = {
Expand All @@ -823,6 +824,7 @@ def test_post_project(self):
model_dict.pop('readme', None)
self.assertEqual(model_dict, expected)

self.assertEqual(RemoteProject.objects.count(), 0)
project_settings = [
'project_bool_setting',
'project_callable_setting',
Expand Down Expand Up @@ -972,6 +974,29 @@ def test_post_project_title_delimiter(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(Project.objects.all().count(), 1)

def test_post_remote(self):
"""Test POST with added remote project"""
self.assertEqual(RemoteProject.objects.count(), 0)
data = self._get_post_data(
title='TestProject',
project_type=PROJECT_TYPE_PROJECT,
parent=self.category,
owner=self.user,
)
data[REMOTE_SITE_FIELD] = True
with self.login(self.user):
response = self.client.post(self.url, data)

self.assertEqual(response.status_code, 302)
self.assertEqual(Project.objects.count(), 2)
project = Project.objects.get(type=PROJECT_TYPE_PROJECT)
self.assertEqual(RemoteProject.objects.count(), 1)
rp = RemoteProject.objects.first()
self.assertEqual(rp.project_uuid, project.sodar_uuid)
self.assertEqual(rp.project, project)
self.assertEqual(rp.site, self.remote_site)
self.assertEqual(rp.level, REMOTE_LEVEL_READ_ROLES)


class TestProjectUpdateView(
ProjectMixin, RoleAssignmentMixin, RemoteTargetMixin, ViewTestBase
Expand Down Expand Up @@ -1221,20 +1246,20 @@ def test_get_not_found(self):
)
self.assertEqual(response.status_code, 404)

# TODO: Test remote site in POST requests once implemented

def test_post_project_superuser(self):
"""Test POST for project as superuser"""
timeline = get_backend_api('timeline_backend')
category_new = self.make_project('NewCat', PROJECT_TYPE_CATEGORY, None)
self.make_assignment(category_new, self.user, self.role_owner)
self.assertEqual(Project.objects.all().count(), 3)
self.assertEqual(RemoteProject.objects.count(), 0)

data = model_to_dict(self.project)
data['title'] = 'updated title'
data['description'] = 'updated description'
data['parent'] = category_new.sodar_uuid # NOTE: Updated parent
data['owner'] = self.user.sodar_uuid # NOTE: Must add owner
data[REMOTE_SITE_FIELD] = False
# Add settings values
ps = self._get_post_app_settings(self.project, self.user)
data.update(ps)
Expand All @@ -1259,6 +1284,8 @@ def test_post_project_superuser(self):
model_dict.pop('readme', None)
self.assertEqual(model_dict, expected)

# Assert remote projects
self.assertEqual(RemoteProject.objects.count(), 0)
# Assert settings
self._assert_app_settings(ps)
# Assert hidden settings
Expand Down Expand Up @@ -1292,6 +1319,7 @@ def test_post_project_superuser(self):
self.assertIn('title', tl_event.extra_data)
self.assertIn('description', tl_event.extra_data)
self.assertIn('parent', tl_event.extra_data)
# TODO: Assert remote sites is NOT in once implemented
# No alert or mail, because the owner has not changed
self.assertEqual(self.app_alert_model.objects.count(), 0)
self.assertEqual(len(mail.outbox), 0)
Expand Down Expand Up @@ -1319,12 +1347,14 @@ def test_post_project_regular_user(self):
category_new = self.make_project('NewCat', PROJECT_TYPE_CATEGORY, None)
self.make_assignment(category_new, user_new, self.role_owner)
self.assertEqual(Project.objects.all().count(), 3)
self.assertEqual(RemoteProject.objects.count(), 0)

data = model_to_dict(self.project)
data['title'] = 'updated title'
data['description'] = 'updated description'
data['parent'] = category_new.sodar_uuid
data['owner'] = user_new.sodar_uuid
data[REMOTE_SITE_FIELD] = False
ps = self._get_post_app_settings(self.project, user_new)
data.update(ps)
with self.login(user_new):
Expand All @@ -1348,6 +1378,7 @@ def test_post_project_regular_user(self):
model_dict.pop('readme', None)
self.assertEqual(model_dict, expected)

self.assertEqual(RemoteProject.objects.count(), 0)
self._assert_app_settings(ps)
# Hidden settings should remain as they were not changed
hidden_val = app_settings.get(
Expand Down Expand Up @@ -1572,8 +1603,72 @@ def test_post_project_parent_different_owner_disable_email(self):
self.assertEqual(self.app_alert_model.objects.first().user, user_new)
self.assertEqual(len(mail.outbox), 0)

@override_settings(PROJECTROLES_SITE_MODE=SITE_MODE_TARGET)
def test_post_remote(self):
"""Test POST with enabled remote project"""
self.assertEqual(RemoteProject.objects.count(), 0)
data = model_to_dict(self.project)
data['parent'] = self.category.sodar_uuid
data['owner'] = self.user.sodar_uuid
data[REMOTE_SITE_FIELD] = True
ps = self._get_post_app_settings(self.project, self.user)
data.update(ps)
with self.login(self.user):
response = self.client.post(self.url, data)
self.assertEqual(response.status_code, 302)
self.assertEqual(RemoteProject.objects.count(), 1)
rp = RemoteProject.objects.first()
self.assertEqual(rp.project_uuid, self.project.sodar_uuid)
self.assertEqual(rp.project, self.project)
self.assertEqual(rp.site, self.remote_site)
self.assertEqual(rp.level, REMOTE_LEVEL_READ_ROLES)
# TODO: Assert timeline event title and extra data once implemented

def test_post_remote_revoke(self):
"""Test POST to revoke existing remote project"""
rp = self.make_remote_project(
project_uuid=self.project.sodar_uuid,
site=self.remote_site,
level=REMOTE_LEVEL_READ_ROLES,
project=self.project,
)
self.assertEqual(RemoteProject.objects.count(), 1)
data = model_to_dict(self.project)
data['parent'] = self.category.sodar_uuid
data['owner'] = self.user.sodar_uuid
data[REMOTE_SITE_FIELD] = False
ps = self._get_post_app_settings(self.project, self.user)
data.update(ps)
with self.login(self.user):
response = self.client.post(self.url, data)
self.assertEqual(response.status_code, 302)
self.assertEqual(RemoteProject.objects.count(), 1)
rp.refresh_from_db()
self.assertEqual(rp.level, REMOTE_LEVEL_REVOKED)

def test_post_remote_enable_revoked(self):
"""Test POST to re-enable revoked remote project"""
rp = self.make_remote_project(
project_uuid=self.project.sodar_uuid,
site=self.remote_site,
level=REMOTE_LEVEL_REVOKED,
project=self.project,
)
self.assertEqual(RemoteProject.objects.count(), 1)
data = model_to_dict(self.project)
data['parent'] = self.category.sodar_uuid
data['owner'] = self.user.sodar_uuid
data[REMOTE_SITE_FIELD] = True
ps = self._get_post_app_settings(self.project, self.user)
data.update(ps)
with self.login(self.user):
response = self.client.post(self.url, data)
self.assertEqual(response.status_code, 302)
self.assertEqual(RemoteProject.objects.count(), 1)
rp.refresh_from_db()
self.assertEqual(rp.level, REMOTE_LEVEL_READ_ROLES)

@override_settings(PROJECTROLES_SITE_MODE=SITE_MODE_TARGET)
def test_post_target_remote(self):
"""Test POST with remote project as target"""
self.set_up_as_target(projects=[self.category, self.project])
data = model_to_dict(self.project)
Expand Down
52 changes: 51 additions & 1 deletion projectroles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
REMOTE_LEVEL_VIEW_AVAIL = SODAR_CONSTANTS['REMOTE_LEVEL_VIEW_AVAIL']
REMOTE_LEVEL_READ_INFO = SODAR_CONSTANTS['REMOTE_LEVEL_READ_INFO']
REMOTE_LEVEL_READ_ROLES = SODAR_CONSTANTS['REMOTE_LEVEL_READ_ROLES']
REMOTE_LEVEL_REVOKED = SODAR_CONSTANTS['REMOTE_LEVEL_REVOKED']
APP_SETTING_SCOPE_PROJECT = SODAR_CONSTANTS['APP_SETTING_SCOPE_PROJECT']
APP_SETTING_SCOPE_PROJECT_USER = SODAR_CONSTANTS[
'APP_SETTING_SCOPE_PROJECT_USER'
Expand Down Expand Up @@ -1013,6 +1014,8 @@ def _get_project_update_data(old_data, project, project_settings):
extra_data['public_guest_access'] = project.public_guest_access
upd_fields.append('public_guest_access')

# TODO: Handle remote sites here

# Settings
for k, v in project_settings.items():
p_name = k.split('.')[1]
Expand Down Expand Up @@ -1062,6 +1065,7 @@ def _create_timeline_event(

else: # Update
tl_desc = 'update ' + type_str.lower()
# TODO: Provide remote sites here
extra_data, upd_fields = cls._get_project_update_data(
old_data, project, project_settings
)
Expand Down Expand Up @@ -1104,6 +1108,42 @@ def _update_settings(cls, project, project_settings):
validate=True,
)

def _update_remote_sites(self, project, data, site_fields):
"""Update remote sites"""
for f in site_fields:
site_uuid = f.split('.')[1]
site = RemoteSite.objects.filter(sodar_uuid=site_uuid).first()
# TODO: Validate site
value = data[f]
rp = RemoteProject.objects.filter(
site=site, project=project
).first()
modify = None
if rp and (
(value and rp.level != REMOTE_LEVEL_READ_ROLES)
or (not value and rp.level == REMOTE_LEVEL_READ_ROLES)
):
rp.level = (
REMOTE_LEVEL_READ_ROLES if value else REMOTE_LEVEL_REVOKED
)
rp.save()
modify = 'Updated'
elif value: # Only create non-existing project if value is True
rp = RemoteProject.objects.create(
project_uuid=project.sodar_uuid,
project=project,
site=site,
level=REMOTE_LEVEL_READ_ROLES,
)
modify = 'Created'
if modify:
logger.debug(
'{} RemoteProject for site "{}" ({}): {}'.format(
modify, site.name, site.sodar_uuid, rp.level
)
)
# TODO: Return (only!) updated data

@classmethod
def _notify_users(cls, project, action, owner, old_parent, request):
"""
Expand Down Expand Up @@ -1242,14 +1282,24 @@ def modify_project(self, data, request, instance=None):
if not owner and old_project: # In case of a PATCH request
owner = old_project.get_owner().user

# TODO: Create/update RemoteProject objects here
# Create/update RemoteProject objects
if (
settings.PROJECTROLES_SITE_MODE == SITE_MODE_SOURCE
and project.type == PROJECT_TYPE_PROJECT
):
site_fields = [f for f in data if f.startswith('remote_site.')]
# TODO: Get old RemoteProject data (store in old_data)
# TODO: Return data to be compared with old data here
self._update_remote_sites(project, data, site_fields)

# Get app settings, store old settings
project_settings = self._get_app_settings(data, project, request.user)
old_settings = None
if action == PROJECT_ACTION_UPDATE:
old_settings = json.loads(json.dumps(project_settings)) # Copy

# Create timeline event
# TODO: Add remote project data here
tl_event = self._create_timeline_event(
project, action, owner, old_data, project_settings, request
)
Expand Down

0 comments on commit 1872804

Please sign in to comment.