From 5f97e93434339a15b15a6ffbb8710aa197398f55 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 14 Sep 2023 15:18:15 +0200
Subject: [PATCH 001/155] Revoke project access for invited email
---
dds_web/api/user.py | 61 +++++++++++++++++++++++++++------------------
1 file changed, 37 insertions(+), 24 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index aaedce3b9..97127b3a4 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -874,31 +874,41 @@ def post(self):
# Check if email is registered to a user
try:
existing_user = user_schemas.UserSchema().load({"email": user_email})
+ unanswered_invite = user_schemas.UnansweredInvite().load({"email": user_email})
except sqlalchemy.exc.OperationalError as err:
raise ddserr.DatabaseError(message=str(err), alt_message="Unexpected database error.")
if not existing_user:
- raise ddserr.NoSuchUserError(
- f"The user with email '{user_email}' does not have access to the specified project."
- " Cannot remove non-existent project access."
- )
+ if not unanswered_invite:
+ # The user doesn't exist and doesnt have a pending invite either
+ raise ddserr.NoSuchUserError(
+ f"The user with email '{user_email}' does not have access to the specified project."
+ " Cannot remove non-existent project access."
+ )
+ else:
+ invite_id = unanswered_invite.id
+ project_invite_key = models.ProjectInviteKeys.query.filter_by(invite_id=invite_id,project_id=project.id).one_or_none()
+ if project_invite_key:
+ db.session.delete(unanswered_invite)
+ db.session.delete(project_invite_key)
- user_in_project = False
- for user_association in project.researchusers:
- if user_association.user_id == existing_user.username:
- user_in_project = True
- db.session.delete(user_association)
- project_user_key = models.ProjectUserKeys.query.filter_by(
- project_id=project.id, user_id=existing_user.username
- ).first()
- if project_user_key:
- db.session.delete(project_user_key)
-
- if not user_in_project:
- raise ddserr.NoSuchUserError(
- f"The user with email '{user_email}' does not have access to the specified project."
- " Cannot remove non-existent project access."
- )
+ else:
+ user_in_project = False
+ for user_association in project.researchusers:
+ if user_association.user_id == existing_user.username:
+ user_in_project = True
+ db.session.delete(user_association)
+ project_user_key = models.ProjectUserKeys.query.filter_by(
+ project_id=project.id, user_id=existing_user.username
+ ).first()
+ if project_user_key:
+ db.session.delete(project_user_key)
+
+ if not user_in_project:
+ raise ddserr.NoSuchUserError(
+ f"The user with email '{user_email}' does not have access to the specified project."
+ " Cannot remove non-existent project access."
+ )
try:
db.session.commit()
@@ -919,12 +929,15 @@ def post(self):
),
) from err
- flask.current_app.logger.debug(
- f"User {existing_user.username} no longer associated with project {project.public_id}."
- )
+ if unanswered_invite:
+ msg = f"Invited user is no longer associated with project {project.public_id}."
+ else:
+ msg = f"User {existing_user.username} no longer associated with project {project.public_id}."
+
+ flask.current_app.logger.debug(msg)
return {
- "message": f"User with email {user_email} no longer associated with {project.public_id}."
+ "message": msg
}
From aef7c3d5ca1023618b3900b9901772b4843c0940 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 14 Sep 2023 15:18:31 +0200
Subject: [PATCH 002/155] Revoke project access for invited email
---
dds_web/api/user.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 97127b3a4..41f83aad0 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -887,7 +887,9 @@ def post(self):
)
else:
invite_id = unanswered_invite.id
- project_invite_key = models.ProjectInviteKeys.query.filter_by(invite_id=invite_id,project_id=project.id).one_or_none()
+ project_invite_key = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invite_id, project_id=project.id
+ ).one_or_none()
if project_invite_key:
db.session.delete(unanswered_invite)
db.session.delete(project_invite_key)
@@ -933,12 +935,10 @@ def post(self):
msg = f"Invited user is no longer associated with project {project.public_id}."
else:
msg = f"User {existing_user.username} no longer associated with project {project.public_id}."
-
+
flask.current_app.logger.debug(msg)
- return {
- "message": msg
- }
+ return {"message": msg}
class EncryptedToken(flask_restful.Resource):
From 9039db0c93831864dbde6d6aed9bb363fbe36ea3 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 15 Sep 2023 11:03:21 +0200
Subject: [PATCH 003/155] started testing
---
tests/test_project_access.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index 6028825a0..0564c48a9 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -405,3 +405,9 @@ def test_fix_access_unitadmin_valid_email_unituser(client):
project_id=project.id, user_id="unituser"
).first()
assert user_project_key_row
+
+def revoking_access_to_existing_user(client):
+ pass
+
+def revoking_access_to_unacepted_invite(client):
+ pass
\ No newline at end of file
From 05ed50b3f2f7154f6a4d230aac8f4eeb89db0aaa Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 18 Sep 2023 13:45:51 +0200
Subject: [PATCH 004/155] tests
---
tests/test_project_access.py | 42 +++++++++++++++++++++++++++++++++---
1 file changed, 39 insertions(+), 3 deletions(-)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index 0564c48a9..de9cb8ed9 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -14,6 +14,8 @@
# proj_data = {"pi": "piName", "title": "Test proj", "description": "A longer project description"}
proj_query = {"project": "public_project_id"}
# proj_query_restricted = {"project": "restricted_project_id"}
+first_new_email = {"email": "first_test_email@mailtrap.io"}
+first_new_user = {**first_new_email, "role": "Researcher"}
# TESTS #################################################################################### TESTS #
@@ -406,8 +408,42 @@ def test_fix_access_unitadmin_valid_email_unituser(client):
).first()
assert user_project_key_row
-def revoking_access_to_existing_user(client):
- pass
def revoking_access_to_unacepted_invite(client):
- pass
\ No newline at end of file
+ project = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
+
+ # invite a new user to an existing project so they receive a new invite
+ response = client.post(
+ tests.DDSEndpoint.USER_ADD,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project.public_id},
+ json=first_new_user,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ invited_user = models.Invite.query.filter_by(email=first_new_email["email"]).one_or_none()
+ assert invited_user
+
+ # check row was added to project invite keys table
+ project_invite_keys = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invited_user.id, project_id=project.id
+ ).one_or_none()
+ assert project_invite_keys
+
+ # Now, revoke access to said user. The invite should be deleted
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
+ query_string={"project": project.public_id},
+ json=first_new_user,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ # Check that the invite is deleted
+ invited_user = models.Invite.query.filter_by(email=first_new_email["email"]).one_or_none()
+ assert not invited_user
+
+ project_invite_keys = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invited_user.id, project_id=project.id
+ ).one_or_none()
+ assert not project_invite_keys
From a0d4aaae756b222a122c242df10c41e55b1f6d45 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 18 Sep 2023 13:57:15 +0200
Subject: [PATCH 005/155] sprintlog and prettier
---
SPRINTLOG.md | 1 +
tests/test_project_access.py | 1 +
2 files changed, 2 insertions(+)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index 77700d4e5..896ad7267 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -300,3 +300,4 @@ _Nothing merged in CLI during this sprint_
- Dependency: Bump `MariaDB` to LTS version 10.11.5 ([#1465](https://github.com/ScilifelabDataCentre/dds_web/pull/1465))
- Bug fixed: Row in `ProjectUsers` should also be added if it doesn't exist when giving Researcher access to a specific project ([#1464](https://github.com/ScilifelabDataCentre/dds_web/pull/1464))
- Workflow: Update PR template and clarify sections ([#1467](https://github.com/ScilifelabDataCentre/dds_web/pull/1467))
+- Revoking access to unacepted invites removes the invites from the DB ([#1192])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13/backlog?epics=visible&selectedIssue=DDS-1192)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index ae0eedb13..fac3c2a0b 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -469,6 +469,7 @@ def revoking_access_to_unacepted_invite(client):
).one_or_none()
assert not project_invite_keys
+
def test_fix_access_unitadmin_valid_email_unituser_no_project(client):
"""Unit Admin giving access to unituser - ok. No project."""
# Remove ProjectUserKeys row for specific project and user
From eb0edf94b654b0fd7bc01421bdb4898eae9a6747 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 18 Sep 2023 14:31:15 +0200
Subject: [PATCH 006/155] testing msg fixed
---
dds_web/api/user.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 41f83aad0..968b17d09 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -932,9 +932,9 @@ def post(self):
) from err
if unanswered_invite:
- msg = f"Invited user is no longer associated with project {project.public_id}."
+ msg = f"Invited user is no longer associated with {project.public_id}."
else:
- msg = f"User {existing_user.username} no longer associated with project {project.public_id}."
+ msg = f"User with email {user_email} no longer associated with {project.public_id}."
flask.current_app.logger.debug(msg)
From 1a69114958384f07dcdfd9c30463b0175fab8b0e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 18 Sep 2023 14:55:30 +0200
Subject: [PATCH 007/155] Update test_project_access.py
---
tests/test_project_access.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index fac3c2a0b..095a5fa78 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -442,7 +442,7 @@ def revoking_access_to_unacepted_invite(client):
)
assert response.status_code == http.HTTPStatus.OK
- invited_user = models.Invite.query.filter_by(email=first_new_email["email"]).one_or_none()
+ invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
assert invited_user
# check row was added to project invite keys table
@@ -461,7 +461,7 @@ def revoking_access_to_unacepted_invite(client):
assert response.status_code == http.HTTPStatus.OK
# Check that the invite is deleted
- invited_user = models.Invite.query.filter_by(email=first_new_email["email"]).one_or_none()
+ invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
assert not invited_user
project_invite_keys = models.ProjectInviteKeys.query.filter_by(
From 9e687680c3d2c045541bcde58d462d15f4c455df Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 18 Sep 2023 16:10:49 +0200
Subject: [PATCH 008/155] testing
---
tests/test_project_access.py | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index fac3c2a0b..37fe98fd4 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -430,7 +430,7 @@ def test_fix_access_unitadmin_valid_email_unituser(client):
assert user_project_key_row
-def revoking_access_to_unacepted_invite(client):
+def test_revoking_access_to_unacepted_invite(client):
project = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
# invite a new user to an existing project so they receive a new invite
@@ -443,11 +443,12 @@ def revoking_access_to_unacepted_invite(client):
assert response.status_code == http.HTTPStatus.OK
invited_user = models.Invite.query.filter_by(email=first_new_email["email"]).one_or_none()
+ invited_user_id = invited_user.id
assert invited_user
# check row was added to project invite keys table
project_invite_keys = models.ProjectInviteKeys.query.filter_by(
- invite_id=invited_user.id, project_id=project.id
+ invite_id=invited_user_id, project_id=project.id
).one_or_none()
assert project_invite_keys
@@ -460,12 +461,17 @@ def revoking_access_to_unacepted_invite(client):
)
assert response.status_code == http.HTTPStatus.OK
+ assert (
+ f"Invited user is no longer associated with {project.public_id}."
+ in response.json["message"]
+ )
+
# Check that the invite is deleted
invited_user = models.Invite.query.filter_by(email=first_new_email["email"]).one_or_none()
assert not invited_user
project_invite_keys = models.ProjectInviteKeys.query.filter_by(
- invite_id=invited_user.id, project_id=project.id
+ invite_id=invited_user_id, project_id=project.id
).one_or_none()
assert not project_invite_keys
From 53c56ddf0d032326bf149f95ccb26c46ce86fdf9 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 18 Sep 2023 16:49:26 +0200
Subject: [PATCH 009/155] improved tests
---
dds_web/api/user.py | 8 +++++
tests/test_project_access.py | 4 ---
tests/test_user_remove_association.py | 48 +++++++++++++++++++++++++++
3 files changed, 56 insertions(+), 4 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 968b17d09..cfa0a6720 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -890,9 +890,17 @@ def post(self):
project_invite_key = models.ProjectInviteKeys.query.filter_by(
invite_id=invite_id, project_id=project.id
).one_or_none()
+
+ # if the unasnwsered invite was asociated in the project, remove the invite and the asociation
if project_invite_key:
db.session.delete(unanswered_invite)
db.session.delete(project_invite_key)
+ else:
+ # the unanswred invite is not asociated with the project
+ raise ddserr.NoSuchUserError(
+ f"The user with email '{user_email}' does not have access to the specified project."
+ " Cannot remove non-existent project access."
+ )
else:
user_in_project = False
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index beb1c412e..6be2f7850 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -442,12 +442,8 @@ def test_revoking_access_to_unacepted_invite(client):
)
assert response.status_code == http.HTTPStatus.OK
-<<<<<<< HEAD
invited_user = models.Invite.query.filter_by(email=first_new_email["email"]).one_or_none()
invited_user_id = invited_user.id
-=======
- invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
->>>>>>> 1a69114958384f07dcdfd9c30463b0175fab8b0e
assert invited_user
# check row was added to project invite keys table
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index 39c0ecbf5..79b3ac56a 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -7,6 +7,16 @@
from tests.test_project_creation import proj_data_with_existing_users, create_unit_admins
from dds_web.database import models
+# CONFIG ################################################################################## CONFIG #
+
+# proj_data = {"pi": "piName", "title": "Test proj", "description": "A longer project description"}
+proj_query = {"project": "public_project_id"}
+# proj_query_restricted = {"project": "restricted_project_id"}
+first_new_email = {"email": "first_test_email@mailtrap.io"}
+first_new_user = {**first_new_email, "role": "Researcher"}
+
+# TESTS ################################################################################## TEST #
+
def test_remove_user_from_project(client, boto3_session):
"""Remove an associated user from a project"""
@@ -98,6 +108,44 @@ def test_remove_nonexistent_user_from_project(client, boto3_session):
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert "Cannot remove non-existent project access" in response.json["message"]
+def test_remove_nonacepted_user_from_other_project(client, boto3_session):
+ """Try to remove an User with an unacepted invite from another project"""
+
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins == 3
+
+ # create a new project
+ response = client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
+ json=proj_data_with_existing_users,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ project_id = response.json.get("project_id")
+
+ # invite a new user to an existing project
+ response = client.post(
+ tests.DDSEndpoint.USER_ADD,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
+ query_string={"project": "public_project_id"},
+ json=first_new_user,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ # try to remove the user from the first project
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
+ query_string={"project": project_id},
+ json=first_new_user,
+ )
+
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ assert "Cannot remove non-existent project access" in response.json["message"]
+
+
def test_remove_existing_user_from_nonexistent_proj(client, boto3_session):
"""Try to an existing user from a nonexistent project"""
From 52b962d1889d4698c4533483854600415438cc81 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 18 Sep 2023 16:50:10 +0200
Subject: [PATCH 010/155] black
---
dds_web/api/user.py | 4 ++--
tests/test_user_remove_association.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index cfa0a6720..4f7c244ed 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -891,7 +891,7 @@ def post(self):
invite_id=invite_id, project_id=project.id
).one_or_none()
- # if the unasnwsered invite was asociated in the project, remove the invite and the asociation
+ # if the unasnwsered invite was asociated in the project, remove the invite and the asociation
if project_invite_key:
db.session.delete(unanswered_invite)
db.session.delete(project_invite_key)
@@ -900,7 +900,7 @@ def post(self):
raise ddserr.NoSuchUserError(
f"The user with email '{user_email}' does not have access to the specified project."
" Cannot remove non-existent project access."
- )
+ )
else:
user_in_project = False
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index 79b3ac56a..d49ae262e 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -108,6 +108,7 @@ def test_remove_nonexistent_user_from_project(client, boto3_session):
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert "Cannot remove non-existent project access" in response.json["message"]
+
def test_remove_nonacepted_user_from_other_project(client, boto3_session):
"""Try to remove an User with an unacepted invite from another project"""
@@ -146,7 +147,6 @@ def test_remove_nonacepted_user_from_other_project(client, boto3_session):
assert "Cannot remove non-existent project access" in response.json["message"]
-
def test_remove_existing_user_from_nonexistent_proj(client, boto3_session):
"""Try to an existing user from a nonexistent project"""
From bff698e93bc2df1e4f8ce385024b4e5bd6ed8f84 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 19 Sep 2023 10:08:45 +0200
Subject: [PATCH 011/155] Added more comments
---
tests/test_project_access.py | 1 +
tests/test_user_remove_association.py | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index 6be2f7850..637fb8ad9 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -431,6 +431,7 @@ def test_fix_access_unitadmin_valid_email_unituser(client):
def test_revoking_access_to_unacepted_invite(client):
+ """ Revoking access to an unacepted invite for an existing project should delete the invite from the db """
project = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
# invite a new user to an existing project so they receive a new invite
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index d49ae262e..902de8922 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -110,7 +110,7 @@ def test_remove_nonexistent_user_from_project(client, boto3_session):
def test_remove_nonacepted_user_from_other_project(client, boto3_session):
- """Try to remove an User with an unacepted invite from another project"""
+ """ Try to remove an User with an unacepted invite from another project should result in an error"""
create_unit_admins(num_admins=2)
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
From 20fcf2eff72095abe334a569574d7083fd12f779 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 19 Sep 2023 10:09:20 +0200
Subject: [PATCH 012/155] black
---
tests/test_project_access.py | 2 +-
tests/test_user_remove_association.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index 637fb8ad9..4a6ce5362 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -431,7 +431,7 @@ def test_fix_access_unitadmin_valid_email_unituser(client):
def test_revoking_access_to_unacepted_invite(client):
- """ Revoking access to an unacepted invite for an existing project should delete the invite from the db """
+ """Revoking access to an unacepted invite for an existing project should delete the invite from the db"""
project = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
# invite a new user to an existing project so they receive a new invite
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index 902de8922..b097b3951 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -110,7 +110,7 @@ def test_remove_nonexistent_user_from_project(client, boto3_session):
def test_remove_nonacepted_user_from_other_project(client, boto3_session):
- """ Try to remove an User with an unacepted invite from another project should result in an error"""
+ """Try to remove an User with an unacepted invite from another project should result in an error"""
create_unit_admins(num_admins=2)
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
From a1f9a76304b6cc3d47a96ccea7ede7d68c4e1089 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 19 Sep 2023 14:42:38 +0200
Subject: [PATCH 013/155] checks that it wasn't the only invite
---
dds_web/api/user.py | 7 +++-
tests/test_project_access.py | 62 ++++++++++++++++++++++++++++++++++--
2 files changed, 66 insertions(+), 3 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 4f7c244ed..3130012e3 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -893,8 +893,13 @@ def post(self):
# if the unasnwsered invite was asociated in the project, remove the invite and the asociation
if project_invite_key:
- db.session.delete(unanswered_invite)
db.session.delete(project_invite_key)
+
+ # if such invite was only associated with a single project, delete the invite. Otherwise leave the invite
+ project_invite_key = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invite_id).one_or_none()
+ if not project_invite_key:
+ db.session.delete(unanswered_invite)
else:
# the unanswred invite is not asociated with the project
raise ddserr.NoSuchUserError(
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index 4a6ce5362..fe9ce5d97 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -430,6 +430,64 @@ def test_fix_access_unitadmin_valid_email_unituser(client):
assert user_project_key_row
+def test_remove_access_invite_associated_several_projects(client):
+ """If an invite is associated with several projects then a single revoke access should not delete the invite"""
+
+ project_1 = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
+ project_2 = models.Project.query.filter_by(public_id="second_public_project_id").one_or_none()
+
+ def add_to_project(project):
+ response = client.post(
+ tests.DDSEndpoint.USER_ADD,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project.public_id},
+ json=first_new_user,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
+ assert invited_user
+
+ project_invite_keys = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invited_user.id, project_id=project.id
+ ).one_or_none()
+ assert project_invite_keys
+
+ return invited_user
+
+ # invite a new user to both projects
+ invited_user = add_to_project(project_1)
+ _ = add_to_project(project_2)
+
+ # Now revoke access for the first project
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project_1.public_id},
+ json=first_new_user,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ assert (
+ f"Invited user is no longer associated with {project_1.public_id}."
+ in response.json["message"]
+ )
+
+ # The project invite row should only be deleted for project 1 and the invite should still exist
+ invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
+ assert invited_user
+
+ project_invite_keys = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invited_user.id, project_id=project_2.id
+ ).one_or_none()
+ assert project_invite_keys
+
+ project_invite_keys = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invited_user.id, project_id=project_1.id
+ ).one_or_none()
+ assert not project_invite_keys
+
+
def test_revoking_access_to_unacepted_invite(client):
"""Revoking access to an unacepted invite for an existing project should delete the invite from the db"""
project = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
@@ -443,7 +501,7 @@ def test_revoking_access_to_unacepted_invite(client):
)
assert response.status_code == http.HTTPStatus.OK
- invited_user = models.Invite.query.filter_by(email=first_new_email["email"]).one_or_none()
+ invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
invited_user_id = invited_user.id
assert invited_user
@@ -456,7 +514,7 @@ def test_revoking_access_to_unacepted_invite(client):
# Now, revoke access to said user. The invite should be deleted
response = client.post(
tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project.public_id},
json=first_new_user,
)
From ac7e825e4b2015b6b0d4d554efaf0481f9fc5633 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 19 Sep 2023 14:43:55 +0200
Subject: [PATCH 014/155] checks that it wasn't the only invite
---
dds_web/api/user.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 3130012e3..207eaaf76 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -894,10 +894,11 @@ def post(self):
# if the unasnwsered invite was asociated in the project, remove the invite and the asociation
if project_invite_key:
db.session.delete(project_invite_key)
-
+
# if such invite was only associated with a single project, delete the invite. Otherwise leave the invite
project_invite_key = models.ProjectInviteKeys.query.filter_by(
- invite_id=invite_id).one_or_none()
+ invite_id=invite_id
+ ).one_or_none()
if not project_invite_key:
db.session.delete(unanswered_invite)
else:
From 01f6309d035dd3d8ded81371f5699c1728f94afd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 20 Sep 2023 16:41:33 +0200
Subject: [PATCH 015/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 207eaaf76..4bd7603b5 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -882,7 +882,7 @@ def post(self):
if not unanswered_invite:
# The user doesn't exist and doesnt have a pending invite either
raise ddserr.NoSuchUserError(
- f"The user with email '{user_email}' does not have access to the specified project."
+ f"The user / invite with email '{user_email}' does not have access to the specified project."
" Cannot remove non-existent project access."
)
else:
From 8577bc71a247a5e1f86deeeb5baa51ebfe87cf2b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 20 Sep 2023 16:41:42 +0200
Subject: [PATCH 016/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 4bd7603b5..9bf4d848e 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -904,7 +904,7 @@ def post(self):
else:
# the unanswred invite is not asociated with the project
raise ddserr.NoSuchUserError(
- f"The user with email '{user_email}' does not have access to the specified project."
+ f"The user / invite with email '{user_email}' does not have access to the specified project."
" Cannot remove non-existent project access."
)
From 4e0aa632fc4817858efbc38b1624bc0d19c144c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 20 Sep 2023 16:41:50 +0200
Subject: [PATCH 017/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 9bf4d848e..2a494be06 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -922,7 +922,7 @@ def post(self):
if not user_in_project:
raise ddserr.NoSuchUserError(
- f"The user with email '{user_email}' does not have access to the specified project."
+ f"The user / invite with email '{user_email}' does not have access to the specified project."
" Cannot remove non-existent project access."
)
From 988597cbb30e71ab1e57f4ba6b1acad6988d4f4b Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 21 Sep 2023 10:51:10 +0200
Subject: [PATCH 018/155] refactoring
---
dds_web/api/user.py | 85 ++++++++++++++++++++++++---------------------
1 file changed, 46 insertions(+), 39 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 2a494be06..fee0a2c4f 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -871,59 +871,66 @@ def post(self):
if not (user_email := json_input.get("email")):
raise ddserr.DDSArgumentError(message="User email missing.")
- # Check if email is registered to a user
+ # Check if the user exists or has a pending invite
try:
existing_user = user_schemas.UserSchema().load({"email": user_email})
unanswered_invite = user_schemas.UnansweredInvite().load({"email": user_email})
except sqlalchemy.exc.OperationalError as err:
raise ddserr.DatabaseError(message=str(err), alt_message="Unexpected database error.")
- if not existing_user:
- if not unanswered_invite:
- # The user doesn't exist and doesnt have a pending invite either
- raise ddserr.NoSuchUserError(
- f"The user / invite with email '{user_email}' does not have access to the specified project."
- " Cannot remove non-existent project access."
- )
- else:
- invite_id = unanswered_invite.id
+ """
+ TODO before proceding with the deletion: We need to ensure that the hierarchy of roles is ensured
+ i.e That:
+
+ Researchers only and just only revoke access for Researchers
+ ProjectOwners revoke access for ProjectOwners (in the same project) and Researchers (in the same project)
+ Unit Personnel & Unit Admins: Researchers and Project Owners (any).
+ """
+
+ # If the user doesn't exist and doesn't have a pending invite
+ if not existing_user and not unanswered_invite:
+ raise ddserr.NoSuchUserError(
+ f"The user / invite with email '{user_email}' does not have access to the specified project. "
+ "Cannot remove non-existent project access."
+ )
+
+ if unanswered_invite:
+ invite_id = unanswered_invite.id
+
+ # Check if the unanswered invite is associated with the project
+ project_invite_key = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invite_id, project_id=project.id
+ ).one_or_none()
+
+ if project_invite_key:
+ # Remove the association if it exists
+ db.session.delete(project_invite_key)
+
+ # Check if the invite is associated with only one project, then delete it
project_invite_key = models.ProjectInviteKeys.query.filter_by(
- invite_id=invite_id, project_id=project.id
+ invite_id=invite_id
).one_or_none()
- # if the unasnwsered invite was asociated in the project, remove the invite and the asociation
- if project_invite_key:
- db.session.delete(project_invite_key)
-
- # if such invite was only associated with a single project, delete the invite. Otherwise leave the invite
- project_invite_key = models.ProjectInviteKeys.query.filter_by(
- invite_id=invite_id
- ).one_or_none()
- if not project_invite_key:
- db.session.delete(unanswered_invite)
- else:
- # the unanswred invite is not asociated with the project
- raise ddserr.NoSuchUserError(
- f"The user / invite with email '{user_email}' does not have access to the specified project."
- " Cannot remove non-existent project access."
- )
+ if not project_invite_key:
+ db.session.delete(unanswered_invite)
+ else:
+ # The unanswered invite is not associated with the project
+ raise ddserr.NoSuchUserError(
+ f"The user / invite with email '{user_email}' does not have access to the specified project. "
+ "Cannot remove non-existent project access."
+ )
else:
- user_in_project = False
- for user_association in project.researchusers:
- if user_association.user_id == existing_user.username:
- user_in_project = True
- db.session.delete(user_association)
- project_user_key = models.ProjectUserKeys.query.filter_by(
- project_id=project.id, user_id=existing_user.username
- ).first()
- if project_user_key:
- db.session.delete(project_user_key)
+ # User exists, check if they are associated with the project
+ user_in_project = any(
+ user_association.user_id == existing_user.username
+ for user_association in project.researchusers
+ )
if not user_in_project:
raise ddserr.NoSuchUserError(
- f"The user / invite with email '{user_email}' does not have access to the specified project."
- " Cannot remove non-existent project access."
+ f"The user / invite with email '{user_email}' does not have access to the specified project. "
+ "Cannot remove non-existent project access."
)
try:
From 6f7d890b8df2240f158d4bb3de217d061a002bbb Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 21 Sep 2023 14:25:24 +0200
Subject: [PATCH 019/155] added logic for hierarchy
---
dds_web/api/user.py | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index fee0a2c4f..bbb3254e7 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -885,8 +885,23 @@ def post(self):
Researchers only and just only revoke access for Researchers
ProjectOwners revoke access for ProjectOwners (in the same project) and Researchers (in the same project)
Unit Personnel & Unit Admins: Researchers and Project Owners (any).
+
+ rules = {
+ "Researcher": {"Researcher"},
+ "Project Owner": {"Project Owner", "Researcher"},
+ "Unit Admin": {"Project Owner", "Researcher"},
+ "Unit Personnel": {"Project Owner", "Researcher"}
+ }
+
+ allowed_users = rules.get(role) # user executing the function
+
+ if existing_user.role not in allowed_users:
+ raise Exception ("Not enough privileges to remove such user")
+
"""
+
+
# If the user doesn't exist and doesn't have a pending invite
if not existing_user and not unanswered_invite:
raise ddserr.NoSuchUserError(
From c83e6f3c8709e37d8beb23b5f40fc35b2f356f8b Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 21 Sep 2023 15:08:59 +0200
Subject: [PATCH 020/155] hierarchy functionaly logic added, todo to implement
---
dds_web/api/user.py | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index bbb3254e7..1d4a93ea1 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -898,6 +898,24 @@ def post(self):
if existing_user.role not in allowed_users:
raise Exception ("Not enough privileges to remove such user")
+
+ ----
+ We need to add functionality to check for the user exists in the project and unit
+
+ 1. Pos and Researchers only delete access in the project they are
+ 2. Unit Admin and Personal delete access for every project in the unit
+
+ user_unit = # query to get the unit(s) the user executing the function is asociated
+ user_project = # query to get the projects the user executing the function is asociated from the previous units (only for pos and researchers)
+ So, for everyone: role.unit != project.unit_id -> Exception
+ pos and researchers: if project.id not in user_project -> Exception
+
+ if user_unit != project.unit_id:
+ raise Exception("Not enough privileges to remove such user")
+
+ if role in {"Project Owner", "Researcher"} and project.id not in user_project:
+ raise Exception("Not enough privileges to remove such user")
+
"""
@@ -909,6 +927,7 @@ def post(self):
"Cannot remove non-existent project access."
)
+
if unanswered_invite:
invite_id = unanswered_invite.id
From 7f43395bea00a8aef9f373a901cbcb1cedf5ea5d Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 22 Sep 2023 12:10:52 +0200
Subject: [PATCH 021/155] hierarchy
---
dds_web/api/user.py | 95 +++++++++++++++++++++++----------------------
1 file changed, 48 insertions(+), 47 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 1d4a93ea1..836f06d12 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -878,48 +878,7 @@ def post(self):
except sqlalchemy.exc.OperationalError as err:
raise ddserr.DatabaseError(message=str(err), alt_message="Unexpected database error.")
- """
- TODO before proceding with the deletion: We need to ensure that the hierarchy of roles is ensured
- i.e That:
-
- Researchers only and just only revoke access for Researchers
- ProjectOwners revoke access for ProjectOwners (in the same project) and Researchers (in the same project)
- Unit Personnel & Unit Admins: Researchers and Project Owners (any).
-
- rules = {
- "Researcher": {"Researcher"},
- "Project Owner": {"Project Owner", "Researcher"},
- "Unit Admin": {"Project Owner", "Researcher"},
- "Unit Personnel": {"Project Owner", "Researcher"}
- }
-
- allowed_users = rules.get(role) # user executing the function
-
- if existing_user.role not in allowed_users:
- raise Exception ("Not enough privileges to remove such user")
-
-
- ----
- We need to add functionality to check for the user exists in the project and unit
-
- 1. Pos and Researchers only delete access in the project they are
- 2. Unit Admin and Personal delete access for every project in the unit
-
- user_unit = # query to get the unit(s) the user executing the function is asociated
- user_project = # query to get the projects the user executing the function is asociated from the previous units (only for pos and researchers)
- So, for everyone: role.unit != project.unit_id -> Exception
- pos and researchers: if project.id not in user_project -> Exception
-
- if user_unit != project.unit_id:
- raise Exception("Not enough privileges to remove such user")
-
- if role in {"Project Owner", "Researcher"} and project.id not in user_project:
- raise Exception("Not enough privileges to remove such user")
-
- """
-
-
-
+
# If the user doesn't exist and doesn't have a pending invite
if not existing_user and not unanswered_invite:
raise ddserr.NoSuchUserError(
@@ -927,7 +886,44 @@ def post(self):
"Cannot remove non-existent project access."
)
+ """
+ We also need to ensure the hierarchy of roles is ensured:
+
+ Researchers only revoke access for Researchers
+ ProjectOwners revoke access for ProjectOwners (in the same project) and Researchers (in the same project)
+ Unit Personnel & Unit Admins: Researchers and Project Owners (any project).
+
+ Because the access to the project has already being verified, and we are revoking access for a specific project
+ The only condition to check is that a Project Owner should not be deleted by a Researcher that is not PO
+ """
+
+ # role of the user calling the function
+ role = auth.current_user().role
+
+ # check if the user/invite trying to get revoked access is a PO
+ is_po = False
+ if unanswered_invite:
+ is_po = models.ProjectInviteKeys.query.filter_by(
+ project_id = project.id,
+ invite_id = unanswered_invite.id,
+ owner = 1
+ )
+ else:
+ is_po = models.ProjectUsers.query.filter_by(
+ project_id = project.id,
+ user_id = existing_user.username,
+ owner = 1
+ ).one_or_none()
+
+ if is_po:
+ """
+ If the user trying to be deleted is a PO -> can only be deleted by a user with higher credentials than a Researcher
+ """
+ is_po = models.ProjectUsers.query.filter_by(user_id = auth.current_user().username, project_id = project.id, owner = 1).one_or_none()
+ if role == "Researcher" and not is_po:
+ raise ddserr.AccessDeniedError()
+
if unanswered_invite:
invite_id = unanswered_invite.id
@@ -955,11 +951,16 @@ def post(self):
)
else:
- # User exists, check if they are associated with the project
- user_in_project = any(
- user_association.user_id == existing_user.username
- for user_association in project.researchusers
- )
+ user_in_project = False
+ for user_association in project.researchusers:
+ if user_association.user_id == existing_user.username:
+ user_in_project = True
+ db.session.delete(user_association)
+ project_user_key = models.ProjectUserKeys.query.filter_by(
+ project_id=project.id, user_id=existing_user.username
+ ).first()
+ if project_user_key:
+ db.session.delete(project_user_key)
if not user_in_project:
raise ddserr.NoSuchUserError(
From 8e4b175794daa38821f241611aad89474e85f019 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 22 Sep 2023 12:11:47 +0200
Subject: [PATCH 022/155] black
---
dds_web/api/user.py | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 836f06d12..6bf4dce03 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -878,7 +878,6 @@ def post(self):
except sqlalchemy.exc.OperationalError as err:
raise ddserr.DatabaseError(message=str(err), alt_message="Unexpected database error.")
-
# If the user doesn't exist and doesn't have a pending invite
if not existing_user and not unanswered_invite:
raise ddserr.NoSuchUserError(
@@ -904,15 +903,11 @@ def post(self):
is_po = False
if unanswered_invite:
is_po = models.ProjectInviteKeys.query.filter_by(
- project_id = project.id,
- invite_id = unanswered_invite.id,
- owner = 1
+ project_id=project.id, invite_id=unanswered_invite.id, owner=1
)
else:
is_po = models.ProjectUsers.query.filter_by(
- project_id = project.id,
- user_id = existing_user.username,
- owner = 1
+ project_id=project.id, user_id=existing_user.username, owner=1
).one_or_none()
if is_po:
@@ -920,10 +915,12 @@ def post(self):
If the user trying to be deleted is a PO -> can only be deleted by a user with higher credentials than a Researcher
"""
- is_po = models.ProjectUsers.query.filter_by(user_id = auth.current_user().username, project_id = project.id, owner = 1).one_or_none()
+ is_po = models.ProjectUsers.query.filter_by(
+ user_id=auth.current_user().username, project_id=project.id, owner=1
+ ).one_or_none()
if role == "Researcher" and not is_po:
raise ddserr.AccessDeniedError()
-
+
if unanswered_invite:
invite_id = unanswered_invite.id
@@ -960,7 +957,7 @@ def post(self):
project_id=project.id, user_id=existing_user.username
).first()
if project_user_key:
- db.session.delete(project_user_key)
+ db.session.delete(project_user_key)
if not user_in_project:
raise ddserr.NoSuchUserError(
From e9f1e0a85641f3eba75f122e9a92bd12922651de Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 22 Sep 2023 16:31:24 +0200
Subject: [PATCH 023/155] added
---
dds_web/api/user.py | 8 +++++---
tests/test_user_remove_association.py | 21 +++++++++++++++++++++
2 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 6bf4dce03..3578f72ff 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -859,7 +859,7 @@ def delete_invite(email):
class RemoveUserAssociation(flask_restful.Resource):
- @auth.login_required(role=["Unit Admin", "Unit Personnel", "Project Owner", "Researcher"])
+ @auth.login_required(role=["Unit Admin", "Unit Personnel", "Project Owner"])
@logging_bind_request
@json_required
@handle_validation_errors
@@ -919,7 +919,7 @@ def post(self):
user_id=auth.current_user().username, project_id=project.id, owner=1
).one_or_none()
if role == "Researcher" and not is_po:
- raise ddserr.AccessDeniedError()
+ raise ddserr.AccessDeniedError("Insufficient credentials")
if unanswered_invite:
invite_id = unanswered_invite.id
@@ -949,7 +949,7 @@ def post(self):
else:
user_in_project = False
- for user_association in project.researchusers:
+ for user_association in project.researchusers: # TODO Possible optimization -> comprehesion list
if user_association.user_id == existing_user.username:
user_in_project = True
db.session.delete(user_association)
@@ -958,6 +958,7 @@ def post(self):
).first()
if project_user_key:
db.session.delete(project_user_key)
+ break
if not user_in_project:
raise ddserr.NoSuchUserError(
@@ -994,6 +995,7 @@ def post(self):
return {"message": msg}
+
class EncryptedToken(flask_restful.Resource):
"""Generates encrypted token for the user."""
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index b097b3951..651403e4a 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -163,3 +163,24 @@ def test_remove_existing_user_from_nonexistent_proj(client, boto3_session):
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert "The specified project does not exist" in response.json["message"]
+
+
+def test_researcher_removes_project_owner(client):
+ """
+ A Researcher who is not a PO should not be able to delete a PO
+ """
+
+ project_id = "public_project_id"
+ email = "projectowner@mailtrap.io"
+
+ rem_user = {"email": email}
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["researchuser"]).token(client),
+ query_string={"project": project_id},
+ json=rem_user,
+ )
+
+ assert response.status_code == http.HTTPStatus.FORBIDDEN
+ assert "You do not have the necessary permissions" in response.json["message"]
+
From 3cb1d28b5f553edefec7671d9dae645d28196030 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 25 Sep 2023 08:04:30 +0200
Subject: [PATCH 024/155] more tests and removed unnecesary code
---
SPRINTLOG.md | 1 -
dds_web/api/user.py | 39 ++----------------------
tests/test_user_remove_association.py | 44 +++++++++++++++++++++++++++
3 files changed, 47 insertions(+), 37 deletions(-)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index 343683442..a7afd821b 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -302,7 +302,6 @@ _Nothing merged in CLI during this sprint_
- Workflow: Update PR template and clarify sections ([#1467](https://github.com/ScilifelabDataCentre/dds_web/pull/1467))
- Revoking access to unacepted invites removes the invites from the DB ([#1192])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13/backlog?epics=visible&selectedIssue=DDS-1192)
-
# 2023-09-18 - 2023-09-29
- Column `sto4_start_time` is automatically set when the create-unit command is run ([#1668])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1668)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 24fa51537..686fd332c 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -890,42 +890,6 @@ def post(self):
"Cannot remove non-existent project access."
)
- """
- We also need to ensure the hierarchy of roles is ensured:
-
- Researchers only revoke access for Researchers
- ProjectOwners revoke access for ProjectOwners (in the same project) and Researchers (in the same project)
- Unit Personnel & Unit Admins: Researchers and Project Owners (any project).
-
- Because the access to the project has already being verified, and we are revoking access for a specific project
- The only condition to check is that a Project Owner should not be deleted by a Researcher that is not PO
- """
-
- # role of the user calling the function
- role = auth.current_user().role
-
- # check if the user/invite trying to get revoked access is a PO
- is_po = False
- if unanswered_invite:
- is_po = models.ProjectInviteKeys.query.filter_by(
- project_id=project.id, invite_id=unanswered_invite.id, owner=1
- )
- else:
- is_po = models.ProjectUsers.query.filter_by(
- project_id=project.id, user_id=existing_user.username, owner=1
- ).one_or_none()
-
- if is_po:
- """
- If the user trying to be deleted is a PO -> can only be deleted by a user with higher credentials than a Researcher
- """
-
- is_po = models.ProjectUsers.query.filter_by(
- user_id=auth.current_user().username, project_id=project.id, owner=1
- ).one_or_none()
- if role == "Researcher" and not is_po:
- raise ddserr.AccessDeniedError("Insufficient credentials")
-
if unanswered_invite:
invite_id = unanswered_invite.id
@@ -953,6 +917,9 @@ def post(self):
)
else:
+ if(auth.current_user().username == existing_user.username):
+ raise ddserr.AccessDeniedError(message="You cannot renew your own access.")
+
user_in_project = False
for user_association in project.researchusers: # TODO Possible optimization -> comprehesion list
if user_association.user_id == existing_user.username:
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index 651403e4a..3deca25f9 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -184,3 +184,47 @@ def test_researcher_removes_project_owner(client):
assert response.status_code == http.HTTPStatus.FORBIDDEN
assert "You do not have the necessary permissions" in response.json["message"]
+
+def test_user_personal_removed(client):
+ """
+ User personal cannot be deleted from individual projects (they should be removed from the unit)
+ """
+
+ project_id = "public_project_id"
+ email = "unituser2@mailtrap.io"
+
+ rem_user = {"email": email}
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project_id},
+ json=rem_user,
+ )
+
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ # Should give error because a unit personal cannot be granted access to individual projects
+ assert "Cannot remove non-existent project access." in response.json["message"]
+
+def test_removed_myself(client):
+ """
+ An User cannot remove themselves from a project
+ """
+
+ project_id = "public_project_id"
+ email = "projectowner@mailtrap.io"
+
+ rem_user = {"email": email}
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["projectowner"]).token(client),
+ query_string={"project": project_id},
+ json=rem_user,
+ )
+
+ assert response.status_code == http.HTTPStatus.FORBIDDEN
+ # Should give error because a unit personal cannot be granted access to individual projects
+ assert "You cannot renew your own access." in response.json["message"]
+
+
+
+
From 4f26365f88f7c9cf6171626953ebe95208fb0d67 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 25 Sep 2023 08:04:45 +0200
Subject: [PATCH 025/155] more tests and removed unnecesary code
---
dds_web/api/user.py | 11 ++++++-----
tests/test_user_remove_association.py | 5 +----
2 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 686fd332c..19232c4ee 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -917,11 +917,13 @@ def post(self):
)
else:
- if(auth.current_user().username == existing_user.username):
- raise ddserr.AccessDeniedError(message="You cannot renew your own access.")
-
+ if auth.current_user().username == existing_user.username:
+ raise ddserr.AccessDeniedError(message="You cannot renew your own access.")
+
user_in_project = False
- for user_association in project.researchusers: # TODO Possible optimization -> comprehesion list
+ for (
+ user_association
+ ) in project.researchusers: # TODO Possible optimization -> comprehesion list
if user_association.user_id == existing_user.username:
user_in_project = True
db.session.delete(user_association)
@@ -967,7 +969,6 @@ def post(self):
return {"message": msg}
-
class EncryptedToken(flask_restful.Resource):
"""Generates encrypted token for the user."""
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index 3deca25f9..ac25fee29 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -205,6 +205,7 @@ def test_user_personal_removed(client):
# Should give error because a unit personal cannot be granted access to individual projects
assert "Cannot remove non-existent project access." in response.json["message"]
+
def test_removed_myself(client):
"""
An User cannot remove themselves from a project
@@ -224,7 +225,3 @@ def test_removed_myself(client):
assert response.status_code == http.HTTPStatus.FORBIDDEN
# Should give error because a unit personal cannot be granted access to individual projects
assert "You cannot renew your own access." in response.json["message"]
-
-
-
-
From 7e437c28f3457d0329e2102970c84784e139df60 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 25 Sep 2023 08:30:34 +0200
Subject: [PATCH 026/155] forgot to change testfile
---
tests/test_user_remove_association.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index ac25fee29..c46b12f0f 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -182,7 +182,7 @@ def test_researcher_removes_project_owner(client):
)
assert response.status_code == http.HTTPStatus.FORBIDDEN
- assert "You do not have the necessary permissions" in response.json["message"]
+ assert "Insufficient credentials" in response.json["message"]
def test_user_personal_removed(client):
From c7a3577ea6247069534a280c3f15c9f56aeb2921 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 25 Sep 2023 11:15:27 +0200
Subject: [PATCH 027/155] checks for unanswered invites of personel
---
dds_web/api/user.py | 7 +++-
tests/test_user_remove_association.py | 60 +++++++++++++++++++++++++++
2 files changed, 66 insertions(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 19232c4ee..315c8862d 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -891,6 +891,10 @@ def post(self):
)
if unanswered_invite:
+
+ if (unanswered_invite.unit_id):
+ raise ddserr.UserDeletionError("Cannot delete Unit Admin / Unit User")
+
invite_id = unanswered_invite.id
# Check if the unanswered invite is associated with the project
@@ -898,11 +902,12 @@ def post(self):
invite_id=invite_id, project_id=project.id
).one_or_none()
+ # if the invite has an unit id -> it is a unit personel invite, dont remove
if project_invite_key:
# Remove the association if it exists
db.session.delete(project_invite_key)
- # Check if the invite is associated with only one project, then delete it
+ # Check if the invite is associated with only one project, if it is -> delete the invite
project_invite_key = models.ProjectInviteKeys.query.filter_by(
invite_id=invite_id
).one_or_none()
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index c46b12f0f..191c7ffbd 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -14,6 +14,8 @@
# proj_query_restricted = {"project": "restricted_project_id"}
first_new_email = {"email": "first_test_email@mailtrap.io"}
first_new_user = {**first_new_email, "role": "Researcher"}
+first_new_user_unit_admin = {**first_new_email, "role": "Unit Admin"}
+first_new_user_unit_personel = {**first_new_email, "role": "Unit Personnel"}
# TESTS ################################################################################## TEST #
@@ -225,3 +227,61 @@ def test_removed_myself(client):
assert response.status_code == http.HTTPStatus.FORBIDDEN
# Should give error because a unit personal cannot be granted access to individual projects
assert "You cannot renew your own access." in response.json["message"]
+
+def test_remove_invite_unit_admin(client):
+ """
+ A project removal request for an unanswered invite of unit admin should not work
+ """
+
+ project_id = "public_project_id"
+
+ # invite a new unit admin to the system
+ response = client.post(
+ tests.DDSEndpoint.USER_ADD,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ json=first_new_user_unit_admin,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ # try to remove the unitadmin for a specific project within their unit -> should not work
+ email = first_new_user_unit_admin["email"]
+ rem_user = {"email": email}
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project_id},
+ json=rem_user,
+ )
+
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ # Should give error because a unit personal cannot be granted access to individual projects
+ assert "Cannot delete Unit Admin / Unit User" in response.json["message"]
+
+def test_invite_unit_user(client):
+ """
+ A project removal request for an unanswered invite of unit admin should not work
+ """
+
+ project_id = "public_project_id"
+
+ # invite a new unit user to the system
+ response = client.post(
+ tests.DDSEndpoint.USER_ADD,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ json=first_new_user_unit_personel,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ # try to remove the unit personal for a specific project within their unit -> should not work
+ email = first_new_user_unit_personel["email"]
+ rem_user = {"email": email}
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project_id},
+ json=rem_user,
+ )
+
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ # Should give error because a unit personal cannot be granted access to individual projects
+ assert "Cannot delete Unit Admin / Unit User" in response.json["message"]
From 5bb88f5f6d0b99da5ca2e08744e23f9041097930 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 25 Sep 2023 11:16:34 +0200
Subject: [PATCH 028/155] black
---
dds_web/api/user.py | 2 +-
tests/test_user_remove_association.py | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 315c8862d..cc95b3104 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -892,7 +892,7 @@ def post(self):
if unanswered_invite:
- if (unanswered_invite.unit_id):
+ if unanswered_invite.unit_id:
raise ddserr.UserDeletionError("Cannot delete Unit Admin / Unit User")
invite_id = unanswered_invite.id
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index 191c7ffbd..4c8bf01f4 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -228,6 +228,7 @@ def test_removed_myself(client):
# Should give error because a unit personal cannot be granted access to individual projects
assert "You cannot renew your own access." in response.json["message"]
+
def test_remove_invite_unit_admin(client):
"""
A project removal request for an unanswered invite of unit admin should not work
@@ -257,6 +258,7 @@ def test_remove_invite_unit_admin(client):
# Should give error because a unit personal cannot be granted access to individual projects
assert "Cannot delete Unit Admin / Unit User" in response.json["message"]
+
def test_invite_unit_user(client):
"""
A project removal request for an unanswered invite of unit admin should not work
From 94bc466f98adfb02980b866701c7cffc0bc6f862 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 25 Sep 2023 11:24:19 +0200
Subject: [PATCH 029/155] black
---
dds_web/api/user.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index cc95b3104..82b727439 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -891,7 +891,6 @@ def post(self):
)
if unanswered_invite:
-
if unanswered_invite.unit_id:
raise ddserr.UserDeletionError("Cannot delete Unit Admin / Unit User")
From c0ecaae6abd678f536aff55ce4ab1e3aa3d03ec0 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 25 Sep 2023 14:07:44 +0200
Subject: [PATCH 030/155] more explicate error message
---
dds_web/api/user.py | 3 ++-
tests/test_user_remove_association.py | 10 ++++++++--
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 82b727439..efc2f08ea 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -892,7 +892,8 @@ def post(self):
if unanswered_invite:
if unanswered_invite.unit_id:
- raise ddserr.UserDeletionError("Cannot delete Unit Admin / Unit User")
+ raise ddserr.UserDeletionError(
+ "Cannot remove a Unit Admin / Unit User from individual projects")
invite_id = unanswered_invite.id
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index 4c8bf01f4..bef154145 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -256,7 +256,10 @@ def test_remove_invite_unit_admin(client):
assert response.status_code == http.HTTPStatus.BAD_REQUEST
# Should give error because a unit personal cannot be granted access to individual projects
- assert "Cannot delete Unit Admin / Unit User" in response.json["message"]
+ assert (
+ "Cannot remove a Unit Admin / Unit User from individual projects"
+ in response.json["message"]
+ )
def test_invite_unit_user(client):
@@ -286,4 +289,7 @@ def test_invite_unit_user(client):
assert response.status_code == http.HTTPStatus.BAD_REQUEST
# Should give error because a unit personal cannot be granted access to individual projects
- assert "Cannot delete Unit Admin / Unit User" in response.json["message"]
+ assert (
+ "Cannot remove a Unit Admin / Unit User from individual projects"
+ in response.json["message"]
+ )
From 91159fe326dd4977bfaf62a44dc57cc75d8fb5e8 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 25 Sep 2023 14:09:54 +0200
Subject: [PATCH 031/155] more explicate error message
---
dds_web/api/user.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index efc2f08ea..2549c159f 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -893,7 +893,8 @@ def post(self):
if unanswered_invite:
if unanswered_invite.unit_id:
raise ddserr.UserDeletionError(
- "Cannot remove a Unit Admin / Unit User from individual projects")
+ "Cannot remove a Unit Admin / Unit User from individual projects"
+ )
invite_id = unanswered_invite.id
From 84a92c5462c9fcfaf34809f6d688bff847cf555d Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 25 Sep 2023 14:17:28 +0200
Subject: [PATCH 032/155] better coments
---
dds_web/api/user.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 2549c159f..a3c7a86cb 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -891,6 +891,8 @@ def post(self):
)
if unanswered_invite:
+ # If there is a unit_id value, it means the invite was associated to a unit
+ # i.e The invite is for a Unit Personel which shouldn't be removed from individual projects
if unanswered_invite.unit_id:
raise ddserr.UserDeletionError(
"Cannot remove a Unit Admin / Unit User from individual projects"
@@ -903,7 +905,6 @@ def post(self):
invite_id=invite_id, project_id=project.id
).one_or_none()
- # if the invite has an unit id -> it is a unit personel invite, dont remove
if project_invite_key:
# Remove the association if it exists
db.session.delete(project_invite_key)
@@ -926,6 +927,7 @@ def post(self):
if auth.current_user().username == existing_user.username:
raise ddserr.AccessDeniedError(message="You cannot renew your own access.")
+ # Search the user in the project, when found delete from the database all references to them
user_in_project = False
for (
user_association
From bd6dcd0f3b6691ca39763b82cddd984e13b9062b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 25 Sep 2023 16:08:54 +0200
Subject: [PATCH 033/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index a3c7a86cb..b5e46d95d 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -895,7 +895,7 @@ def post(self):
# i.e The invite is for a Unit Personel which shouldn't be removed from individual projects
if unanswered_invite.unit_id:
raise ddserr.UserDeletionError(
- "Cannot remove a Unit Admin / Unit User from individual projects"
+ "Cannot remove a Unit Admin / Unit Personnel from individual projects."
)
invite_id = unanswered_invite.id
From f95bc061e0bc4a298f919bcfd875885d28938204 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 25 Sep 2023 16:09:11 +0200
Subject: [PATCH 034/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index b5e46d95d..245a44208 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -925,7 +925,7 @@ def post(self):
else:
if auth.current_user().username == existing_user.username:
- raise ddserr.AccessDeniedError(message="You cannot renew your own access.")
+ raise ddserr.AccessDeniedError(message="You cannot revoke your own access.")
# Search the user in the project, when found delete from the database all references to them
user_in_project = False
From 95a36e8c392ddc134d8e3840459578c97c1052ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 25 Sep 2023 16:09:26 +0200
Subject: [PATCH 035/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 245a44208..70c71b68f 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -919,7 +919,7 @@ def post(self):
else:
# The unanswered invite is not associated with the project
raise ddserr.NoSuchUserError(
- f"The user / invite with email '{user_email}' does not have access to the specified project. "
+ f"The invite with email '{user_email}' does not have access to the specified project. "
"Cannot remove non-existent project access."
)
From ffd00cba5c22f6ca9843faad66217220c013399e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 25 Sep 2023 16:09:38 +0200
Subject: [PATCH 036/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 70c71b68f..c70ee9602 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -944,7 +944,7 @@ def post(self):
if not user_in_project:
raise ddserr.NoSuchUserError(
- f"The user / invite with email '{user_email}' does not have access to the specified project. "
+ f"The user with email '{user_email}' does not have access to the specified project. "
"Cannot remove non-existent project access."
)
From 6d96fb147e81d7e24f16ead2ce06b9634bb08c76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 25 Sep 2023 16:10:26 +0200
Subject: [PATCH 037/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index c70ee9602..f7588bcb4 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -972,7 +972,6 @@ def post(self):
else:
msg = f"User with email {user_email} no longer associated with {project.public_id}."
- flask.current_app.logger.debug(msg)
return {"message": msg}
From 822b080e4db6913b0bafe110ee28322c2d815163 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 25 Sep 2023 16:10:34 +0200
Subject: [PATCH 038/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index f7588bcb4..23456f97f 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -967,10 +967,6 @@ def post(self):
),
) from err
- if unanswered_invite:
- msg = f"Invited user is no longer associated with {project.public_id}."
- else:
- msg = f"User with email {user_email} no longer associated with {project.public_id}."
return {"message": msg}
From dced7de1cdbd28fb3d1ac60682364dd67a7f9429 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 25 Sep 2023 16:10:42 +0200
Subject: [PATCH 039/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 23456f97f..98e062a81 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -947,6 +947,7 @@ def post(self):
f"The user with email '{user_email}' does not have access to the specified project. "
"Cannot remove non-existent project access."
)
+ msg = f"User with email {user_email} no longer associated with {project.public_id}."
try:
db.session.commit()
From c4821dcb03be6b8f146e1611abe2128c5db8189f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 25 Sep 2023 16:10:53 +0200
Subject: [PATCH 040/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 98e062a81..889692f18 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -906,6 +906,7 @@ def post(self):
).one_or_none()
if project_invite_key:
+ msg = f"Invited user is no longer associated with {project.public_id}."
# Remove the association if it exists
db.session.delete(project_invite_key)
From ebf8ff04763db2cb5486b16f09ac2c8e253ce2ab Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 25 Sep 2023 16:12:17 +0200
Subject: [PATCH 041/155] black
---
dds_web/api/user.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 889692f18..020b3a3ab 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -969,8 +969,6 @@ def post(self):
),
) from err
-
-
return {"message": msg}
From cf974837d48e9fe064366bdc119c2f1428ff947b Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 25 Sep 2023 16:38:58 +0200
Subject: [PATCH 042/155] new test comment
---
tests/test_user_remove_association.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index bef154145..db6c996c4 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -257,7 +257,7 @@ def test_remove_invite_unit_admin(client):
assert response.status_code == http.HTTPStatus.BAD_REQUEST
# Should give error because a unit personal cannot be granted access to individual projects
assert (
- "Cannot remove a Unit Admin / Unit User from individual projects"
+ "Cannot remove a Unit Admin / Unit Personnel from individual projects"
in response.json["message"]
)
@@ -290,6 +290,6 @@ def test_invite_unit_user(client):
assert response.status_code == http.HTTPStatus.BAD_REQUEST
# Should give error because a unit personal cannot be granted access to individual projects
assert (
- "Cannot remove a Unit Admin / Unit User from individual projects"
+ "Cannot remove a Unit Admin / Unit Personnel from individual projects"
in response.json["message"]
)
From 0a3a35afc934281928978b3faa431691a59aca26 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 26 Sep 2023 09:24:19 +0200
Subject: [PATCH 043/155] fixed strings
---
tests/test_user_remove_association.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index db6c996c4..c2e730581 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -226,7 +226,7 @@ def test_removed_myself(client):
assert response.status_code == http.HTTPStatus.FORBIDDEN
# Should give error because a unit personal cannot be granted access to individual projects
- assert "You cannot renew your own access." in response.json["message"]
+ assert "You cannot revoke your own access" in response.json["message"]
def test_remove_invite_unit_admin(client):
From f952f3039c13ed36a2c0b782d561810b7a3bbed1 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 26 Sep 2023 10:18:41 +0200
Subject: [PATCH 044/155] refactoring test
---
tests/test_project_access.py | 62 +++++++++++----------------
tests/test_user_remove_association.py | 58 +++++++++----------------
2 files changed, 45 insertions(+), 75 deletions(-)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index fe9ce5d97..e49dbe000 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -21,6 +21,26 @@
# UTILITY FUNCTIONS ############################################################ UTILITY FUNCTIONS #
+def add_to_project(project, client, json_query):
+ response = client.post(
+ tests.DDSEndpoint.USER_ADD,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project.public_id},
+ json=json_query,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ invited_user = models.Invite.query.filter_by(email=json_query["email"]).one_or_none()
+ assert invited_user
+
+ project_invite_keys = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invited_user.id, project_id=project.id
+ ).one_or_none()
+ assert project_invite_keys
+
+ return invited_user
+
+
def delete_project_user(project_id, user_id, table_to_use):
"""Delete row in either ProjectUsers or ProjectUserKeys."""
# Get project from database
@@ -436,28 +456,9 @@ def test_remove_access_invite_associated_several_projects(client):
project_1 = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
project_2 = models.Project.query.filter_by(public_id="second_public_project_id").one_or_none()
- def add_to_project(project):
- response = client.post(
- tests.DDSEndpoint.USER_ADD,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- query_string={"project": project.public_id},
- json=first_new_user,
- )
- assert response.status_code == http.HTTPStatus.OK
-
- invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
- assert invited_user
-
- project_invite_keys = models.ProjectInviteKeys.query.filter_by(
- invite_id=invited_user.id, project_id=project.id
- ).one_or_none()
- assert project_invite_keys
-
- return invited_user
-
# invite a new user to both projects
- invited_user = add_to_project(project_1)
- _ = add_to_project(project_2)
+ invited_user = add_to_project(project=project_1, client=client, json_query=first_new_user)
+ _ = add_to_project(project=project_2, client=client, json_query=first_new_user)
# Now revoke access for the first project
response = client.post(
@@ -492,24 +493,9 @@ def test_revoking_access_to_unacepted_invite(client):
"""Revoking access to an unacepted invite for an existing project should delete the invite from the db"""
project = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
- # invite a new user to an existing project so they receive a new invite
- response = client.post(
- tests.DDSEndpoint.USER_ADD,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- query_string={"project": project.public_id},
- json=first_new_user,
- )
- assert response.status_code == http.HTTPStatus.OK
-
- invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
+ # Invite a new user to the project
+ invited_user = add_to_project(project=project, client=client, json_query=first_new_user)
invited_user_id = invited_user.id
- assert invited_user
-
- # check row was added to project invite keys table
- project_invite_keys = models.ProjectInviteKeys.query.filter_by(
- invite_id=invited_user_id, project_id=project.id
- ).one_or_none()
- assert project_invite_keys
# Now, revoke access to said user. The invite should be deleted
response = client.post(
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index c2e730581..b8f051dc7 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -6,6 +6,7 @@
import tests
from tests.test_project_creation import proj_data_with_existing_users, create_unit_admins
from dds_web.database import models
+from tests.test_project_access import add_to_project
# CONFIG ################################################################################## CONFIG #
@@ -17,6 +18,10 @@
first_new_user_unit_admin = {**first_new_email, "role": "Unit Admin"}
first_new_user_unit_personel = {**first_new_email, "role": "Unit Personnel"}
+remove_user_project_owner = {"email": "projectowner@mailtrap.io"}
+remove_user_unit_user = {"email": "unituser2@mailtrap.io"}
+remove_user_project_owner = {"email": "projectowner@mailtrap.io"}
+
# TESTS ################################################################################## TEST #
@@ -114,6 +119,8 @@ def test_remove_nonexistent_user_from_project(client, boto3_session):
def test_remove_nonacepted_user_from_other_project(client, boto3_session):
"""Try to remove an User with an unacepted invite from another project should result in an error"""
+ existing_project = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
+
create_unit_admins(num_admins=2)
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
assert current_unit_admins == 3
@@ -125,23 +132,16 @@ def test_remove_nonacepted_user_from_other_project(client, boto3_session):
json=proj_data_with_existing_users,
)
assert response.status_code == http.HTTPStatus.OK
-
- project_id = response.json.get("project_id")
+ new_project_id = response.json.get("project_id")
# invite a new user to an existing project
- response = client.post(
- tests.DDSEndpoint.USER_ADD,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
- query_string={"project": "public_project_id"},
- json=first_new_user,
- )
- assert response.status_code == http.HTTPStatus.OK
+ add_to_project(project=existing_project, client=client, json_query=first_new_user)
# try to remove the user from the first project
response = client.post(
tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
- query_string={"project": project_id},
+ query_string={"project": new_project_id},
json=first_new_user,
)
@@ -172,15 +172,12 @@ def test_researcher_removes_project_owner(client):
A Researcher who is not a PO should not be able to delete a PO
"""
- project_id = "public_project_id"
- email = "projectowner@mailtrap.io"
-
- rem_user = {"email": email}
+ # Research user trying to delete PO
response = client.post(
tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
headers=tests.UserAuth(tests.USER_CREDENTIALS["researchuser"]).token(client),
- query_string={"project": project_id},
- json=rem_user,
+ query_string=proj_query,
+ json=remove_user_project_owner,
)
assert response.status_code == http.HTTPStatus.FORBIDDEN
@@ -189,18 +186,14 @@ def test_researcher_removes_project_owner(client):
def test_user_personal_removed(client):
"""
- User personal cannot be deleted from individual projects (they should be removed from the unit)
+ User personal cannot be deleted from individual projects (they should be removed from the unit instead)
"""
- project_id = "public_project_id"
- email = "unituser2@mailtrap.io"
-
- rem_user = {"email": email}
response = client.post(
tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- query_string={"project": project_id},
- json=rem_user,
+ query_string=proj_query,
+ json=remove_user_unit_user,
)
assert response.status_code == http.HTTPStatus.BAD_REQUEST
@@ -213,19 +206,14 @@ def test_removed_myself(client):
An User cannot remove themselves from a project
"""
- project_id = "public_project_id"
- email = "projectowner@mailtrap.io"
-
- rem_user = {"email": email}
response = client.post(
tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
headers=tests.UserAuth(tests.USER_CREDENTIALS["projectowner"]).token(client),
- query_string={"project": project_id},
- json=rem_user,
+ query_string=proj_query,
+ json=remove_user_project_owner,
)
assert response.status_code == http.HTTPStatus.FORBIDDEN
- # Should give error because a unit personal cannot be granted access to individual projects
assert "You cannot revoke your own access" in response.json["message"]
@@ -234,8 +222,6 @@ def test_remove_invite_unit_admin(client):
A project removal request for an unanswered invite of unit admin should not work
"""
- project_id = "public_project_id"
-
# invite a new unit admin to the system
response = client.post(
tests.DDSEndpoint.USER_ADD,
@@ -250,7 +236,7 @@ def test_remove_invite_unit_admin(client):
response = client.post(
tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- query_string={"project": project_id},
+ query_string=proj_query,
json=rem_user,
)
@@ -264,11 +250,9 @@ def test_remove_invite_unit_admin(client):
def test_invite_unit_user(client):
"""
- A project removal request for an unanswered invite of unit admin should not work
+ A project removal request for an unanswered invite of unit personel should not work
"""
- project_id = "public_project_id"
-
# invite a new unit user to the system
response = client.post(
tests.DDSEndpoint.USER_ADD,
@@ -283,7 +267,7 @@ def test_invite_unit_user(client):
response = client.post(
tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- query_string={"project": project_id},
+ query_string=proj_query,
json=rem_user,
)
From 8f8001f84a9765c17721f5206dfa8fd50a765988 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 26 Sep 2023 11:12:09 +0200
Subject: [PATCH 045/155] refactoring more test
---
tests/test_project_access.py | 28 +++++++++++++++++++--------
tests/test_user_remove_association.py | 28 +++++++++------------------
2 files changed, 29 insertions(+), 27 deletions(-)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index e49dbe000..7616bc99a 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -17,11 +17,21 @@
first_new_email = {"email": "first_test_email@mailtrap.io"}
first_new_user = {**first_new_email, "role": "Researcher"}
-
# UTILITY FUNCTIONS ############################################################ UTILITY FUNCTIONS #
-def add_to_project(project, client, json_query):
+def get_existing_projects():
+ """Return existing projects for the tests"""
+ existing_project_1 = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
+ existing_project_2 = models.Project.query.filter_by(
+ public_id="second_public_project_id"
+ ).one_or_none()
+
+ return [existing_project_1, existing_project_2]
+
+
+def invite_to_project(project, client, json_query):
+ """Create a invitation of a user for a project"""
response = client.post(
tests.DDSEndpoint.USER_ADD,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
@@ -453,12 +463,13 @@ def test_fix_access_unitadmin_valid_email_unituser(client):
def test_remove_access_invite_associated_several_projects(client):
"""If an invite is associated with several projects then a single revoke access should not delete the invite"""
- project_1 = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
- project_2 = models.Project.query.filter_by(public_id="second_public_project_id").one_or_none()
+ projects = get_existing_projects()
+ project_1 = projects[0]
+ project_2 = projects[1]
# invite a new user to both projects
- invited_user = add_to_project(project=project_1, client=client, json_query=first_new_user)
- _ = add_to_project(project=project_2, client=client, json_query=first_new_user)
+ invited_user = invite_to_project(project=project_1, client=client, json_query=first_new_user)
+ _ = invite_to_project(project=project_2, client=client, json_query=first_new_user)
# Now revoke access for the first project
response = client.post(
@@ -491,10 +502,11 @@ def test_remove_access_invite_associated_several_projects(client):
def test_revoking_access_to_unacepted_invite(client):
"""Revoking access to an unacepted invite for an existing project should delete the invite from the db"""
- project = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
+
+ project = get_existing_projects()[0]
# Invite a new user to the project
- invited_user = add_to_project(project=project, client=client, json_query=first_new_user)
+ invited_user = invite_to_project(project=project, client=client, json_query=first_new_user)
invited_user_id = invited_user.id
# Now, revoke access to said user. The invite should be deleted
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index b8f051dc7..13ee20977 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -6,7 +6,7 @@
import tests
from tests.test_project_creation import proj_data_with_existing_users, create_unit_admins
from dds_web.database import models
-from tests.test_project_access import add_to_project
+from tests.test_project_access import invite_to_project, get_existing_projects
# CONFIG ################################################################################## CONFIG #
@@ -22,6 +22,7 @@
remove_user_unit_user = {"email": "unituser2@mailtrap.io"}
remove_user_project_owner = {"email": "projectowner@mailtrap.io"}
+
# TESTS ################################################################################## TEST #
@@ -119,29 +120,18 @@ def test_remove_nonexistent_user_from_project(client, boto3_session):
def test_remove_nonacepted_user_from_other_project(client, boto3_session):
"""Try to remove an User with an unacepted invite from another project should result in an error"""
- existing_project = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
-
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins == 3
-
- # create a new project
- response = client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
- json=proj_data_with_existing_users,
- )
- assert response.status_code == http.HTTPStatus.OK
- new_project_id = response.json.get("project_id")
+ projects = get_existing_projects()
+ project_1 = projects[0]
+ project_2 = projects[1]
- # invite a new user to an existing project
- add_to_project(project=existing_project, client=client, json_query=first_new_user)
+ # invite a new user to a project
+ invite_to_project(project=project_1, client=client, json_query=first_new_user)
- # try to remove the user from the first project
+ # try to remove the same user from a different one
response = client.post(
tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
- query_string={"project": new_project_id},
+ query_string={"project": project_2.public_id},
json=first_new_user,
)
From f70f5ba5ce865f9bd71fab43a805fc894cc5ef05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 27 Sep 2023 13:52:11 +0200
Subject: [PATCH 046/155] Update SPRINTLOG.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
SPRINTLOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index a7afd821b..d965cd8b5 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -300,7 +300,7 @@ _Nothing merged in CLI during this sprint_
- Dependency: Bump `MariaDB` to LTS version 10.11.5 ([#1465](https://github.com/ScilifelabDataCentre/dds_web/pull/1465))
- Bug fixed: Row in `ProjectUsers` should also be added if it doesn't exist when giving Researcher access to a specific project ([#1464](https://github.com/ScilifelabDataCentre/dds_web/pull/1464))
- Workflow: Update PR template and clarify sections ([#1467](https://github.com/ScilifelabDataCentre/dds_web/pull/1467))
-- Revoking access to unacepted invites removes the invites from the DB ([#1192])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13/backlog?epics=visible&selectedIssue=DDS-1192)
+- Revoke project access for unaccepted invites ([#1192])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13/backlog?epics=visible&selectedIssue=DDS-1192)
# 2023-09-18 - 2023-09-29
From e90f7cba3ee2fd5abb3ed4505a560fa649d6ed3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 27 Sep 2023 13:52:57 +0200
Subject: [PATCH 047/155] Update dds_web/api/user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 020b3a3ab..3031b4a0f 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -906,7 +906,7 @@ def post(self):
).one_or_none()
if project_invite_key:
- msg = f"Invited user is no longer associated with {project.public_id}."
+ msg = f"Invited user is no longer associated with the project '{project.public_id}'."
# Remove the association if it exists
db.session.delete(project_invite_key)
From 1c49497907f334d7ea5d71ecf6077664e3398001 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 27 Sep 2023 13:53:39 +0200
Subject: [PATCH 048/155] Update tests/test_project_access.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
tests/test_project_access.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index 7616bc99a..b311fb39e 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -27,7 +27,7 @@ def get_existing_projects():
public_id="second_public_project_id"
).one_or_none()
- return [existing_project_1, existing_project_2]
+ return existing_project_1, existing_project_2
def invite_to_project(project, client, json_query):
From 4c09789ebd591fd7374f7958e9e4d900cab0ed35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 27 Sep 2023 13:53:49 +0200
Subject: [PATCH 049/155] Update tests/test_project_access.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
tests/test_project_access.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index b311fb39e..7b4d43be9 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -463,9 +463,7 @@ def test_fix_access_unitadmin_valid_email_unituser(client):
def test_remove_access_invite_associated_several_projects(client):
"""If an invite is associated with several projects then a single revoke access should not delete the invite"""
- projects = get_existing_projects()
- project_1 = projects[0]
- project_2 = projects[1]
+ project_1, project_2 = get_existing_projects()
# invite a new user to both projects
invited_user = invite_to_project(project=project_1, client=client, json_query=first_new_user)
From 1c3bab60f30e4b41522c6123cdcaf1ac42f0c4db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 27 Sep 2023 13:54:32 +0200
Subject: [PATCH 050/155] Update tests/test_project_access.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
tests/test_project_access.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index 7b4d43be9..6b2c098ae 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -501,7 +501,7 @@ def test_remove_access_invite_associated_several_projects(client):
def test_revoking_access_to_unacepted_invite(client):
"""Revoking access to an unacepted invite for an existing project should delete the invite from the db"""
- project = get_existing_projects()[0]
+ project, _ = get_existing_projects()
# Invite a new user to the project
invited_user = invite_to_project(project=project, client=client, json_query=first_new_user)
From 241647b5e8d7ca74275b97b4ecbf388ee6cb87dd Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Wed, 27 Sep 2023 14:17:29 +0200
Subject: [PATCH 051/155] black and comments
---
dds_web/api/user.py | 6 ++++--
tests/test_project_access.py | 4 ++--
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index 3031b4a0f..d8fb964aa 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -906,7 +906,9 @@ def post(self):
).one_or_none()
if project_invite_key:
- msg = f"Invited user is no longer associated with the project '{project.public_id}'."
+ msg = (
+ f"Invited user is no longer associated with the project '{project.public_id}'."
+ )
# Remove the association if it exists
db.session.delete(project_invite_key)
@@ -941,7 +943,7 @@ def post(self):
).first()
if project_user_key:
db.session.delete(project_user_key)
- break
+ break
if not user_in_project:
raise ddserr.NoSuchUserError(
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index 6b2c098ae..59364a60a 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -479,7 +479,7 @@ def test_remove_access_invite_associated_several_projects(client):
assert response.status_code == http.HTTPStatus.OK
assert (
- f"Invited user is no longer associated with {project_1.public_id}."
+ f"Invited user is no longer associated with the project {project_1.public_id}."
in response.json["message"]
)
@@ -517,7 +517,7 @@ def test_revoking_access_to_unacepted_invite(client):
assert response.status_code == http.HTTPStatus.OK
assert (
- f"Invited user is no longer associated with {project.public_id}."
+ f"Invited user is no longer associated with the project {project.public_id}."
in response.json["message"]
)
From cd891b2a355781134a8e747262a0645613685dd8 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Wed, 27 Sep 2023 14:44:56 +0200
Subject: [PATCH 052/155] moved tests
---
tests/api/test_user.py | 255 ++++++++++++++++++++++++++
tests/test_project_access.py | 102 -----------
tests/test_user_remove_association.py | 142 --------------
3 files changed, 255 insertions(+), 244 deletions(-)
diff --git a/tests/api/test_user.py b/tests/api/test_user.py
index 3473a96eb..f9fdba731 100644
--- a/tests/api/test_user.py
+++ b/tests/api/test_user.py
@@ -16,11 +16,15 @@
import werkzeug
import time
+# CONFIG ################################################################################## CONFIG #
+
existing_project = "public_project_id"
existing_project_2 = "second_public_project_id"
first_new_email = {"email": "first_test_email@mailtrap.io"}
first_new_user = {**first_new_email, "role": "Researcher"}
first_new_owner = {**first_new_email, "role": "Project Owner"}
+first_new_user_unit_admin = {**first_new_email, "role": "Unit Admin"}
+first_new_user_unit_personel = {**first_new_email, "role": "Unit Personnel"}
first_new_user_existing_project = {**first_new_user, "project": "public_project_id"}
first_new_user_extra_args = {**first_new_user, "extra": "test"}
first_new_user_invalid_role = {**first_new_email, "role": "Invalid Role"}
@@ -48,6 +52,43 @@
**existing_research_user_owner,
"project": "second_public_project_id",
}
+remove_user_project_owner = {"email": "projectowner@mailtrap.io"}
+remove_user_unit_user = {"email": "unituser2@mailtrap.io"}
+remove_user_project_owner = {"email": "projectowner@mailtrap.io"}
+
+# UTILITY FUNCTIONS ############################################################ UTILITY FUNCTIONS #
+
+
+def get_existing_projects():
+ """Return existing projects for the tests"""
+ existing_project_1 = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
+ existing_project_2 = models.Project.query.filter_by(
+ public_id="second_public_project_id"
+ ).one_or_none()
+
+ return existing_project_1, existing_project_2
+
+
+def invite_to_project(project, client, json_query):
+ """Create a invitation of a user for a project"""
+ response = client.post(
+ tests.DDSEndpoint.USER_ADD,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project.public_id},
+ json=json_query,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ invited_user = models.Invite.query.filter_by(email=json_query["email"]).one_or_none()
+ assert invited_user
+
+ project_invite_keys = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invited_user.id, project_id=project.id
+ ).one_or_none()
+ assert project_invite_keys
+
+ return invited_user
+
# AddUser ################################################################# AddUser #
@@ -1258,3 +1299,217 @@ def get_list(as_user) -> dict:
assert response.status_code == http.HTTPStatus.FORBIDDEN
assert not response.json.get("invites")
assert not response.json.get("keys")
+
+
+##### Test for RemoveUserAssociation
+
+
+def test_remove_access_invite_associated_several_projects(client):
+ """If an invite is associated with several projects then a single revoke access should not delete the invite"""
+
+ project_1, project_2 = get_existing_projects()
+
+ # invite a new user to both projects
+ invited_user = invite_to_project(project=project_1, client=client, json_query=first_new_user)
+ _ = invite_to_project(project=project_2, client=client, json_query=first_new_user)
+
+ # Now revoke access for the first project
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project_1.public_id},
+ json=first_new_user,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ assert (
+ f"Invited user is no longer associated with the project '{project_1.public_id}'."
+ in response.json["message"]
+ )
+
+ # The project invite row should only be deleted for project 1 and the invite should still exist
+ invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
+ assert invited_user
+
+ project_invite_keys = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invited_user.id, project_id=project_2.id
+ ).one_or_none()
+ assert project_invite_keys
+
+ project_invite_keys = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invited_user.id, project_id=project_1.id
+ ).one_or_none()
+ assert not project_invite_keys
+
+
+def test_revoking_access_to_unacepted_invite(client):
+ """Revoking access to an unacepted invite for an existing project should delete the invite from the db"""
+
+ project, _ = get_existing_projects()
+
+ # Invite a new user to the project
+ invited_user = invite_to_project(project=project, client=client, json_query=first_new_user)
+ invited_user_id = invited_user.id
+
+ # Now, revoke access to said user. The invite should be deleted
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project.public_id},
+ json=first_new_user,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ assert (
+ f"Invited user is no longer associated with the project {project.public_id}."
+ in response.json["message"]
+ )
+
+ # Check that the invite is deleted
+ invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
+ assert not invited_user
+
+ project_invite_keys = models.ProjectInviteKeys.query.filter_by(
+ invite_id=invited_user_id, project_id=project.id
+ ).one_or_none()
+ assert not project_invite_keys
+
+
+def test_remove_nonacepted_user_from_other_project(client, boto3_session):
+ """Try to remove an User with an unacepted invite from another project should result in an error"""
+
+ projects = get_existing_projects()
+ project_1 = projects[0]
+ project_2 = projects[1]
+
+ # invite a new user to a project
+ invite_to_project(project=project_1, client=client, json_query=first_new_user)
+
+ # try to remove the same user from a different one
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
+ query_string={"project": project_2.public_id},
+ json=first_new_user,
+ )
+
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ assert "Cannot remove non-existent project access" in response.json["message"]
+
+
+def test_researcher_removes_project_owner(client):
+ """
+ A Researcher who is not a PO should not be able to delete a PO
+ """
+
+ project, _ = get_existing_projects()
+
+ # Research user trying to delete PO
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["researchuser"]).token(client),
+ query_string={"project": project.public_id},
+ json=remove_user_project_owner,
+ )
+
+ assert response.status_code == http.HTTPStatus.FORBIDDEN
+ assert "Insufficient credentials" in response.json["message"]
+
+
+def test_user_personal_removed(client):
+ """
+ User personal cannot be deleted from individual projects (they should be removed from the unit instead)
+ """
+ project, _ = get_existing_projects()
+
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project.public_id},
+ json=remove_user_unit_user,
+ )
+
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ # Should give error because a unit personal cannot be granted access to individual projects
+ assert "Cannot remove non-existent project access." in response.json["message"]
+
+
+def test_removed_myself(client):
+ """
+ An User cannot remove themselves from a project
+ """
+ project, _ = get_existing_projects()
+
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["projectowner"]).token(client),
+ query_string={"project": project.public_id},
+ json=remove_user_project_owner,
+ )
+
+ assert response.status_code == http.HTTPStatus.FORBIDDEN
+ assert "You cannot revoke your own access" in response.json["message"]
+
+
+def test_remove_invite_unit_admin(client):
+ """
+ A project removal request for an unanswered invite of unit admin should not work
+ """
+ project, _ = get_existing_projects()
+
+ # invite a new unit admin to the system
+ response = client.post(
+ tests.DDSEndpoint.USER_ADD,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ json=first_new_user_unit_admin,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ # try to remove the unitadmin for a specific project within their unit -> should not work
+ email = first_new_user_unit_admin["email"]
+ rem_user = {"email": email}
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project.public_id},
+ json=rem_user,
+ )
+
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ # Should give error because a unit personal cannot be granted access to individual projects
+ assert (
+ "Cannot remove a Unit Admin / Unit Personnel from individual projects"
+ in response.json["message"]
+ )
+
+
+def test_invite_unit_user(client):
+ """
+ A project removal request for an unanswered invite of unit personel should not work
+ """
+ project, _ = get_existing_projects()
+
+ # invite a new unit user to the system
+ response = client.post(
+ tests.DDSEndpoint.USER_ADD,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ json=first_new_user_unit_personel,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ # try to remove the unit personal for a specific project within their unit -> should not work
+ email = first_new_user_unit_personel["email"]
+ rem_user = {"email": email}
+ response = client.post(
+ tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ query_string={"project": project.public_id},
+ json=rem_user,
+ )
+
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ # Should give error because a unit personal cannot be granted access to individual projects
+ assert (
+ "Cannot remove a Unit Admin / Unit Personnel from individual projects"
+ in response.json["message"]
+ )
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index 59364a60a..f551feeab 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -20,37 +20,6 @@
# UTILITY FUNCTIONS ############################################################ UTILITY FUNCTIONS #
-def get_existing_projects():
- """Return existing projects for the tests"""
- existing_project_1 = models.Project.query.filter_by(public_id="public_project_id").one_or_none()
- existing_project_2 = models.Project.query.filter_by(
- public_id="second_public_project_id"
- ).one_or_none()
-
- return existing_project_1, existing_project_2
-
-
-def invite_to_project(project, client, json_query):
- """Create a invitation of a user for a project"""
- response = client.post(
- tests.DDSEndpoint.USER_ADD,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- query_string={"project": project.public_id},
- json=json_query,
- )
- assert response.status_code == http.HTTPStatus.OK
-
- invited_user = models.Invite.query.filter_by(email=json_query["email"]).one_or_none()
- assert invited_user
-
- project_invite_keys = models.ProjectInviteKeys.query.filter_by(
- invite_id=invited_user.id, project_id=project.id
- ).one_or_none()
- assert project_invite_keys
-
- return invited_user
-
-
def delete_project_user(project_id, user_id, table_to_use):
"""Delete row in either ProjectUsers or ProjectUserKeys."""
# Get project from database
@@ -460,77 +429,6 @@ def test_fix_access_unitadmin_valid_email_unituser(client):
assert user_project_key_row
-def test_remove_access_invite_associated_several_projects(client):
- """If an invite is associated with several projects then a single revoke access should not delete the invite"""
-
- project_1, project_2 = get_existing_projects()
-
- # invite a new user to both projects
- invited_user = invite_to_project(project=project_1, client=client, json_query=first_new_user)
- _ = invite_to_project(project=project_2, client=client, json_query=first_new_user)
-
- # Now revoke access for the first project
- response = client.post(
- tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- query_string={"project": project_1.public_id},
- json=first_new_user,
- )
- assert response.status_code == http.HTTPStatus.OK
-
- assert (
- f"Invited user is no longer associated with the project {project_1.public_id}."
- in response.json["message"]
- )
-
- # The project invite row should only be deleted for project 1 and the invite should still exist
- invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
- assert invited_user
-
- project_invite_keys = models.ProjectInviteKeys.query.filter_by(
- invite_id=invited_user.id, project_id=project_2.id
- ).one_or_none()
- assert project_invite_keys
-
- project_invite_keys = models.ProjectInviteKeys.query.filter_by(
- invite_id=invited_user.id, project_id=project_1.id
- ).one_or_none()
- assert not project_invite_keys
-
-
-def test_revoking_access_to_unacepted_invite(client):
- """Revoking access to an unacepted invite for an existing project should delete the invite from the db"""
-
- project, _ = get_existing_projects()
-
- # Invite a new user to the project
- invited_user = invite_to_project(project=project, client=client, json_query=first_new_user)
- invited_user_id = invited_user.id
-
- # Now, revoke access to said user. The invite should be deleted
- response = client.post(
- tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- query_string={"project": project.public_id},
- json=first_new_user,
- )
- assert response.status_code == http.HTTPStatus.OK
-
- assert (
- f"Invited user is no longer associated with the project {project.public_id}."
- in response.json["message"]
- )
-
- # Check that the invite is deleted
- invited_user = models.Invite.query.filter_by(email=first_new_user["email"]).one_or_none()
- assert not invited_user
-
- project_invite_keys = models.ProjectInviteKeys.query.filter_by(
- invite_id=invited_user_id, project_id=project.id
- ).one_or_none()
- assert not project_invite_keys
-
-
def test_fix_access_unitadmin_valid_email_unituser_no_project(client):
"""Unit Admin giving access to unituser - ok. No project."""
# Remove ProjectUserKeys row for specific project and user
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index 13ee20977..eb79c449d 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -6,7 +6,6 @@
import tests
from tests.test_project_creation import proj_data_with_existing_users, create_unit_admins
from dds_web.database import models
-from tests.test_project_access import invite_to_project, get_existing_projects
# CONFIG ################################################################################## CONFIG #
@@ -15,13 +14,6 @@
# proj_query_restricted = {"project": "restricted_project_id"}
first_new_email = {"email": "first_test_email@mailtrap.io"}
first_new_user = {**first_new_email, "role": "Researcher"}
-first_new_user_unit_admin = {**first_new_email, "role": "Unit Admin"}
-first_new_user_unit_personel = {**first_new_email, "role": "Unit Personnel"}
-
-remove_user_project_owner = {"email": "projectowner@mailtrap.io"}
-remove_user_unit_user = {"email": "unituser2@mailtrap.io"}
-remove_user_project_owner = {"email": "projectowner@mailtrap.io"}
-
# TESTS ################################################################################## TEST #
@@ -117,28 +109,6 @@ def test_remove_nonexistent_user_from_project(client, boto3_session):
assert "Cannot remove non-existent project access" in response.json["message"]
-def test_remove_nonacepted_user_from_other_project(client, boto3_session):
- """Try to remove an User with an unacepted invite from another project should result in an error"""
-
- projects = get_existing_projects()
- project_1 = projects[0]
- project_2 = projects[1]
-
- # invite a new user to a project
- invite_to_project(project=project_1, client=client, json_query=first_new_user)
-
- # try to remove the same user from a different one
- response = client.post(
- tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
- query_string={"project": project_2.public_id},
- json=first_new_user,
- )
-
- assert response.status_code == http.HTTPStatus.BAD_REQUEST
- assert "Cannot remove non-existent project access" in response.json["message"]
-
-
def test_remove_existing_user_from_nonexistent_proj(client, boto3_session):
"""Try to an existing user from a nonexistent project"""
@@ -155,115 +125,3 @@ def test_remove_existing_user_from_nonexistent_proj(client, boto3_session):
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert "The specified project does not exist" in response.json["message"]
-
-
-def test_researcher_removes_project_owner(client):
- """
- A Researcher who is not a PO should not be able to delete a PO
- """
-
- # Research user trying to delete PO
- response = client.post(
- tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["researchuser"]).token(client),
- query_string=proj_query,
- json=remove_user_project_owner,
- )
-
- assert response.status_code == http.HTTPStatus.FORBIDDEN
- assert "Insufficient credentials" in response.json["message"]
-
-
-def test_user_personal_removed(client):
- """
- User personal cannot be deleted from individual projects (they should be removed from the unit instead)
- """
-
- response = client.post(
- tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- query_string=proj_query,
- json=remove_user_unit_user,
- )
-
- assert response.status_code == http.HTTPStatus.BAD_REQUEST
- # Should give error because a unit personal cannot be granted access to individual projects
- assert "Cannot remove non-existent project access." in response.json["message"]
-
-
-def test_removed_myself(client):
- """
- An User cannot remove themselves from a project
- """
-
- response = client.post(
- tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["projectowner"]).token(client),
- query_string=proj_query,
- json=remove_user_project_owner,
- )
-
- assert response.status_code == http.HTTPStatus.FORBIDDEN
- assert "You cannot revoke your own access" in response.json["message"]
-
-
-def test_remove_invite_unit_admin(client):
- """
- A project removal request for an unanswered invite of unit admin should not work
- """
-
- # invite a new unit admin to the system
- response = client.post(
- tests.DDSEndpoint.USER_ADD,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- json=first_new_user_unit_admin,
- )
- assert response.status_code == http.HTTPStatus.OK
-
- # try to remove the unitadmin for a specific project within their unit -> should not work
- email = first_new_user_unit_admin["email"]
- rem_user = {"email": email}
- response = client.post(
- tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- query_string=proj_query,
- json=rem_user,
- )
-
- assert response.status_code == http.HTTPStatus.BAD_REQUEST
- # Should give error because a unit personal cannot be granted access to individual projects
- assert (
- "Cannot remove a Unit Admin / Unit Personnel from individual projects"
- in response.json["message"]
- )
-
-
-def test_invite_unit_user(client):
- """
- A project removal request for an unanswered invite of unit personel should not work
- """
-
- # invite a new unit user to the system
- response = client.post(
- tests.DDSEndpoint.USER_ADD,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- json=first_new_user_unit_personel,
- )
- assert response.status_code == http.HTTPStatus.OK
-
- # try to remove the unit personal for a specific project within their unit -> should not work
- email = first_new_user_unit_personel["email"]
- rem_user = {"email": email}
- response = client.post(
- tests.DDSEndpoint.REMOVE_USER_FROM_PROJ,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
- query_string=proj_query,
- json=rem_user,
- )
-
- assert response.status_code == http.HTTPStatus.BAD_REQUEST
- # Should give error because a unit personal cannot be granted access to individual projects
- assert (
- "Cannot remove a Unit Admin / Unit Personnel from individual projects"
- in response.json["message"]
- )
From 8685e4af7ecac9f7c0d61437fba3e872bc83b99a Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Wed, 27 Sep 2023 14:50:41 +0200
Subject: [PATCH 053/155] updated tes
---
tests/api/test_user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/api/test_user.py b/tests/api/test_user.py
index f9fdba731..f9ceebc1e 100644
--- a/tests/api/test_user.py
+++ b/tests/api/test_user.py
@@ -1361,7 +1361,7 @@ def test_revoking_access_to_unacepted_invite(client):
assert response.status_code == http.HTTPStatus.OK
assert (
- f"Invited user is no longer associated with the project {project.public_id}."
+ f"Invited user is no longer associated with the project '{project.public_id}'."
in response.json["message"]
)
From 4b6c10249b4de6473acf2eafa77658dc5e29912a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 27 Sep 2023 15:39:25 +0200
Subject: [PATCH 054/155] Update tests/api/test_user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
tests/api/test_user.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/tests/api/test_user.py b/tests/api/test_user.py
index f9ceebc1e..6bb5ecf9b 100644
--- a/tests/api/test_user.py
+++ b/tests/api/test_user.py
@@ -1378,9 +1378,7 @@ def test_revoking_access_to_unacepted_invite(client):
def test_remove_nonacepted_user_from_other_project(client, boto3_session):
"""Try to remove an User with an unacepted invite from another project should result in an error"""
- projects = get_existing_projects()
- project_1 = projects[0]
- project_2 = projects[1]
+ project_1, project_2 = get_existing_projects()
# invite a new user to a project
invite_to_project(project=project_1, client=client, json_query=first_new_user)
From 26a88f1b25687f1cefa8be19a3c7a08ecfe842b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 27 Sep 2023 15:39:39 +0200
Subject: [PATCH 055/155] Update tests/api/test_user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
tests/api/test_user.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/api/test_user.py b/tests/api/test_user.py
index 6bb5ecf9b..b2f9a24aa 100644
--- a/tests/api/test_user.py
+++ b/tests/api/test_user.py
@@ -1414,9 +1414,9 @@ def test_researcher_removes_project_owner(client):
assert "Insufficient credentials" in response.json["message"]
-def test_user_personal_removed(client):
+def test_unit_personnel_removed(client):
"""
- User personal cannot be deleted from individual projects (they should be removed from the unit instead)
+ Unit Personnel cannot be deleted from individual projects (they should be removed from the unit instead)
"""
project, _ = get_existing_projects()
From 6a28caea8ef3eeb9e1d4a42b5b56cae8722b8480 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 27 Sep 2023 15:39:51 +0200
Subject: [PATCH 056/155] Update tests/api/test_user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
tests/api/test_user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/api/test_user.py b/tests/api/test_user.py
index b2f9a24aa..5dc0b9600 100644
--- a/tests/api/test_user.py
+++ b/tests/api/test_user.py
@@ -1506,7 +1506,7 @@ def test_invite_unit_user(client):
)
assert response.status_code == http.HTTPStatus.BAD_REQUEST
- # Should give error because a unit personal cannot be granted access to individual projects
+ # Should give error because a Unit Personnel cannot be granted access to individual projects
assert (
"Cannot remove a Unit Admin / Unit Personnel from individual projects"
in response.json["message"]
From daaeeed87fd3f80bb0a22b177a281c50ef143859 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 27 Sep 2023 15:39:59 +0200
Subject: [PATCH 057/155] Update tests/test_user_remove_association.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
tests/test_user_remove_association.py | 8 --------
1 file changed, 8 deletions(-)
diff --git a/tests/test_user_remove_association.py b/tests/test_user_remove_association.py
index eb79c449d..34b726886 100644
--- a/tests/test_user_remove_association.py
+++ b/tests/test_user_remove_association.py
@@ -7,14 +7,6 @@
from tests.test_project_creation import proj_data_with_existing_users, create_unit_admins
from dds_web.database import models
-# CONFIG ################################################################################## CONFIG #
-
-# proj_data = {"pi": "piName", "title": "Test proj", "description": "A longer project description"}
-proj_query = {"project": "public_project_id"}
-# proj_query_restricted = {"project": "restricted_project_id"}
-first_new_email = {"email": "first_test_email@mailtrap.io"}
-first_new_user = {**first_new_email, "role": "Researcher"}
-
# TESTS ################################################################################## TEST #
From 38d42680bcaaeb01b25c921ab07ec5281843faf3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 27 Sep 2023 15:40:07 +0200
Subject: [PATCH 058/155] Update tests/test_project_access.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
tests/test_project_access.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/tests/test_project_access.py b/tests/test_project_access.py
index f551feeab..9466f7c73 100644
--- a/tests/test_project_access.py
+++ b/tests/test_project_access.py
@@ -14,8 +14,6 @@
# proj_data = {"pi": "piName", "title": "Test proj", "description": "A longer project description"}
proj_query = {"project": "public_project_id"}
# proj_query_restricted = {"project": "restricted_project_id"}
-first_new_email = {"email": "first_test_email@mailtrap.io"}
-first_new_user = {**first_new_email, "role": "Researcher"}
# UTILITY FUNCTIONS ############################################################ UTILITY FUNCTIONS #
From e5bfb52c0c25e2029340decb908b30943cf4a2db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Wed, 27 Sep 2023 15:40:17 +0200
Subject: [PATCH 059/155] Update tests/api/test_user.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
tests/api/test_user.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/api/test_user.py b/tests/api/test_user.py
index 5dc0b9600..615590e5a 100644
--- a/tests/api/test_user.py
+++ b/tests/api/test_user.py
@@ -1495,7 +1495,7 @@ def test_invite_unit_user(client):
)
assert response.status_code == http.HTTPStatus.OK
- # try to remove the unit personal for a specific project within their unit -> should not work
+ # try to remove the Unit Personnel for a specific project within their unit -> should not work
email = first_new_user_unit_personel["email"]
rem_user = {"email": email}
response = client.post(
From 88254d2f799f1c9e509ac36e3e1b2e91b9e44ce9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ina=20Od=C3=A9n=20=C3=96sterbo?=
Date: Thu, 28 Sep 2023 14:58:44 +0200
Subject: [PATCH 060/155] comment about whitelisted IPs
---
dds_web/database/models.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/database/models.py b/dds_web/database/models.py
index 104fa932d..8d27ea779 100644
--- a/dds_web/database/models.py
+++ b/dds_web/database/models.py
@@ -195,7 +195,7 @@ class Unit(db.Model):
sto2_access = db.Column(db.String(255), unique=False, nullable=True) # unique=True later
sto2_secret = db.Column(db.String(255), unique=False, nullable=True) # unique=True later
- # New safespring storage
+ # New safespring storage - NOTE: MAKE SURE IPS ARE WHITELISTED ON UPPMAX AND OTHER SERVERS
sto4_start_time = db.Column(db.DateTime(), nullable=True)
sto4_endpoint = db.Column(db.String(255), unique=False, nullable=True) # unique=True later
sto4_name = db.Column(db.String(255), unique=False, nullable=True) # unique=True later
From 70e5d6045441d72fb78f71ff2741a1baca6b948e Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 3 Oct 2023 10:43:44 +0200
Subject: [PATCH 061/155] new info
---
dds_web/api/user.py | 4 ++++
dds_web/templates/mail/project_release.html | 16 +++++++++++-----
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/dds_web/api/user.py b/dds_web/api/user.py
index d8fb964aa..35ec90405 100644
--- a/dds_web/api/user.py
+++ b/dds_web/api/user.py
@@ -423,6 +423,7 @@ def compose_and_send_email_to_user(userobj, mail_type, link=None, project=None):
unit_email = None
project_id = None
+ project_title = None
deadline = None
# Don't display unit admins or personnels name
@@ -440,6 +441,7 @@ def compose_and_send_email_to_user(userobj, mail_type, link=None, project=None):
elif mail_type == "project_release":
subject = f"Project made available by {displayed_sender} in the SciLifeLab Data Delivery System"
project_id = project.public_id
+ project_title = project.title
deadline = project.current_deadline.astimezone(datetime.timezone.utc).strftime(
"%Y-%m-%d %H:%M:%S %Z"
)
@@ -470,6 +472,7 @@ def compose_and_send_email_to_user(userobj, mail_type, link=None, project=None):
displayed_sender=displayed_sender,
unit_email=unit_email,
project_id=project_id,
+ project_title=project_title,
deadline=deadline,
)
msg.html = flask.render_template(
@@ -478,6 +481,7 @@ def compose_and_send_email_to_user(userobj, mail_type, link=None, project=None):
displayed_sender=displayed_sender,
unit_email=unit_email,
project_id=project_id,
+ project_title=project_title,
deadline=deadline,
)
diff --git a/dds_web/templates/mail/project_release.html b/dds_web/templates/mail/project_release.html
index 24262a413..1a8ef6272 100644
--- a/dds_web/templates/mail/project_release.html
+++ b/dds_web/templates/mail/project_release.html
@@ -10,11 +10,13 @@
-
The
- project {{project_id}} is now available for your access in the SciLifeLab Data Delivery System (DDS).
-
- The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple
- way.
+
+ The following project is now available for your access in the SciLifeLab Data Delivery System (DDS) and you can now download your data.
+
+
Project Title: {{project_title}}
+
DDS project ID: {{project_id}}
+
+
{% if unit_email %}
You were added to this project on behalf of {{displayed_sender}} ({{unit_email}}).
@@ -28,6 +30,10 @@
The DDS CLI command dds data get -p {{project_id}} -a can be used to download all the files in this project to your current directory.
Your access to this project will expire on {{deadline}}
+
+ What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple
+ way.
+
From 30465f27978f53a98f4bdc912644db6db0fe8bd0 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 3 Oct 2023 11:31:08 +0200
Subject: [PATCH 062/155] txt templatw
---
dds_web/templates/mail/project_release.txt | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/dds_web/templates/mail/project_release.txt b/dds_web/templates/mail/project_release.txt
index 121d5ea6f..a7cf4774c 100644
--- a/dds_web/templates/mail/project_release.txt
+++ b/dds_web/templates/mail/project_release.txt
@@ -1,5 +1,6 @@
-The project {{project_id}} is now available for your access in the SciLifeLab Data Delivery System (DDS).
-The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way.
+The following project is now available for your access in the SciLifeLab Data Delivery System (DDS) and you can now download your data.
+ - Project Title: {{project_title}}
+ - DDS project ID: {{project_id}}
{% if unit_email %}
You were added to this project on behalf of {{displayed_sender}} ({{unit_email}}).
@@ -12,3 +13,6 @@ The DDS CLI command 'dds ls -p {{project_id}}' can be used to list the files in
The DDS CLI command 'dds data get -p {{project_id}} -a' can be used to download all the files in this project to your current directory.
Your access to this project will expire on {{deadline}}
+
+What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way.
+
From 2364ced2e80fe99d5617bac497e77fbb17b3ec58 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Wed, 4 Oct 2023 12:57:19 +0200
Subject: [PATCH 063/155] test email
---
tests/api/test_project.py | 43 +++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index cd1d7f740..81a3a7b9a 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1388,3 +1388,46 @@ def test_project_usage(module_client):
# Call project_usage() for the project and check if cost is calculated correctly
proj_bhours, proj_cost = UserProjects.project_usage(project=project_0)
assert (proj_bhours / 1e9) * cost_gbhour == proj_cost
+
+
+def test_email_project_release(module_client):
+ """Test that the email to the researches is sent when the project has been released
+ Function is compose_and_send_email_to_user used at project.py
+ """
+
+ # Create unit admins to allow project creation
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ # Create project
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ project_id = response.json.get("project_id")
+
+ # Release project and check email
+ with unittest.mock.patch.object(flask_mail.Mail) as mock_mail:
+ with mock_mail.record_messages() as outbox:
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"new_status": "Available"},
+ )
+ assert len(outbox) == 1
+ assert "Project made available by" in outbox[0].subject
+
+ assert response.status_code == http.HTTPStatus.OK
+
+
+# msg = outbox[-1]
+# assert msg.subject == const.RESET_EMAIL_SUBJECT
+# assert 'Reset Password' in msg.html
+# assert 'Reset Password' in msg.body
From c8378300aee63c9171db6d36ad9149c776282ec6 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Wed, 4 Oct 2023 13:30:48 +0200
Subject: [PATCH 064/155] changed client
---
tests/api/test_project.py | 22 ++++++++++------------
1 file changed, 10 insertions(+), 12 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 81a3a7b9a..fd909f3c1 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1390,37 +1390,35 @@ def test_project_usage(module_client):
assert (proj_bhours / 1e9) * cost_gbhour == proj_cost
-def test_email_project_release(module_client):
+def test_email_project_release(client):
"""Test that the email to the researches is sent when the project has been released
Function is compose_and_send_email_to_user used at project.py
"""
-
- # Create unit admins to allow project creation
+ create_unit_admins(num_admins=2)
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- if current_unit_admins < 3:
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins >= 3
+ assert current_unit_admins == 3
- # Create project
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
json=proj_data,
)
assert response.status_code == http.HTTPStatus.OK
project_id = response.json.get("project_id")
+ assert response.status_code == http.HTTPStatus.OK
+
# Release project and check email
with unittest.mock.patch.object(flask_mail.Mail) as mock_mail:
with mock_mail.record_messages() as outbox:
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
query_string={"project": project_id},
json={"new_status": "Available"},
)
+
assert len(outbox) == 1
assert "Project made available by" in outbox[0].subject
From c45a2e5edf385440c474f7e3b41371c3a44e45e8 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Wed, 4 Oct 2023 15:32:52 +0200
Subject: [PATCH 065/155] added tests
---
tests/api/test_project.py | 39 ++++++++++++++++++---------------------
1 file changed, 18 insertions(+), 21 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index fd909f3c1..27966c903 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -16,7 +16,7 @@
# Own
import dds_web
-from dds_web import db
+from dds_web import auth, mail, db, basic_auth, limiter
from dds_web.errors import BucketNotFoundError, DatabaseError, DeletionError
import tests
from tests.test_files_new import project_row, file_in_db, FIRST_NEW_FILE
@@ -1390,7 +1390,7 @@ def test_project_usage(module_client):
assert (proj_bhours / 1e9) * cost_gbhour == proj_cost
-def test_email_project_release(client):
+def test_email_project_release(client,boto3_session):
"""Test that the email to the researches is sent when the project has been released
Function is compose_and_send_email_to_user used at project.py
"""
@@ -1401,31 +1401,28 @@ def test_email_project_release(client):
response = client.post(
tests.DDSEndpoint.PROJECT_CREATE,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
- json=proj_data,
+ json=proj_data_with_existing_users,
)
assert response.status_code == http.HTTPStatus.OK
- project_id = response.json.get("project_id")
-
- assert response.status_code == http.HTTPStatus.OK
+ public_project_id = response.json.get("project_id")
# Release project and check email
- with unittest.mock.patch.object(flask_mail.Mail) as mock_mail:
- with mock_mail.record_messages() as outbox:
- response = client.post(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
- query_string={"project": project_id},
- json={"new_status": "Available"},
- )
-
- assert len(outbox) == 1
- assert "Project made available by" in outbox[0].subject
+ with mail.record_messages() as outbox:
+ response = client.post(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
+ query_string={"project": public_project_id},
+ json={"new_status": "Available", "deadline": 10, "send_email": True},
+ )
+ assert len(outbox) == 3
+ assert "Project made available by" in outbox[-1].subject
+ # TODO check the body of the email
+ # msg = outbox[-1]
+# assert msg.subject == const.RESET_EMAIL_SUBJECT
+# assert 'Reset Password' in msg.html
+# assert 'Reset Password' in msg.body
assert response.status_code == http.HTTPStatus.OK
-# msg = outbox[-1]
-# assert msg.subject == const.RESET_EMAIL_SUBJECT
-# assert 'Reset Password' in msg.html
-# assert 'Reset Password' in msg.body
From e5bbc5d6bbad21890b153804c71a55a4f925d1b4 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 5 Oct 2023 09:50:27 +0200
Subject: [PATCH 066/155] tryit
---
tests/api/test_project.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 27966c903..23375d5dd 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -16,7 +16,7 @@
# Own
import dds_web
-from dds_web import auth, mail, db, basic_auth, limiter
+from dds_web import mail, db
from dds_web.errors import BucketNotFoundError, DatabaseError, DeletionError
import tests
from tests.test_files_new import project_row, file_in_db, FIRST_NEW_FILE
@@ -1421,7 +1421,7 @@ def test_email_project_release(client,boto3_session):
# msg = outbox[-1]
# assert msg.subject == const.RESET_EMAIL_SUBJECT
# assert 'Reset Password' in msg.html
-# assert 'Reset Password' in msg.body
+# assert 'Reset Password' in msg.body -> plain text
assert response.status_code == http.HTTPStatus.OK
From 353ad737a121a17f167501bbe5c3643bd0ed7813 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 5 Oct 2023 09:55:43 +0200
Subject: [PATCH 067/155] black
---
tests/api/test_project.py | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 23375d5dd..1fc557fa5 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1390,7 +1390,7 @@ def test_project_usage(module_client):
assert (proj_bhours / 1e9) * cost_gbhour == proj_cost
-def test_email_project_release(client,boto3_session):
+def test_email_project_release(client, boto3_session):
"""Test that the email to the researches is sent when the project has been released
Function is compose_and_send_email_to_user used at project.py
"""
@@ -1419,10 +1419,8 @@ def test_email_project_release(client,boto3_session):
assert "Project made available by" in outbox[-1].subject
# TODO check the body of the email
# msg = outbox[-1]
-# assert msg.subject == const.RESET_EMAIL_SUBJECT
-# assert 'Reset Password' in msg.html
-# assert 'Reset Password' in msg.body -> plain text
+ # assert msg.subject == const.RESET_EMAIL_SUBJECT
+ # assert 'Reset Password' in msg.html
+ # assert 'Reset Password' in msg.body -> plain text
assert response.status_code == http.HTTPStatus.OK
-
-
From 9ee7ae2dce933c00ccdd182525393fad3f1298cf Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 5 Oct 2023 10:23:12 +0200
Subject: [PATCH 068/155] changes to module client
---
tests/api/test_project.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 1fc557fa5..854373c81 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1390,7 +1390,7 @@ def test_project_usage(module_client):
assert (proj_bhours / 1e9) * cost_gbhour == proj_cost
-def test_email_project_release(client, boto3_session):
+def test_email_project_release(module_client, boto3_session):
"""Test that the email to the researches is sent when the project has been released
Function is compose_and_send_email_to_user used at project.py
"""
@@ -1398,9 +1398,9 @@ def test_email_project_release(client, boto3_session):
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
assert current_unit_admins == 3
- response = client.post(
+ response = module_client.post(
tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
json=proj_data_with_existing_users,
)
assert response.status_code == http.HTTPStatus.OK
@@ -1409,9 +1409,9 @@ def test_email_project_release(client, boto3_session):
# Release project and check email
with mail.record_messages() as outbox:
- response = client.post(
+ response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
query_string={"project": public_project_id},
json={"new_status": "Available", "deadline": 10, "send_email": True},
)
From dd394b72348ec0e7e5214948a43e08bba3be95f1 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 5 Oct 2023 10:57:51 +0200
Subject: [PATCH 069/155] completed test
---
tests/api/test_project.py | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 854373c81..8ef851f1d 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1396,11 +1396,13 @@ def test_email_project_release(module_client, boto3_session):
"""
create_unit_admins(num_admins=2)
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins == 3
+ assert current_unit_admins >= 3
+
+ token = tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client)
response = module_client.post(
tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ headers=token,
json=proj_data_with_existing_users,
)
assert response.status_code == http.HTTPStatus.OK
@@ -1411,16 +1413,25 @@ def test_email_project_release(module_client, boto3_session):
with mail.record_messages() as outbox:
response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ headers=token,
query_string={"project": public_project_id},
json={"new_status": "Available", "deadline": 10, "send_email": True},
)
assert len(outbox) == 3
assert "Project made available by" in outbox[-1].subject
- # TODO check the body of the email
- # msg = outbox[-1]
- # assert msg.subject == const.RESET_EMAIL_SUBJECT
- # assert 'Reset Password' in msg.html
- # assert 'Reset Password' in msg.body -> plain text
+
+ body = outbox[-1].body #plain text
+ html = outbox[-1].html
+
+ project_title = proj_data_with_existing_users["title"]
+
+ ## check plain text message
+ assert f"- Project Title: {project_title}" in outbox[-1].body
+ assert f"- DDS project ID: {public_project_id}" in outbox[-1].body
+
+ ## check html
+
+ assert f"
Project Title: {project_title}
"
+ assert f"
DDS project ID: {public_project_id}
"
assert response.status_code == http.HTTPStatus.OK
From 7ba770375995a706150919c30ac03bada453f89e Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 5 Oct 2023 11:00:16 +0200
Subject: [PATCH 070/155] black
---
tests/api/test_project.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 8ef851f1d..dbb6812a1 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1397,7 +1397,7 @@ def test_email_project_release(module_client, boto3_session):
create_unit_admins(num_admins=2)
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
assert current_unit_admins >= 3
-
+
token = tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client)
response = module_client.post(
@@ -1419,8 +1419,8 @@ def test_email_project_release(module_client, boto3_session):
)
assert len(outbox) == 3
assert "Project made available by" in outbox[-1].subject
-
- body = outbox[-1].body #plain text
+
+ body = outbox[-1].body # plain text
html = outbox[-1].html
project_title = proj_data_with_existing_users["title"]
From 4eee28e501e4f9d36def9fdd145eea59a87aeedd Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 5 Oct 2023 11:13:27 +0200
Subject: [PATCH 071/155] files
---
dds_web/api/superadmin_only.py | 2 +-
tests/api/test_superadmin_only.py | 14 ++++++++++----
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/dds_web/api/superadmin_only.py b/dds_web/api/superadmin_only.py
index 44fae0fa0..3c73305f1 100644
--- a/dds_web/api/superadmin_only.py
+++ b/dds_web/api/superadmin_only.py
@@ -164,7 +164,7 @@ def post(self):
# Create email content
# put motd_obj.message etc in there etc
- subject: str = "DDS Important Information"
+ subject: str = "Important Information: Data Delivery System"
body: str = flask.render_template(f"mail/motd.txt", motd=motd_obj.message)
html = flask.render_template(f"mail/motd.html", motd=motd_obj.message)
diff --git a/tests/api/test_superadmin_only.py b/tests/api/test_superadmin_only.py
index 022c537a7..54d600058 100644
--- a/tests/api/test_superadmin_only.py
+++ b/tests/api/test_superadmin_only.py
@@ -20,7 +20,7 @@
import click
# Own
-from dds_web import db
+from dds_web import db, mail
from dds_web.database import models
import tests
from dds_web.commands import collect_stats
@@ -155,9 +155,15 @@ def test_create_motd_as_superadmin_empty_message(client: flask.testing.FlaskClie
def test_create_motd_as_superadmin_success(client: flask.testing.FlaskClient) -> None:
"""Create a new message of the day, using a Super Admin account."""
token: typing.Dict = get_token(username=users["Super Admin"], client=client)
- response: werkzeug.test.WrapperTestResponse = client.post(
- tests.DDSEndpoint.MOTD, headers=token, json={"message": "test"}
- )
+
+ with mail.record_messages() as outbox:
+ response: werkzeug.test.WrapperTestResponse = client.post(
+ tests.DDSEndpoint.MOTD, headers=token, json={"message": "test"}
+ )
+
+ assert len(outbox) == 1
+ assert "Important Information: Data Delivery System" in outbox[-1].subject
+
assert response.status_code == http.HTTPStatus.OK
assert "The MOTD was successfully added to the database." in response.json.get("message")
From 5527e1288cdfc8227ae532bf1c756c98f889bca8 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 5 Oct 2023 11:23:13 +0200
Subject: [PATCH 072/155] sprintlog and change int
---
SPRINTLOG.md | 4 ++++
tests/api/test_project.py | 10 +++++-----
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index d965cd8b5..3b0a4e900 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -307,3 +307,7 @@ _Nothing merged in CLI during this sprint_
- Column `sto4_start_time` is automatically set when the create-unit command is run ([#1668])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1668)
- Replace expired invites when there's a new invitation attempt ([#1466](https://github.com/ScilifelabDataCentre/dds_web/pull/1466))
- New version: 2.5.1 ([#1471](https://github.com/ScilifelabDataCentre/dds_web/pull/1471))
+
+# 2023-10-02 - 2023-10-13
+
+- Added the project title aling with the internal project ID in the email sent when a project is released ([#1537])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1537)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index dbb6812a1..e798960f8 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1417,7 +1417,7 @@ def test_email_project_release(module_client, boto3_session):
query_string={"project": public_project_id},
json={"new_status": "Available", "deadline": 10, "send_email": True},
)
- assert len(outbox) == 3
+ assert len(outbox) == 2 # Emails informing researchers
assert "Project made available by" in outbox[-1].subject
body = outbox[-1].body # plain text
@@ -1426,12 +1426,12 @@ def test_email_project_release(module_client, boto3_session):
project_title = proj_data_with_existing_users["title"]
## check plain text message
- assert f"- Project Title: {project_title}" in outbox[-1].body
- assert f"- DDS project ID: {public_project_id}" in outbox[-1].body
+ assert f"- Project Title: {project_title}" in body
+ assert f"- DDS project ID: {public_project_id}" in body
## check html
- assert f"
Project Title: {project_title}
"
- assert f"
DDS project ID: {public_project_id}
"
+ assert f"
Project Title: {project_title}
" in html
+ assert f"
DDS project ID: {public_project_id}
" in html
assert response.status_code == http.HTTPStatus.OK
From f7a770bccab7ba9e619cc839af8989fdb0a97ff5 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 5 Oct 2023 11:24:18 +0200
Subject: [PATCH 073/155] black
---
tests/api/test_project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index e798960f8..a73cda918 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1417,7 +1417,7 @@ def test_email_project_release(module_client, boto3_session):
query_string={"project": public_project_id},
json={"new_status": "Available", "deadline": 10, "send_email": True},
)
- assert len(outbox) == 2 # Emails informing researchers
+ assert len(outbox) == 2 # Emails informing researchers
assert "Project made available by" in outbox[-1].subject
body = outbox[-1].body # plain text
From caba69046dbbc5de4f16d50c9add98ad667a286d Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 5 Oct 2023 11:54:59 +0200
Subject: [PATCH 074/155] sprintlog and move test
---
SPRINTLOG.md | 4 ++++
tests/api/test_superadmin_only.py | 17 ++++++-----------
2 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index d965cd8b5..bdf8ee865 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -307,3 +307,7 @@ _Nothing merged in CLI during this sprint_
- Column `sto4_start_time` is automatically set when the create-unit command is run ([#1668])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1668)
- Replace expired invites when there's a new invitation attempt ([#1466](https://github.com/ScilifelabDataCentre/dds_web/pull/1466))
- New version: 2.5.1 ([#1471](https://github.com/ScilifelabDataCentre/dds_web/pull/1471))
+
+# 2023-10-02 - 2023-10-13
+
+- Changed email subject when motd is release to send the whole DDS name and not just the acronynm ([#1422])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1422)
diff --git a/tests/api/test_superadmin_only.py b/tests/api/test_superadmin_only.py
index 54d600058..d06df07b0 100644
--- a/tests/api/test_superadmin_only.py
+++ b/tests/api/test_superadmin_only.py
@@ -155,15 +155,9 @@ def test_create_motd_as_superadmin_empty_message(client: flask.testing.FlaskClie
def test_create_motd_as_superadmin_success(client: flask.testing.FlaskClient) -> None:
"""Create a new message of the day, using a Super Admin account."""
token: typing.Dict = get_token(username=users["Super Admin"], client=client)
-
- with mail.record_messages() as outbox:
- response: werkzeug.test.WrapperTestResponse = client.post(
- tests.DDSEndpoint.MOTD, headers=token, json={"message": "test"}
- )
-
- assert len(outbox) == 1
- assert "Important Information: Data Delivery System" in outbox[-1].subject
-
+ response: werkzeug.test.WrapperTestResponse = client.post(
+ tests.DDSEndpoint.MOTD, headers=token, json={"message": "test"}
+ )
assert response.status_code == http.HTTPStatus.OK
assert "The MOTD was successfully added to the database." in response.json.get("message")
@@ -611,12 +605,13 @@ def test_send_motd_ok(client: flask.testing.FlaskClient) -> None:
num_users = models.User.query.count()
# Attempt request
- with unittest.mock.patch.object(flask_mail.Connection, "send") as mock_mail_send:
+ with mail.record_messages() as outbox:
response: werkzeug.test.WrapperTestResponse = client.post(
tests.DDSEndpoint.MOTD_SEND, headers=token, json={"motd_id": created_motd.id}
)
assert response.status_code == http.HTTPStatus.OK
- assert mock_mail_send.call_count == num_users
+ assert len(outbox) == num_users
+ assert "Important Information: Data Delivery System" in outbox[-1].subject
# Maintenance ######################################################################################
From 9eb94c5d4a434926be94fe352223d485400fc667 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 5 Oct 2023 12:48:12 +0200
Subject: [PATCH 075/155] last test
---
tests/api/test_superadmin_only.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/tests/api/test_superadmin_only.py b/tests/api/test_superadmin_only.py
index d06df07b0..c3e4e44b8 100644
--- a/tests/api/test_superadmin_only.py
+++ b/tests/api/test_superadmin_only.py
@@ -577,13 +577,14 @@ def test_send_motd_no_primary_email(client: flask.testing.FlaskClient) -> None:
assert not models.Email.query.filter_by(email=email).one_or_none()
assert not models.User.query.filter_by(username=username).one().primary_email
- # Attempt request
- with unittest.mock.patch.object(flask_mail.Connection, "send") as mock_mail_send:
+ # Attempt request and catch email
+ with mail.record_messages() as outbox:
response: werkzeug.test.WrapperTestResponse = client.post(
tests.DDSEndpoint.MOTD_SEND, headers=token, json={"motd_id": created_motd.id}
)
assert response.status_code == http.HTTPStatus.OK
- assert mock_mail_send.call_count == num_users - 1
+ assert len(outbox) == num_users - 1
+ assert "Important Information: Data Delivery System" in outbox[-1].subject
def test_send_motd_ok(client: flask.testing.FlaskClient) -> None:
@@ -604,7 +605,7 @@ def test_send_motd_ok(client: flask.testing.FlaskClient) -> None:
# Get number of users
num_users = models.User.query.count()
- # Attempt request
+ # Attempt request and catch email
with mail.record_messages() as outbox:
response: werkzeug.test.WrapperTestResponse = client.post(
tests.DDSEndpoint.MOTD_SEND, headers=token, json={"motd_id": created_motd.id}
From 2038b39855aea4eb37229549cb7fa78c3c55d0b3 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 5 Oct 2023 13:08:17 +0200
Subject: [PATCH 076/155] refactoring
---
tests/api/test_project.py | 24 ++++++++++--------------
1 file changed, 10 insertions(+), 14 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index a73cda918..867da5ef3 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1391,23 +1391,19 @@ def test_project_usage(module_client):
def test_email_project_release(module_client, boto3_session):
- """Test that the email to the researches is sent when the project has been released
- Function is compose_and_send_email_to_user used at project.py
- """
+ """Test that check that the email sent to the researchers when project is released is correct"""
+ public_project_id = "public_project_id"
+
create_unit_admins(num_admins=2)
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
assert current_unit_admins >= 3
+ # user to perfrom the operation
token = tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client)
-
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=token,
- json=proj_data_with_existing_users,
- )
- assert response.status_code == http.HTTPStatus.OK
-
- public_project_id = response.json.get("project_id")
+ # project to be released
+ project = models.Project.query.filter_by(public_id=public_project_id).first()
+ # num of researchers that will receive email
+ num_users = models.ProjectUsers.query.filter_by(project_id=project.id).count()
# Release project and check email
with mail.record_messages() as outbox:
@@ -1417,13 +1413,13 @@ def test_email_project_release(module_client, boto3_session):
query_string={"project": public_project_id},
json={"new_status": "Available", "deadline": 10, "send_email": True},
)
- assert len(outbox) == 2 # Emails informing researchers
+ assert len(outbox) == num_users # nº of Emails informing researchers
assert "Project made available by" in outbox[-1].subject
body = outbox[-1].body # plain text
html = outbox[-1].html
- project_title = proj_data_with_existing_users["title"]
+ project_title = project.title
## check plain text message
assert f"- Project Title: {project_title}" in body
From 55727c54baf42d1732625e68d1c125f4868e0c23 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Thu, 5 Oct 2023 17:39:28 +0200
Subject: [PATCH 077/155] Update SPRINTLOG.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
SPRINTLOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index 3b0a4e900..d8c6aa2c7 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -310,4 +310,4 @@ _Nothing merged in CLI during this sprint_
# 2023-10-02 - 2023-10-13
-- Added the project title aling with the internal project ID in the email sent when a project is released ([#1537])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1537)
+- Project title displayed along with the internal project ID email sent when a project is released ([#1537])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1537)
From ca812f327d7ce37f9461e92ae0378f761eafd141 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Thu, 5 Oct 2023 17:39:34 +0200
Subject: [PATCH 078/155] Update dds_web/templates/mail/project_release.html
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/templates/mail/project_release.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/templates/mail/project_release.html b/dds_web/templates/mail/project_release.html
index 1a8ef6272..45f6ad4cd 100644
--- a/dds_web/templates/mail/project_release.html
+++ b/dds_web/templates/mail/project_release.html
@@ -11,7 +11,7 @@
- The following project is now available for your access in the SciLifeLab Data Delivery System (DDS) and you can now download your data.
+ The following project is now available for your access in the SciLifeLab Data Delivery System (DDS) and you can download your data.
Your access to this project will expire on {{deadline}}
- What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple
+ What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple
way.
From 7894f51b2681cb9b9328880cc7801fe7a228d4f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Thu, 5 Oct 2023 17:42:02 +0200
Subject: [PATCH 080/155] Update SPRINTLOG.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
SPRINTLOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index bdf8ee865..5e2242f7b 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -310,4 +310,4 @@ _Nothing merged in CLI during this sprint_
# 2023-10-02 - 2023-10-13
-- Changed email subject when motd is release to send the whole DDS name and not just the acronynm ([#1422])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1422)
+- Use full DDS name in MOTD email subject ([#1422])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1422)
From c61513665111c3f8132f4f2fbe1f1784d54bd178 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Thu, 5 Oct 2023 17:42:10 +0200
Subject: [PATCH 081/155] Update tests/api/test_superadmin_only.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
tests/api/test_superadmin_only.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/tests/api/test_superadmin_only.py b/tests/api/test_superadmin_only.py
index c3e4e44b8..ca1d5d428 100644
--- a/tests/api/test_superadmin_only.py
+++ b/tests/api/test_superadmin_only.py
@@ -585,6 +585,7 @@ def test_send_motd_no_primary_email(client: flask.testing.FlaskClient) -> None:
assert response.status_code == http.HTTPStatus.OK
assert len(outbox) == num_users - 1
assert "Important Information: Data Delivery System" in outbox[-1].subject
+ assert "incorrect subject" not in outbox[-1].subject
def test_send_motd_ok(client: flask.testing.FlaskClient) -> None:
From fbd6efec87324dfe0c2af4db4d2f18c52a26a42f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Fri, 6 Oct 2023 11:01:10 +0200
Subject: [PATCH 082/155] Update SPRINTLOG.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
SPRINTLOG.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index 7ddbbd5d9..09dc78670 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -310,5 +310,5 @@ _Nothing merged in CLI during this sprint_
# 2023-10-02 - 2023-10-13
-- Use full DDS name in MOTD email subject ([#1422])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1422)
-- Project title displayed along with the internal project ID email sent when a project is released ([#1537])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1537)
+- Project title displayed along with the internal project ID email sent when a project is released ([#1475](https://github.com/ScilifelabDataCentre/dds_web/pull/1475))
+- Use full DDS name in MOTD email subject ([#1477](https://github.com/ScilifelabDataCentre/dds_web/pull/1477))
From bc389e90d1af250a1fd96e70ab9c8f26fb66d09e Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 6 Oct 2023 12:02:37 +0200
Subject: [PATCH 083/155] new templates
---
dds_web/templates/mail/project_release.html | 2 +-
dds_web/templates/mail/project_release.txt | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/dds_web/templates/mail/project_release.html b/dds_web/templates/mail/project_release.html
index 1e96f384c..4183de5fa 100644
--- a/dds_web/templates/mail/project_release.html
+++ b/dds_web/templates/mail/project_release.html
@@ -27,7 +27,7 @@
The DDS CLI command dds ls -p {{project_id}} can be used to list the files in this project.
- The DDS CLI command dds data get -p {{project_id}} -a can be used to download all the files in this project to your current directory.
+ The DDS CLI command dds data get -p {{project_id}} -a --verify-checksum can be used to download all the files in this project to your current directory.
Your access to this project will expire on {{deadline}}
diff --git a/dds_web/templates/mail/project_release.txt b/dds_web/templates/mail/project_release.txt
index a7cf4774c..162f0a803 100644
--- a/dds_web/templates/mail/project_release.txt
+++ b/dds_web/templates/mail/project_release.txt
@@ -10,7 +10,7 @@ You were added to this project by {{displayed_sender}}.
The DDS CLI command 'dds ls -p {{project_id}}' can be used to list the files in this project.
-The DDS CLI command 'dds data get -p {{project_id}} -a' can be used to download all the files in this project to your current directory.
+The DDS CLI command 'dds data get -p {{project_id}} -a --verify-checksum' can be used to download all the files in this project to your current directory.
Your access to this project will expire on {{deadline}}
From 972f9d706ee09bc1b98f284e824dce34cafda1e4 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 6 Oct 2023 12:42:27 +0200
Subject: [PATCH 084/155] springlog
---
SPRINTLOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index 09dc78670..c96aac575 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -310,5 +310,6 @@ _Nothing merged in CLI during this sprint_
# 2023-10-02 - 2023-10-13
+- Add flag --verify-checksum to the comand in email template ([#1409])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1409)
- Project title displayed along with the internal project ID email sent when a project is released ([#1475](https://github.com/ScilifelabDataCentre/dds_web/pull/1475))
- Use full DDS name in MOTD email subject ([#1477](https://github.com/ScilifelabDataCentre/dds_web/pull/1477))
From 2bdd4ce6d30414d61edbbe8cf3b28936794cc51c Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 6 Oct 2023 15:27:05 +0200
Subject: [PATCH 085/155] correct sprintlog
---
SPRINTLOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index c96aac575..ee717956f 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -310,6 +310,6 @@ _Nothing merged in CLI during this sprint_
# 2023-10-02 - 2023-10-13
-- Add flag --verify-checksum to the comand in email template ([#1409])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1409)
- Project title displayed along with the internal project ID email sent when a project is released ([#1475](https://github.com/ScilifelabDataCentre/dds_web/pull/1475))
- Use full DDS name in MOTD email subject ([#1477](https://github.com/ScilifelabDataCentre/dds_web/pull/1477))
+- Add flag --verify-checksum to the comand in email template ([#1478])(https://github.com/ScilifelabDataCentre/dds_web/pull/1478)
From e986301602345d1051cb448c82710dd318c71f4d Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 9 Oct 2023 10:59:30 +0200
Subject: [PATCH 086/155] modified files for functionality
---
dds_web/templates/mail/mail_base.html | 23 ++++++++++++++++++-
dds_web/templates/mail/project_release.html | 25 +++++++++++++--------
dds_web/templates/mail/project_release.txt | 18 ++++++++++-----
3 files changed, 50 insertions(+), 16 deletions(-)
diff --git a/dds_web/templates/mail/mail_base.html b/dds_web/templates/mail/mail_base.html
index d0dd52e7f..5fbafcd8a 100644
--- a/dds_web/templates/mail/mail_base.html
+++ b/dds_web/templates/mail/mail_base.html
@@ -1,6 +1,23 @@
-
+
+
@@ -122,6 +139,10 @@
border-color: #a00202 !important;
}
}
+ mark {
+ background-color: pink;
+ color: black;
+}
diff --git a/dds_web/templates/mail/project_release.html b/dds_web/templates/mail/project_release.html
index 4183de5fa..ae49f4b70 100644
--- a/dds_web/templates/mail/project_release.html
+++ b/dds_web/templates/mail/project_release.html
@@ -18,22 +18,29 @@
+ You were added to this project on behalf of {{displayed_sender}}.
+
+
+ To list the files in this project, run:
+ dds ls -p {{project_id}}.
+
+ To download all the files in this project to your current directory, run:
+ dds data get -p {{project_id}} -a --verify-checksum.
+
{% if unit_email %}
- You were added to this project on behalf of {{displayed_sender}} ({{unit_email}}).
+ if you experience issues, please contact the SciLifeLab unit {{displayed_sender}} at ({{unit_email}}).
{% else %}
- You were added to this project by {{displayed_sender}}.
+ if you experience issues, please contact the SciLifeLab unit {{displayed_sender}}.
{% endif %}
-
- The DDS CLI command dds ls -p {{project_id}} can be used to list the files in this project.
-
- The DDS CLI command dds data get -p {{project_id}} -a --verify-checksum can be used to download all the files in this project to your current directory.
-
- Your access to this project will expire on {{deadline}}
+
+ Your access to this project will expire on: {{deadline}}
What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple
way.
-
diff --git a/dds_web/templates/mail/project_release.txt b/dds_web/templates/mail/project_release.txt
index 162f0a803..e8d88dda9 100644
--- a/dds_web/templates/mail/project_release.txt
+++ b/dds_web/templates/mail/project_release.txt
@@ -2,16 +2,22 @@ The following project is now available for your access in the SciLifeLab Data De
- Project Title: {{project_title}}
- DDS project ID: {{project_id}}
+You were added to this project on behalf of {{displayed_sender}}.
+
+To list the files in this project, run:
+ dds ls -p {{project_id}}
+
+To download all the files in this project to your current directory, run:
+ dds data get -p {{project_id}} -a --verify-checksum.
+
+For more information (including an installation guide), see the DDS CLI documentation here: https://scilifelabdatacentre.github.io/dds_cli/
+
{% if unit_email %}
-You were added to this project on behalf of {{displayed_sender}} ({{unit_email}}).
+if you experience issues, please contact the SciLifeLab unit {{displayed_sender}}at ({{unit_email}}).
{% else %}
-You were added to this project by {{displayed_sender}}.
+if you experience issues, please contact the SciLifeLab unit {{displayed_sender}}.
{% endif %}
-The DDS CLI command 'dds ls -p {{project_id}}' can be used to list the files in this project.
-
-The DDS CLI command 'dds data get -p {{project_id}} -a --verify-checksum' can be used to download all the files in this project to your current directory.
-
Your access to this project will expire on {{deadline}}
What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way.
From 720e5042837fe234454d2d93df71921f01d25f1e Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 9 Oct 2023 11:14:54 +0200
Subject: [PATCH 087/155] new tests
---
dds_web/templates/mail/project_release.html | 3 +--
tests/api/test_project.py | 9 ++++++++-
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/dds_web/templates/mail/project_release.html b/dds_web/templates/mail/project_release.html
index ae49f4b70..c5ed54bf3 100644
--- a/dds_web/templates/mail/project_release.html
+++ b/dds_web/templates/mail/project_release.html
@@ -39,8 +39,7 @@
Your access to this project will expire on: {{deadline}}
- What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple
- way.
+ What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way.
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 867da5ef3..1032d31e8 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1424,10 +1424,17 @@ def test_email_project_release(module_client, boto3_session):
## check plain text message
assert f"- Project Title: {project_title}" in body
assert f"- DDS project ID: {public_project_id}" in body
+ assert f"dds ls -p {public_project_id}" in body
+ assert f"dds data get -p {public_project_id} -a --verify-checksum" in body
+ assert "if you experience issues, please contact the SciLifeLab unit" in body
+ assert "What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way" in body
## check html
-
assert f"
Project Title: {project_title}
" in html
assert f"
DDS project ID: {public_project_id}
" in html
+ assert f"dds ls -p {public_project_id}" in html
+ assert f"dds data get -p {public_project_id} -a --verify-checksum" in html
+ assert "if you experience issues, please contact the SciLifeLab unit" in html
+ assert "What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way." in html
assert response.status_code == http.HTTPStatus.OK
From 43c45e20a3125e2f21a0e53297334288fed9abf7 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 9 Oct 2023 11:17:38 +0200
Subject: [PATCH 088/155] test
---
tests/api/test_project.py | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 1032d31e8..89d5e1c2a 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1427,7 +1427,10 @@ def test_email_project_release(module_client, boto3_session):
assert f"dds ls -p {public_project_id}" in body
assert f"dds data get -p {public_project_id} -a --verify-checksum" in body
assert "if you experience issues, please contact the SciLifeLab unit" in body
- assert "What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way" in body
+ assert (
+ "What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way"
+ in body
+ )
## check html
assert f"
Project Title: {project_title}
" in html
@@ -1435,6 +1438,9 @@ def test_email_project_release(module_client, boto3_session):
assert f"dds ls -p {public_project_id}" in html
assert f"dds data get -p {public_project_id} -a --verify-checksum" in html
assert "if you experience issues, please contact the SciLifeLab unit" in html
- assert "What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way." in html
+ assert (
+ "What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way."
+ in html
+ )
assert response.status_code == http.HTTPStatus.OK
From c49699b62be0c6c17044d5ed9f667791e8f3c23b Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 9 Oct 2023 11:18:59 +0200
Subject: [PATCH 089/155] springlog
---
SPRINTLOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index ee717956f..32341599a 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -313,3 +313,4 @@ _Nothing merged in CLI during this sprint_
- Project title displayed along with the internal project ID email sent when a project is released ([#1475](https://github.com/ScilifelabDataCentre/dds_web/pull/1475))
- Use full DDS name in MOTD email subject ([#1477](https://github.com/ScilifelabDataCentre/dds_web/pull/1477))
- Add flag --verify-checksum to the comand in email template ([#1478])(https://github.com/ScilifelabDataCentre/dds_web/pull/1478)
+- Improved on email layout when project release ([#1479])(https://github.com/ScilifelabDataCentre/dds_web/pull/1479)
From 724322dad09ed48a050e370b806d8e86a170f4c8 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 9 Oct 2023 15:53:36 +0200
Subject: [PATCH 090/155] new functionality
---
dds_web/api/project.py | 96 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 96 insertions(+)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 4b00c8042..6f0cf572b 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -188,6 +188,102 @@ def post(self):
return {"message": return_message}
+ @auth.login_required(role=["Unit Admin", "Unit Personnel"])
+ @logging_bind_request
+ @json_required
+ @handle_validation_errors
+ def patch(self):
+ """Partially update a the project status"""
+ # Get project ID, project and verify access
+ project_id = dds_web.utils.get_required_item(obj=flask.request.args, req="project")
+ project = dds_web.utils.collect_project(project_id=project_id)
+ dds_web.utils.verify_project_access(project=project)
+
+ # Cannot change project status if project is busy
+ if project.busy:
+ raise ProjectBusyError(
+ message=(
+ f"The status for the project '{project_id}' is already in the process of being changed. "
+ "Please try again later. \n\nIf you know the project is not busy, contact support."
+ )
+ )
+
+ self.set_busy(project=project, busy=True)
+
+ try:
+ # get atributes
+ json_input = flask.request.get_json(silent=True) # Already checked by json_required
+ extend_deadline = json_input.get("extend_deadline", False) # False by default
+
+ # some variable definition
+ curr_date = dds_web.utils.current_time()
+ send_email = False
+
+ if extend_deadline:
+ # deadline can only be extended from Available
+ if not project.current_status == "Available":
+ raise DDSArgumentError("Can only extend deadline if the project is available")
+
+ new_deadline_in = json_input.get("new_deadline_in")
+ if not new_deadline_in:
+ raise DDSArgumentError(
+ message="No new deadline provived, cannot perform operation."
+ )
+ if new_deadline_in > 90:
+ raise DDSArgumentError(
+ message="The deadline needs to be less than (or equal to) 90 days."
+ )
+
+ if project.times_expired > 2:
+ raise DDSArgumentError(
+ "Project availability limit: Project cannot have an extended deadline any more times"
+ )
+
+ try:
+ # add a fake archived status to mimick a re-release in order to have an udpated deadline
+ new_status_row = self.expire_project(
+ project=project,
+ current_time=curr_date,
+ deadline_in=project.responsible_unit.days_in_expired,
+ )
+ project.project_statuses.append(new_status_row)
+ db.session.commit()
+
+ new_status_row = self.release_project(
+ project=project, current_time=curr_date, deadline_in=new_deadline_in
+ )
+ project.project_statuses.append(new_status_row)
+
+ project.busy = False # return to not busy
+ db.session.commit()
+
+ except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.SQLAlchemyError) as err:
+ flask.current_app.logger.exception(err)
+ db.session.rollback()
+ raise DatabaseError(
+ message=str(err),
+ alt_message=(
+ "Status was not updated"
+ + (
+ ": Database malfunction."
+ if isinstance(err, sqlalchemy.exc.OperationalError)
+ else ": Server Error."
+ )
+ ),
+ ) from err
+
+ return_message = f"{project.public_id} has been given a new deadline"
+
+ return_message += (
+ f". An e-mail notification has{' not ' if not send_email else ' '}been sent."
+ )
+
+ except:
+ self.set_busy(project=project, busy=False)
+ raise
+
+ return {"message": return_message}
+
@staticmethod
@dbsession
def set_busy(project: models.Project, busy: bool) -> None:
From ef340d2d10c2944282a0a09ea591abcf4dff694b Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 9 Oct 2023 16:24:26 +0200
Subject: [PATCH 091/155] started tests
---
dds_web/api/project.py | 14 +++++++++---
tests/api/test_project.py | 47 +++++++++++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+), 3 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 6f0cf572b..015a3d8a5 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -225,13 +225,19 @@ def patch(self):
raise DDSArgumentError("Can only extend deadline if the project is available")
new_deadline_in = json_input.get("new_deadline_in")
+ current_deadline = (project.current_deadline - curr_date).days
+
if not new_deadline_in:
raise DDSArgumentError(
message="No new deadline provived, cannot perform operation."
)
- if new_deadline_in > 90:
+ if current_deadline > 10:
+ raise DDSArgumentError(
+ message=f"There are still {current_deadline} days left, it is not possible to extend deadline yet"
+ )
+ if new_deadline_in + current_deadline > 90:
raise DDSArgumentError(
- message="The deadline needs to be less than (or equal to) 90 days."
+ message="The new deadline needs to be less than (or equal to) 90 days."
)
if project.times_expired > 2:
@@ -250,7 +256,9 @@ def patch(self):
db.session.commit()
new_status_row = self.release_project(
- project=project, current_time=curr_date, deadline_in=new_deadline_in
+ project=project,
+ current_time=curr_date,
+ deadline_in=new_deadline_in + current_deadline,
)
project.project_statuses.append(new_status_row)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 867da5ef3..bff12417d 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1079,6 +1079,53 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session
assert "The deadline needs to be less than (or equal to) 30 days." in response.json["message"]
+def test_extend_deadline(module_client, boto3_session):
+ """Extend a project deadline of a project already release"""
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
+ project = project_row(project_id=project_id)
+
+ # Release project with a small deadline
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"new_status": "Available", "deadline": 5},
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ # hasnt been expired or extended deadline yet
+ assert project.times_expired == 0
+
+ deadline = project.current_deadline
+
+ time.sleep(1) # tests are too fast
+
+ # extend deadline
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"extend_deadline": True, "new_deadline_in": 20},
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ assert project.times_expired == 1
+ assert not project.current_deadline == deadline
+
+ assert f"{project_id} has been given a new deadline" in response.json["message"]
+ assert "An e-mail notification has not been sent." in response.json["message"]
+
+
def test_projectstatus_post_deletion_and_archivation_errors(module_client, boto3_session):
"""Mock the different expections that can occur when deleting project."""
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
From ffa340bc6ea95889b34413a0fbbf72ced38d7d51 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 10 Oct 2023 11:49:05 +0200
Subject: [PATCH 092/155] status code
---
dds_web/api/project.py | 20 ++++++++++++++++----
doc/status_codes_api.md | 5 +++++
tests/api/test_project.py | 2 +-
3 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 015a3d8a5..fa6cb0ee8 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -199,6 +199,17 @@ def patch(self):
project = dds_web.utils.collect_project(project_id=project_id)
dds_web.utils.verify_project_access(project=project)
+ # get atributes
+ json_input = flask.request.get_json(silent=True) # Already checked by json_required
+
+ # false by default - operation must be confirmed by the user
+ confirmed_operation = json_input.get("confirmed", False)
+ if not isinstance(confirmed_operation, bool):
+ raise DDSArgumentError(message="`confirmed` is a boolean value: True or False.")
+ if not confirmed_operation:
+ warning_message = "Operation must be confirmed before proceding"
+ return {"warning": warning_message}
+
# Cannot change project status if project is busy
if project.busy:
raise ProjectBusyError(
@@ -211,18 +222,19 @@ def patch(self):
self.set_busy(project=project, busy=True)
try:
- # get atributes
- json_input = flask.request.get_json(silent=True) # Already checked by json_required
extend_deadline = json_input.get("extend_deadline", False) # False by default
# some variable definition
curr_date = dds_web.utils.current_time()
send_email = False
+ # Update the deadline
if extend_deadline:
# deadline can only be extended from Available
if not project.current_status == "Available":
- raise DDSArgumentError("Can only extend deadline if the project is available")
+ raise DDSArgumentError(
+ "you can only extend the deadline for a project that has the status Available"
+ )
new_deadline_in = json_input.get("new_deadline_in")
current_deadline = (project.current_deadline - curr_date).days
@@ -242,7 +254,7 @@ def patch(self):
if project.times_expired > 2:
raise DDSArgumentError(
- "Project availability limit: Project cannot have an extended deadline any more times"
+ "The project has already been available for download 3 times. cannot extend the deadline again"
)
try:
diff --git a/doc/status_codes_api.md b/doc/status_codes_api.md
index 2bd2bc1a1..58aa04f35 100644
--- a/doc/status_codes_api.md
+++ b/doc/status_codes_api.md
@@ -237,6 +237,11 @@
- `archive_project`
- Database or S3 issues
+#### `patch`
+
+- [Authentication errors](#authentication)
+
+
### GetPublic
- [Authentication errors](#authentication)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index bff12417d..53a5a09ad 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1116,7 +1116,7 @@ def test_extend_deadline(module_client, boto3_session):
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json={"extend_deadline": True, "new_deadline_in": 20},
+ json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True},
)
assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 1
From 096ba14b26f325e49798c9f5faa45b58c65610c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Tue, 10 Oct 2023 11:49:28 +0200
Subject: [PATCH 093/155] Update SPRINTLOG.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
SPRINTLOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index 32341599a..8001f5ca5 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -313,4 +313,4 @@ _Nothing merged in CLI during this sprint_
- Project title displayed along with the internal project ID email sent when a project is released ([#1475](https://github.com/ScilifelabDataCentre/dds_web/pull/1475))
- Use full DDS name in MOTD email subject ([#1477](https://github.com/ScilifelabDataCentre/dds_web/pull/1477))
- Add flag --verify-checksum to the comand in email template ([#1478])(https://github.com/ScilifelabDataCentre/dds_web/pull/1478)
-- Improved on email layout when project release ([#1479])(https://github.com/ScilifelabDataCentre/dds_web/pull/1479)
+- Improved email layout; Highlighted information and commands when project is released ([#1479])(https://github.com/ScilifelabDataCentre/dds_web/pull/1479)
From 44bdf81db10b5d53cf48be10bb28dcd28a9a74e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Tue, 10 Oct 2023 11:49:46 +0200
Subject: [PATCH 094/155] Update dds_web/templates/mail/project_release.html
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/templates/mail/project_release.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/templates/mail/project_release.html b/dds_web/templates/mail/project_release.html
index c5ed54bf3..1c3e50346 100644
--- a/dds_web/templates/mail/project_release.html
+++ b/dds_web/templates/mail/project_release.html
@@ -31,7 +31,7 @@
{% if unit_email %}
- if you experience issues, please contact the SciLifeLab unit {{displayed_sender}} at ({{unit_email}}).
+ if you experience issues, please contact the SciLifeLab unit {{displayed_sender}} at {{unit_email}}.
{% else %}
if you experience issues, please contact the SciLifeLab unit {{displayed_sender}}.
{% endif %}
From 492b790afdc0815f7ae974f3f46a2c72c8de0f66 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 10 Oct 2023 12:04:27 +0200
Subject: [PATCH 095/155] added feedback
---
dds_web/templates/mail/mail_base.html | 2 +-
dds_web/templates/mail/project_release.html | 7 ++++---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/dds_web/templates/mail/mail_base.html b/dds_web/templates/mail/mail_base.html
index 5fbafcd8a..a9531bcb7 100644
--- a/dds_web/templates/mail/mail_base.html
+++ b/dds_web/templates/mail/mail_base.html
@@ -139,7 +139,7 @@
border-color: #a00202 !important;
}
}
- mark {
+ code {
background-color: pink;
color: black;
}
diff --git a/dds_web/templates/mail/project_release.html b/dds_web/templates/mail/project_release.html
index 1c3e50346..da99d68e9 100644
--- a/dds_web/templates/mail/project_release.html
+++ b/dds_web/templates/mail/project_release.html
@@ -22,10 +22,11 @@
To list the files in this project, run:
- dds ls -p {{project_id}}.
+ dds ls -p {{project_id}}.
+
To download all the files in this project to your current directory, run:
- dds data get -p {{project_id}} -a --verify-checksum.
+ dds data get -p {{project_id}} -a --verify-checksum.
- Your access to this project will expire on: {{deadline}}
+ Your access to this project will expire on: {{deadline}}
What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way.
From 700f8c1ca82e73148f1548349ba0b81f28e3d21e Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 10 Oct 2023 12:07:05 +0200
Subject: [PATCH 096/155] feedback
---
dds_web/templates/mail/project_release.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/templates/mail/project_release.txt b/dds_web/templates/mail/project_release.txt
index e8d88dda9..1813881e1 100644
--- a/dds_web/templates/mail/project_release.txt
+++ b/dds_web/templates/mail/project_release.txt
@@ -13,7 +13,7 @@ To download all the files in this project to your current directory, run:
For more information (including an installation guide), see the DDS CLI documentation here: https://scilifelabdatacentre.github.io/dds_cli/
{% if unit_email %}
-if you experience issues, please contact the SciLifeLab unit {{displayed_sender}}at ({{unit_email}}).
+if you experience issues, please contact the SciLifeLab unit {{displayed_sender}} at ({{unit_email}}).
{% else %}
if you experience issues, please contact the SciLifeLab unit {{displayed_sender}}.
{% endif %}
From 5df28d6d0400f4e2c77192466942d70c0b3436d5 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 10 Oct 2023 14:05:59 +0200
Subject: [PATCH 097/155] finalized tests
---
dds_web/api/project.py | 10 +-
doc/status_codes_api.md | 22 ++-
tests/api/test_project.py | 328 ++++++++++++++++++++++++++++++++++++--
3 files changed, 339 insertions(+), 21 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index fa6cb0ee8..a0e24e17f 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -207,14 +207,14 @@ def patch(self):
if not isinstance(confirmed_operation, bool):
raise DDSArgumentError(message="`confirmed` is a boolean value: True or False.")
if not confirmed_operation:
- warning_message = "Operation must be confirmed before proceding"
+ warning_message = "Operation must be confirmed before proceding."
return {"warning": warning_message}
# Cannot change project status if project is busy
if project.busy:
raise ProjectBusyError(
message=(
- f"The status for the project '{project_id}' is already in the process of being changed. "
+ f"The deadline for the project '{project_id}' is already in the process of being changed. "
"Please try again later. \n\nIf you know the project is not busy, contact support."
)
)
@@ -233,7 +233,7 @@ def patch(self):
# deadline can only be extended from Available
if not project.current_status == "Available":
raise DDSArgumentError(
- "you can only extend the deadline for a project that has the status Available"
+ "you can only extend the deadline for a project that has the status Available."
)
new_deadline_in = json_input.get("new_deadline_in")
@@ -245,7 +245,7 @@ def patch(self):
)
if current_deadline > 10:
raise DDSArgumentError(
- message=f"There are still {current_deadline} days left, it is not possible to extend deadline yet"
+ message=f"There are still {current_deadline} days left, it is not possible to extend deadline yet."
)
if new_deadline_in + current_deadline > 90:
raise DDSArgumentError(
@@ -254,7 +254,7 @@ def patch(self):
if project.times_expired > 2:
raise DDSArgumentError(
- "The project has already been available for download 3 times. cannot extend the deadline again"
+ "Project availability limit: Project cannot be made Available any more times."
)
try:
diff --git a/doc/status_codes_api.md b/doc/status_codes_api.md
index 58aa04f35..84fb085c0 100644
--- a/doc/status_codes_api.md
+++ b/doc/status_codes_api.md
@@ -240,7 +240,27 @@
#### `patch`
- [Authentication errors](#authentication)
-
+- `400 Bad Request`
+ - Decorators
+ - Json required but not provided
+ - Validation error
+ - Schemas
+ - Project does not exist
+ - Project is busy
+ - `extend_deadline`
+ - Invalid deadline
+ - No deadline provided
+ - Project is not in Available state
+ - Max number of times available reached
+- `403 Forbidden`
+ - Schemas
+ - User does not have access to project
+- `500 Internal Server Error`
+ - Database errors
+ - `delete_project`
+ - Database or S3 issues
+ - `archive_project`
+ - Database or S3 issues
### GetPublic
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 53a5a09ad..1eeb787f9 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -21,6 +21,7 @@
import tests
from tests.test_files_new import project_row, file_in_db, FIRST_NEW_FILE
from tests.test_project_creation import proj_data_with_existing_users, create_unit_admins
+from tests.api.test_user import get_existing_projects
from dds_web.database import models
from dds_web.api.project import UserProjects
@@ -44,6 +45,15 @@
# "date_updated",
]
+release_project_small_deadline = {"new_status": "Available", "deadline": 5}
+
+extend_deadline_data_no_confirmed = {
+ "extend_deadline": True,
+ "new_deadline_in": 20,
+}
+
+extend_deadline_data = {**extend_deadline_data_no_confirmed, "confirmed": True}
+
@pytest.fixture(scope="module")
def test_project(module_client):
@@ -1079,36 +1089,290 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session
assert "The deadline needs to be less than (or equal to) 30 days." in response.json["message"]
-def test_extend_deadline(module_client, boto3_session):
- """Extend a project deadline of a project already release"""
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- if current_unit_admins < 3:
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins >= 3
+def test_extend_deadline_bad_confirmed(module_client, boto3_session):
+ """Try to extend a deadline and send a not boolean for confirmation"""
+ username = "researchuser"
+ # Get user
+ user = models.User.query.filter_by(username=username).one_or_none()
+ assert user
+
+ # Get project
+ project = user.projects[0]
+ assert project
+ project_id = project.public_id
+
+ # Release project
response = module_client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
- json=proj_data,
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"new_status": "Available"},
)
assert response.status_code == http.HTTPStatus.OK
- project_id = response.json.get("project_id")
- project = project_row(project_id=project_id)
+ # hasnt been expired or extended deadline yet
+ assert project.times_expired == 0
+
+ json_data = {**extend_deadline_data, "confirmed": "true"}
+ # extend deadline
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json=json_data,
+ )
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ assert "`confirmed` is a boolean value: True or False." in response.json["message"]
+
+
+def test_extend_deadline_no_confirmed(module_client, boto3_session):
+ """Try to extend a deadline before confirmation should sent a warning and no operation perfrom"""
+
+ project, _ = get_existing_projects()
+ project_id = project.public_id
+
+ # Release project
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"new_status": "Available"},
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ # hasnt been expired or extended deadline yet
+ assert project.times_expired == 0
+
+ # extend deadline
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json=extend_deadline_data_no_confirmed,
+ )
+ # status code is ok but no operation perfrom
+ assert response.status_code == http.HTTPStatus.OK
+ assert project.times_expired == 0
+
+ assert "Operation must be confirmed before proceding." in response.json["warning"]
+
+
+def test_extend_deadline_when_busy(module_client, boto3_session):
+ """Request should not be possible when project is busy."""
+
+ project, _ = get_existing_projects()
+ project_id = project.public_id
+
+ # Release project
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"new_status": "Available"},
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ # hasnt been expired or extended deadline yet
+ assert project.times_expired == 0
+
+ # set to busy
+ project.busy = True
+ db.session.commit()
+ assert project.busy
+
+ time.sleep(1) # tests are too fast
+
+ # attempt to extend deadline
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json=extend_deadline_data,
+ )
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+
+ assert (
+ f"The deadline for the project '{project_id}' is already in the process of being changed. "
+ in response.json["message"]
+ )
+ assert (
+ "Please try again later. \n\nIf you know the project is not busy, contact support."
+ in response.json["message"]
+ )
+
+
+def test_extend_deadline_project_not_available(module_client, boto3_session):
+ """Is not possible to extend deadline to project in other status than available."""
+
+ project, _ = get_existing_projects()
+ project_id = project.public_id
+
+ # attempt to extend deadline - project is in progress
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json=extend_deadline_data,
+ )
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+
+ assert (
+ "you can only extend the deadline for a project that has the status Available."
+ in response.json["message"]
+ )
+
+
+def test_extend_deadline_no_deadline(module_client, boto3_session):
+ """If no deadline has been provided it should fail"""
+
+ project, _ = get_existing_projects()
+ project_id = project.public_id
+
+ # Release project
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"new_status": "Available"},
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ # hasnt been expired or extended deadline yet
+ assert project.times_expired == 0
+
+ # try to extend deadline
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"extend_deadline": True, "confirmed": True},
+ )
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ assert "No new deadline provived, cannot perform operation." in response.json["message"]
+
+
+def test_extend_deadline_not_enough_time_left(module_client, boto3_session):
+ """If there are more than 10 days left, extend deadline should not be possible"""
+
+ project, _ = get_existing_projects()
+ project_id = project.public_id
+
+ # Release project with a big deadline
+ current_deadline = 30
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"new_status": "Available", "deadline": current_deadline},
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ # hasnt been expired or extended deadline yet
+ assert project.times_expired == 0
+
+ # try to extend upon such deadline
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True},
+ )
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ assert (
+ f"There are still {current_deadline} days left, it is not possible to extend deadline yet."
+ in response.json["message"]
+ )
+
+
+def test_extend_deadline_too_much_days(module_client, boto3_session):
+ """If the new deadline together with the time left already is more than 90 days it should not work"""
+
+ project, _ = get_existing_projects()
+ project_id = project.public_id
+
+ # Release project with a small deadline
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json=release_project_small_deadline,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ # hasnt been expired or extended deadline yet
+ assert project.times_expired == 0
+
+ # try to extend deadline a lot of days
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"extend_deadline": True, "new_deadline_in": 90, "confirmed": True},
+ )
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ assert (
+ "The new deadline needs to be less than (or equal to) 90 days." in response.json["message"]
+ )
+
+
+def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session):
+ """If the deadline has been extended more than 2 times it should not work"""
+
+ project, _ = get_existing_projects()
+ project_id = project.public_id
# Release project with a small deadline
response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json={"new_status": "Available", "deadline": 5},
+ json=release_project_small_deadline,
)
assert response.status_code == http.HTTPStatus.OK
# hasnt been expired or extended deadline yet
assert project.times_expired == 0
deadline = project.current_deadline
+ new_deadline_in = 1
+
+ for i in range(1, 4):
+ time.sleep(1) # tests are too fast
+
+ # extend deadline with a small new deadline so we can do it several times
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={**extend_deadline_data, "new_deadline_in": new_deadline_in},
+ )
+ if i < 3:
+ assert response.status_code == http.HTTPStatus.OK
+ assert project.times_expired == i
+ assert project.current_deadline == deadline + datetime.timedelta(days=new_deadline_in)
+ deadline = project.current_deadline
+ assert f"{project_id} has been given a new deadline" in response.json["message"]
+ assert "An e-mail notification has not been sent." in response.json["message"]
+ else:
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ assert (
+ "Project availability limit: Project cannot be made Available any more times"
+ in response.json["message"]
+ )
+
+
+def test_extend_deadline_ok(module_client, boto3_session):
+ """Extend a project deadline of a project already release"""
+
+ project, _ = get_existing_projects()
+ project_id = project.public_id
+
+ # Release project with a small deadline
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json=release_project_small_deadline,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ # hasnt been expired or extended deadline yet
+ assert project.times_expired == 0
+ deadline = project.current_deadline
time.sleep(1) # tests are too fast
# extend deadline
@@ -1116,16 +1380,50 @@ def test_extend_deadline(module_client, boto3_session):
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True},
+ json=extend_deadline_data,
)
assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 1
- assert not project.current_deadline == deadline
+ assert project.current_deadline == deadline + datetime.timedelta(
+ days=extend_deadline_data.get("new_deadline_in")
+ )
assert f"{project_id} has been given a new deadline" in response.json["message"]
assert "An e-mail notification has not been sent." in response.json["message"]
+def test_extend_deadline_mock_database_error(module_client, boto3_session):
+ """Mock error when updating the database"""
+
+ project, _ = get_existing_projects()
+ project_id = project.public_id
+
+ # Release project with a small deadline
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json=release_project_small_deadline,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ # hasnt been expired or extended deadline yet
+ assert project.times_expired == 0
+
+ time.sleep(1) # tests are too fast
+
+ token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client)
+ with unittest.mock.patch("dds_web.db.session.commit", mock_sqlalchemyerror):
+ # extend deadline
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=token,
+ query_string={"project": project_id},
+ json=extend_deadline_data,
+ )
+ assert response.status_code == http.HTTPStatus.INTERNAL_SERVER_ERROR
+ assert "Saving database changes failed." in response.json["message"]
+
+
def test_projectstatus_post_deletion_and_archivation_errors(module_client, boto3_session):
"""Mock the different expections that can occur when deleting project."""
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
From a4c0885d1981f0b68e8e03aede049c0d0555fd02 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Tue, 10 Oct 2023 14:07:11 +0200
Subject: [PATCH 098/155] Update dds_web/templates/mail/project_release.html
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/templates/mail/project_release.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/templates/mail/project_release.html b/dds_web/templates/mail/project_release.html
index da99d68e9..588b4732b 100644
--- a/dds_web/templates/mail/project_release.html
+++ b/dds_web/templates/mail/project_release.html
@@ -18,7 +18,7 @@
- You were added to this project on behalf of {{displayed_sender}}.
+ You were added to this project {% if unit_email %} on behalf of {% else %} by {% endif %} {{displayed_sender}}.
To list the files in this project, run:
From 138fd01857faace723ca9bb6831ca0ac2736547f Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 10 Oct 2023 14:13:25 +0200
Subject: [PATCH 099/155] feedback
---
dds_web/templates/mail/mail_base.html | 2 ++
dds_web/templates/mail/project_release.html | 4 ++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/dds_web/templates/mail/mail_base.html b/dds_web/templates/mail/mail_base.html
index a9531bcb7..816e6fbf3 100644
--- a/dds_web/templates/mail/mail_base.html
+++ b/dds_web/templates/mail/mail_base.html
@@ -142,6 +142,8 @@
code {
background-color: pink;
color: black;
+ font-weight: normal;
+ font-style: normal;
}
diff --git a/dds_web/templates/mail/project_release.html b/dds_web/templates/mail/project_release.html
index 588b4732b..25dbfe448 100644
--- a/dds_web/templates/mail/project_release.html
+++ b/dds_web/templates/mail/project_release.html
@@ -22,11 +22,11 @@
To list the files in this project, run:
- dds ls -p {{project_id}}.
+ dds ls -p {{project_id}}
To download all the files in this project to your current directory, run:
- dds data get -p {{project_id}} -a --verify-checksum.
+ dds data get -p {{project_id}} -a --verify-checksum
From fc6efa41dca224e9fe249cf229dbfc47b9233415 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 10 Oct 2023 14:23:40 +0200
Subject: [PATCH 102/155] feedback
---
dds_web/templates/mail/mail_base.html | 2 --
1 file changed, 2 deletions(-)
diff --git a/dds_web/templates/mail/mail_base.html b/dds_web/templates/mail/mail_base.html
index 816e6fbf3..a9531bcb7 100644
--- a/dds_web/templates/mail/mail_base.html
+++ b/dds_web/templates/mail/mail_base.html
@@ -142,8 +142,6 @@
code {
background-color: pink;
color: black;
- font-weight: normal;
- font-style: normal;
}
From 294d9b7e7097d178c514a673253bf069ac6d8ca2 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 10 Oct 2023 14:26:45 +0200
Subject: [PATCH 103/155] fix test
---
tests/api/test_project.py | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 1eeb787f9..7e140af7b 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1092,14 +1092,7 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session
def test_extend_deadline_bad_confirmed(module_client, boto3_session):
"""Try to extend a deadline and send a not boolean for confirmation"""
- username = "researchuser"
- # Get user
- user = models.User.query.filter_by(username=username).one_or_none()
- assert user
-
- # Get project
- project = user.projects[0]
- assert project
+ project, _ = get_existing_projects()
project_id = project.public_id
# Release project
@@ -1113,13 +1106,12 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session):
# hasnt been expired or extended deadline yet
assert project.times_expired == 0
- json_data = {**extend_deadline_data, "confirmed": "true"}
# extend deadline
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json=json_data,
+ json={**extend_deadline_data, "confirmed": "true"},
)
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert "`confirmed` is a boolean value: True or False." in response.json["message"]
From 739bbb483777c26c6d7e5a0fb2fa995f375bb758 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Tue, 10 Oct 2023 14:34:15 +0200
Subject: [PATCH 104/155] Update dds_web/templates/mail/project_release.html
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/templates/mail/project_release.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/templates/mail/project_release.html b/dds_web/templates/mail/project_release.html
index 307be8dd7..701ad8b21 100644
--- a/dds_web/templates/mail/project_release.html
+++ b/dds_web/templates/mail/project_release.html
@@ -32,7 +32,7 @@
{% if unit_email %}
- if you experience issues, please contact the SciLifeLab unit {{displayed_sender}} at {{unit_email}}.
+ If you experience issues, please contact the SciLifeLab unit {{displayed_sender}} at {{unit_email}}.
{% else %}
if you experience issues, please contact the SciLifeLab unit {{displayed_sender}}.
{% endif %}
From 3db8026bd734eff0ecbd5f7ebb5b8d59eadbc2f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Tue, 10 Oct 2023 14:34:25 +0200
Subject: [PATCH 105/155] Update dds_web/templates/mail/project_release.html
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/templates/mail/project_release.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/templates/mail/project_release.html b/dds_web/templates/mail/project_release.html
index 701ad8b21..06d536664 100644
--- a/dds_web/templates/mail/project_release.html
+++ b/dds_web/templates/mail/project_release.html
@@ -34,7 +34,7 @@
{% if unit_email %}
If you experience issues, please contact the SciLifeLab unit {{displayed_sender}} at {{unit_email}}.
{% else %}
- if you experience issues, please contact the SciLifeLab unit {{displayed_sender}}.
+ If you experience issues, please contact the SciLifeLab unit {{displayed_sender}}.
{% endif %}
From ca1fd4263504d8b1a4da38d79a6cdaaeccf0eacc Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 10 Oct 2023 14:36:48 +0200
Subject: [PATCH 106/155] updated txt
---
dds_web/templates/mail/project_release.txt | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/dds_web/templates/mail/project_release.txt b/dds_web/templates/mail/project_release.txt
index 1813881e1..9b6dd9220 100644
--- a/dds_web/templates/mail/project_release.txt
+++ b/dds_web/templates/mail/project_release.txt
@@ -2,7 +2,7 @@ The following project is now available for your access in the SciLifeLab Data De
- Project Title: {{project_title}}
- DDS project ID: {{project_id}}
-You were added to this project on behalf of {{displayed_sender}}.
+You were added to this project {% if unit_email %} on behalf of {% else %} by {% endif %} {{displayed_sender}}.
To list the files in this project, run:
dds ls -p {{project_id}}
@@ -12,11 +12,11 @@ To download all the files in this project to your current directory, run:
For more information (including an installation guide), see the DDS CLI documentation here: https://scilifelabdatacentre.github.io/dds_cli/
-{% if unit_email %}
-if you experience issues, please contact the SciLifeLab unit {{displayed_sender}} at ({{unit_email}}).
-{% else %}
-if you experience issues, please contact the SciLifeLab unit {{displayed_sender}}.
-{% endif %}
+{% if unit_email %}
+If you experience issues, please contact the SciLifeLab unit {{displayed_sender}} at {{unit_email}}.
+{% else %}
+If you experience issues, please contact the SciLifeLab unit {{displayed_sender}}.
+{% endif %}
Your access to this project will expire on {{deadline}}
From fdca870051595692ffb740af97fb50e01a23cf5f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Tue, 10 Oct 2023 15:00:33 +0200
Subject: [PATCH 107/155] Update test_project.py
---
tests/api/test_project.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 89d5e1c2a..7ac6ce020 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1426,7 +1426,7 @@ def test_email_project_release(module_client, boto3_session):
assert f"- DDS project ID: {public_project_id}" in body
assert f"dds ls -p {public_project_id}" in body
assert f"dds data get -p {public_project_id} -a --verify-checksum" in body
- assert "if you experience issues, please contact the SciLifeLab unit" in body
+ assert "If you experience issues, please contact the SciLifeLab unit" in body
assert (
"What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way"
in body
@@ -1437,7 +1437,7 @@ def test_email_project_release(module_client, boto3_session):
assert f"
DDS project ID: {public_project_id}
" in html
assert f"dds ls -p {public_project_id}" in html
assert f"dds data get -p {public_project_id} -a --verify-checksum" in html
- assert "if you experience issues, please contact the SciLifeLab unit" in html
+ assert "If you experience issues, please contact the SciLifeLab unit" in html
assert (
"What is the DDS? The DDS is a system for SciLifeLab infrastructures to deliver data to researchers in a fast, secure and simple way."
in html
From 9c3805a1c7fc1bee6ecf0615972f1c5b5f8bca5c Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 10 Oct 2023 15:23:09 +0200
Subject: [PATCH 108/155] change fixture
---
tests/api/test_project.py | 104 +++++++++++++++++++++-----------------
1 file changed, 57 insertions(+), 47 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 7e140af7b..a98892931 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1089,16 +1089,16 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session
assert "The deadline needs to be less than (or equal to) 30 days." in response.json["message"]
-def test_extend_deadline_bad_confirmed(module_client, boto3_session):
+def test_extend_deadline_bad_confirmed(client, boto3_session):
"""Try to extend a deadline and send a not boolean for confirmation"""
project, _ = get_existing_projects()
project_id = project.public_id
# Release project
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json={"new_status": "Available"},
)
@@ -1106,10 +1106,12 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session):
# hasnt been expired or extended deadline yet
assert project.times_expired == 0
+ time.sleep(1) # tests are too fast
+
# extend deadline
- response = module_client.patch(
+ response = client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json={**extend_deadline_data, "confirmed": "true"},
)
@@ -1117,16 +1119,16 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session):
assert "`confirmed` is a boolean value: True or False." in response.json["message"]
-def test_extend_deadline_no_confirmed(module_client, boto3_session):
+def test_extend_deadline_no_confirmed(client, boto3_session):
"""Try to extend a deadline before confirmation should sent a warning and no operation perfrom"""
project, _ = get_existing_projects()
project_id = project.public_id
# Release project
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json={"new_status": "Available"},
)
@@ -1134,10 +1136,12 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session):
# hasnt been expired or extended deadline yet
assert project.times_expired == 0
+ time.sleep(1) # tests are too fast
+
# extend deadline
- response = module_client.patch(
+ response = client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json=extend_deadline_data_no_confirmed,
)
@@ -1148,16 +1152,16 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session):
assert "Operation must be confirmed before proceding." in response.json["warning"]
-def test_extend_deadline_when_busy(module_client, boto3_session):
+def test_extend_deadline_when_busy(client, boto3_session):
"""Request should not be possible when project is busy."""
project, _ = get_existing_projects()
project_id = project.public_id
# Release project
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json={"new_status": "Available"},
)
@@ -1173,9 +1177,9 @@ def test_extend_deadline_when_busy(module_client, boto3_session):
time.sleep(1) # tests are too fast
# attempt to extend deadline
- response = module_client.patch(
+ response = client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json=extend_deadline_data,
)
@@ -1191,16 +1195,16 @@ def test_extend_deadline_when_busy(module_client, boto3_session):
)
-def test_extend_deadline_project_not_available(module_client, boto3_session):
+def test_extend_deadline_project_not_available(client, boto3_session):
"""Is not possible to extend deadline to project in other status than available."""
project, _ = get_existing_projects()
project_id = project.public_id
# attempt to extend deadline - project is in progress
- response = module_client.patch(
+ response = client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json=extend_deadline_data,
)
@@ -1212,16 +1216,16 @@ def test_extend_deadline_project_not_available(module_client, boto3_session):
)
-def test_extend_deadline_no_deadline(module_client, boto3_session):
+def test_extend_deadline_no_deadline(client, boto3_session):
"""If no deadline has been provided it should fail"""
project, _ = get_existing_projects()
project_id = project.public_id
# Release project
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json={"new_status": "Available"},
)
@@ -1229,10 +1233,12 @@ def test_extend_deadline_no_deadline(module_client, boto3_session):
# hasnt been expired or extended deadline yet
assert project.times_expired == 0
+ time.sleep(1) # tests are too fast
+
# try to extend deadline
- response = module_client.patch(
+ response = client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json={"extend_deadline": True, "confirmed": True},
)
@@ -1240,7 +1246,7 @@ def test_extend_deadline_no_deadline(module_client, boto3_session):
assert "No new deadline provived, cannot perform operation." in response.json["message"]
-def test_extend_deadline_not_enough_time_left(module_client, boto3_session):
+def test_extend_deadline_not_enough_time_left(client, boto3_session):
"""If there are more than 10 days left, extend deadline should not be possible"""
project, _ = get_existing_projects()
@@ -1248,9 +1254,9 @@ def test_extend_deadline_not_enough_time_left(module_client, boto3_session):
# Release project with a big deadline
current_deadline = 30
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json={"new_status": "Available", "deadline": current_deadline},
)
@@ -1258,10 +1264,12 @@ def test_extend_deadline_not_enough_time_left(module_client, boto3_session):
# hasnt been expired or extended deadline yet
assert project.times_expired == 0
+ time.sleep(1) # tests are too fast
+
# try to extend upon such deadline
- response = module_client.patch(
+ response = client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True},
)
@@ -1272,16 +1280,16 @@ def test_extend_deadline_not_enough_time_left(module_client, boto3_session):
)
-def test_extend_deadline_too_much_days(module_client, boto3_session):
+def test_extend_deadline_too_much_days(client, boto3_session):
"""If the new deadline together with the time left already is more than 90 days it should not work"""
project, _ = get_existing_projects()
project_id = project.public_id
# Release project with a small deadline
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json=release_project_small_deadline,
)
@@ -1289,8 +1297,10 @@ def test_extend_deadline_too_much_days(module_client, boto3_session):
# hasnt been expired or extended deadline yet
assert project.times_expired == 0
+ time.sleep(1) # tests are too fast
+
# try to extend deadline a lot of days
- response = module_client.patch(
+ response = client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
@@ -1302,16 +1312,16 @@ def test_extend_deadline_too_much_days(module_client, boto3_session):
)
-def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session):
+def test_extend_deadline_maxium_number_available_exceded(client, boto3_session):
"""If the deadline has been extended more than 2 times it should not work"""
project, _ = get_existing_projects()
project_id = project.public_id
# Release project with a small deadline
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json=release_project_small_deadline,
)
@@ -1326,9 +1336,9 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se
time.sleep(1) # tests are too fast
# extend deadline with a small new deadline so we can do it several times
- response = module_client.patch(
+ response = client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json={**extend_deadline_data, "new_deadline_in": new_deadline_in},
)
@@ -1347,16 +1357,16 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se
)
-def test_extend_deadline_ok(module_client, boto3_session):
+def test_extend_deadline_ok(client, boto3_session):
"""Extend a project deadline of a project already release"""
project, _ = get_existing_projects()
project_id = project.public_id
# Release project with a small deadline
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json=release_project_small_deadline,
)
@@ -1368,9 +1378,9 @@ def test_extend_deadline_ok(module_client, boto3_session):
time.sleep(1) # tests are too fast
# extend deadline
- response = module_client.patch(
+ response = client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json=extend_deadline_data,
)
@@ -1384,16 +1394,16 @@ def test_extend_deadline_ok(module_client, boto3_session):
assert "An e-mail notification has not been sent." in response.json["message"]
-def test_extend_deadline_mock_database_error(module_client, boto3_session):
+def test_extend_deadline_mock_database_error(client, boto3_session):
"""Mock error when updating the database"""
project, _ = get_existing_projects()
project_id = project.public_id
# Release project with a small deadline
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json=release_project_small_deadline,
)
@@ -1403,10 +1413,10 @@ def test_extend_deadline_mock_database_error(module_client, boto3_session):
time.sleep(1) # tests are too fast
- token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client)
+ token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client)
with unittest.mock.patch("dds_web.db.session.commit", mock_sqlalchemyerror):
# extend deadline
- response = module_client.patch(
+ response = client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=token,
query_string={"project": project_id},
From 996cc0813b73d8844b5bc52f34fa46471b400909 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 10 Oct 2023 16:19:31 +0200
Subject: [PATCH 109/155] go back to first approach of test
---
tests/api/test_project.py | 324 +++++++++++++++++++++++++++-----------
1 file changed, 233 insertions(+), 91 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index a98892931..076a78abe 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -55,6 +55,39 @@
extend_deadline_data = {**extend_deadline_data_no_confirmed, "confirmed": True}
+# HELPER FUNCTIONS ################################################################################## CONFIG #
+
+
+def create_and_release_project(module_client, proj_data, release_data):
+ """Helper function that creates a project and set it ups as available"""
+
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
+ project = project_row(project_id=project_id)
+
+ # Release project
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json=release_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+
+ return project_id, project
+
+
@pytest.fixture(scope="module")
def test_project(module_client):
"""Create a shared test project"""
@@ -1089,29 +1122,40 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session
assert "The deadline needs to be less than (or equal to) 30 days." in response.json["message"]
-def test_extend_deadline_bad_confirmed(client, boto3_session):
+def test_extend_deadline_bad_confirmed(module_client, boto3_session):
"""Try to extend a deadline and send a not boolean for confirmation"""
- project, _ = get_existing_projects()
- project_id = project.public_id
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
+ project = project_row(project_id=project_id)
# Release project
- response = client.post(
+ release_data = {"new_status": "Available"}
+ response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json={"new_status": "Available"},
+ json=release_data,
)
assert response.status_code == http.HTTPStatus.OK
- # hasnt been expired or extended deadline yet
assert project.times_expired == 0
-
time.sleep(1) # tests are too fast
# extend deadline
- response = client.patch(
+ response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json={**extend_deadline_data, "confirmed": "true"},
)
@@ -1119,29 +1163,40 @@ def test_extend_deadline_bad_confirmed(client, boto3_session):
assert "`confirmed` is a boolean value: True or False." in response.json["message"]
-def test_extend_deadline_no_confirmed(client, boto3_session):
+def test_extend_deadline_no_confirmed(module_client, boto3_session):
"""Try to extend a deadline before confirmation should sent a warning and no operation perfrom"""
- project, _ = get_existing_projects()
- project_id = project.public_id
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
+ project = project_row(project_id=project_id)
# Release project
- response = client.post(
+ release_data = {"new_status": "Available"}
+ response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json={"new_status": "Available"},
+ json=release_data,
)
assert response.status_code == http.HTTPStatus.OK
- # hasnt been expired or extended deadline yet
assert project.times_expired == 0
-
time.sleep(1) # tests are too fast
# extend deadline
- response = client.patch(
+ response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json=extend_deadline_data_no_confirmed,
)
@@ -1152,22 +1207,35 @@ def test_extend_deadline_no_confirmed(client, boto3_session):
assert "Operation must be confirmed before proceding." in response.json["warning"]
-def test_extend_deadline_when_busy(client, boto3_session):
+def test_extend_deadline_when_busy(module_client, boto3_session):
"""Request should not be possible when project is busy."""
- project, _ = get_existing_projects()
- project_id = project.public_id
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
+ project = project_row(project_id=project_id)
# Release project
- response = client.post(
+ release_data = {"new_status": "Available"}
+ response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json={"new_status": "Available"},
+ json=release_data,
)
assert response.status_code == http.HTTPStatus.OK
- # hasnt been expired or extended deadline yet
assert project.times_expired == 0
+ time.sleep(1) # tests are too fast
# set to busy
project.busy = True
@@ -1177,9 +1245,9 @@ def test_extend_deadline_when_busy(client, boto3_session):
time.sleep(1) # tests are too fast
# attempt to extend deadline
- response = client.patch(
+ response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json=extend_deadline_data,
)
@@ -1195,16 +1263,27 @@ def test_extend_deadline_when_busy(client, boto3_session):
)
-def test_extend_deadline_project_not_available(client, boto3_session):
+def test_extend_deadline_project_not_available(module_client, boto3_session):
"""Is not possible to extend deadline to project in other status than available."""
- project, _ = get_existing_projects()
- project_id = project.public_id
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
# attempt to extend deadline - project is in progress
- response = client.patch(
+ response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json=extend_deadline_data,
)
@@ -1216,29 +1295,40 @@ def test_extend_deadline_project_not_available(client, boto3_session):
)
-def test_extend_deadline_no_deadline(client, boto3_session):
+def test_extend_deadline_no_deadline(module_client, boto3_session):
"""If no deadline has been provided it should fail"""
- project, _ = get_existing_projects()
- project_id = project.public_id
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
+ project = project_row(project_id=project_id)
# Release project
- response = client.post(
+ release_data = {"new_status": "Available"}
+ response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json={"new_status": "Available"},
+ json=release_data,
)
assert response.status_code == http.HTTPStatus.OK
- # hasnt been expired or extended deadline yet
assert project.times_expired == 0
-
time.sleep(1) # tests are too fast
# try to extend deadline
- response = client.patch(
+ response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json={"extend_deadline": True, "confirmed": True},
)
@@ -1246,30 +1336,41 @@ def test_extend_deadline_no_deadline(client, boto3_session):
assert "No new deadline provived, cannot perform operation." in response.json["message"]
-def test_extend_deadline_not_enough_time_left(client, boto3_session):
+def test_extend_deadline_not_enough_time_left(module_client, boto3_session):
"""If there are more than 10 days left, extend deadline should not be possible"""
- project, _ = get_existing_projects()
- project_id = project.public_id
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
+ project = project_row(project_id=project_id)
- # Release project with a big deadline
+ # Release project
current_deadline = 30
- response = client.post(
+ release_data = {"new_status": "Available", "deadline": current_deadline}
+ response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json={"new_status": "Available", "deadline": current_deadline},
+ json=release_data,
)
assert response.status_code == http.HTTPStatus.OK
- # hasnt been expired or extended deadline yet
assert project.times_expired == 0
-
time.sleep(1) # tests are too fast
# try to extend upon such deadline
- response = client.patch(
+ response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True},
)
@@ -1280,27 +1381,37 @@ def test_extend_deadline_not_enough_time_left(client, boto3_session):
)
-def test_extend_deadline_too_much_days(client, boto3_session):
+def test_extend_deadline_too_much_days(module_client, boto3_session):
"""If the new deadline together with the time left already is more than 90 days it should not work"""
- project, _ = get_existing_projects()
- project_id = project.public_id
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
+ project = project_row(project_id=project_id)
# Release project with a small deadline
- response = client.post(
+ response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json=release_project_small_deadline,
)
assert response.status_code == http.HTTPStatus.OK
- # hasnt been expired or extended deadline yet
assert project.times_expired == 0
-
time.sleep(1) # tests are too fast
# try to extend deadline a lot of days
- response = client.patch(
+ response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
@@ -1312,33 +1423,44 @@ def test_extend_deadline_too_much_days(client, boto3_session):
)
-def test_extend_deadline_maxium_number_available_exceded(client, boto3_session):
+def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session):
"""If the deadline has been extended more than 2 times it should not work"""
- project, _ = get_existing_projects()
- project_id = project.public_id
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
+ project = project_row(project_id=project_id)
# Release project with a small deadline
- response = client.post(
+ response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json=release_project_small_deadline,
)
assert response.status_code == http.HTTPStatus.OK
- # hasnt been expired or extended deadline yet
assert project.times_expired == 0
+ time.sleep(1) # tests are too fast
deadline = project.current_deadline
new_deadline_in = 1
-
for i in range(1, 4):
time.sleep(1) # tests are too fast
# extend deadline with a small new deadline so we can do it several times
- response = client.patch(
+ response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json={**extend_deadline_data, "new_deadline_in": new_deadline_in},
)
@@ -1357,30 +1479,40 @@ def test_extend_deadline_maxium_number_available_exceded(client, boto3_session):
)
-def test_extend_deadline_ok(client, boto3_session):
+def test_extend_deadline_ok(module_client, boto3_session):
"""Extend a project deadline of a project already release"""
- project, _ = get_existing_projects()
- project_id = project.public_id
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
+ project = project_row(project_id=project_id)
# Release project with a small deadline
- response = client.post(
+ response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json=release_project_small_deadline,
)
assert response.status_code == http.HTTPStatus.OK
- # hasnt been expired or extended deadline yet
assert project.times_expired == 0
-
- deadline = project.current_deadline
time.sleep(1) # tests are too fast
+ deadline = project.current_deadline
# extend deadline
- response = client.patch(
+ response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json=extend_deadline_data,
)
@@ -1394,29 +1526,39 @@ def test_extend_deadline_ok(client, boto3_session):
assert "An e-mail notification has not been sent." in response.json["message"]
-def test_extend_deadline_mock_database_error(client, boto3_session):
+def test_extend_deadline_mock_database_error(module_client, boto3_session):
"""Mock error when updating the database"""
- project, _ = get_existing_projects()
- project_id = project.public_id
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ if current_unit_admins < 3:
+ create_unit_admins(num_admins=2)
+ current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
+ assert current_unit_admins >= 3
+
+ response = module_client.post(
+ tests.DDSEndpoint.PROJECT_CREATE,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ json=proj_data,
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ project_id = response.json.get("project_id")
+ project = project_row(project_id=project_id)
# Release project with a small deadline
- response = client.post(
+ response = module_client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json=release_project_small_deadline,
)
assert response.status_code == http.HTTPStatus.OK
- # hasnt been expired or extended deadline yet
assert project.times_expired == 0
-
time.sleep(1) # tests are too fast
- token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client)
+ token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client)
with unittest.mock.patch("dds_web.db.session.commit", mock_sqlalchemyerror):
# extend deadline
- response = client.patch(
+ response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=token,
query_string={"project": project_id},
From c410d464dfcd6cdf4f8a1c7f6f0bc1f252cfd38f Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Wed, 11 Oct 2023 11:01:35 +0200
Subject: [PATCH 110/155] improved
---
tests/api/test_project.py | 242 +++++---------------------------------
1 file changed, 29 insertions(+), 213 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 076a78abe..e2d96efd0 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -21,7 +21,6 @@
import tests
from tests.test_files_new import project_row, file_in_db, FIRST_NEW_FILE
from tests.test_project_creation import proj_data_with_existing_users, create_unit_admins
-from tests.api.test_user import get_existing_projects
from dds_web.database import models
from dds_web.api.project import UserProjects
@@ -58,7 +57,7 @@
# HELPER FUNCTIONS ################################################################################## CONFIG #
-def create_and_release_project(module_client, proj_data, release_data):
+def create_and_release_project(client, proj_data, release_data):
"""Helper function that creates a project and set it ups as available"""
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
@@ -67,9 +66,9 @@ def create_and_release_project(module_client, proj_data, release_data):
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
assert current_unit_admins >= 3
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(client),
json=proj_data,
)
assert response.status_code == http.HTTPStatus.OK
@@ -77,9 +76,9 @@ def create_and_release_project(module_client, proj_data, release_data):
project = project_row(project_id=project_id)
# Release project
- response = module_client.post(
+ response = client.post(
tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(client),
query_string={"project": project_id},
json=release_data,
)
@@ -1125,30 +1124,9 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session
def test_extend_deadline_bad_confirmed(module_client, boto3_session):
"""Try to extend a deadline and send a not boolean for confirmation"""
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- if current_unit_admins < 3:
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins >= 3
-
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
- json=proj_data,
- )
- assert response.status_code == http.HTTPStatus.OK
- project_id = response.json.get("project_id")
- project = project_row(project_id=project_id)
-
- # Release project
- release_data = {"new_status": "Available"}
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
- query_string={"project": project_id},
- json=release_data,
+ project_id, project = create_and_release_project(
+ client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
)
- assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 0
time.sleep(1) # tests are too fast
@@ -1166,30 +1144,9 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session):
def test_extend_deadline_no_confirmed(module_client, boto3_session):
"""Try to extend a deadline before confirmation should sent a warning and no operation perfrom"""
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- if current_unit_admins < 3:
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins >= 3
-
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
- json=proj_data,
+ project_id, project = create_and_release_project(
+ client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
)
- assert response.status_code == http.HTTPStatus.OK
- project_id = response.json.get("project_id")
- project = project_row(project_id=project_id)
-
- # Release project
- release_data = {"new_status": "Available"}
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
- query_string={"project": project_id},
- json=release_data,
- )
- assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 0
time.sleep(1) # tests are too fast
@@ -1210,30 +1167,9 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session):
def test_extend_deadline_when_busy(module_client, boto3_session):
"""Request should not be possible when project is busy."""
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- if current_unit_admins < 3:
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins >= 3
-
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
- json=proj_data,
+ project_id, project = create_and_release_project(
+ client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
)
- assert response.status_code == http.HTTPStatus.OK
- project_id = response.json.get("project_id")
- project = project_row(project_id=project_id)
-
- # Release project
- release_data = {"new_status": "Available"}
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
- query_string={"project": project_id},
- json=release_data,
- )
- assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 0
time.sleep(1) # tests are too fast
@@ -1242,8 +1178,6 @@ def test_extend_deadline_when_busy(module_client, boto3_session):
db.session.commit()
assert project.busy
- time.sleep(1) # tests are too fast
-
# attempt to extend deadline
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
@@ -1266,6 +1200,7 @@ def test_extend_deadline_when_busy(module_client, boto3_session):
def test_extend_deadline_project_not_available(module_client, boto3_session):
"""Is not possible to extend deadline to project in other status than available."""
+ # create a new project and never release it
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
if current_unit_admins < 3:
create_unit_admins(num_admins=2)
@@ -1298,30 +1233,9 @@ def test_extend_deadline_project_not_available(module_client, boto3_session):
def test_extend_deadline_no_deadline(module_client, boto3_session):
"""If no deadline has been provided it should fail"""
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- if current_unit_admins < 3:
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins >= 3
-
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
- json=proj_data,
- )
- assert response.status_code == http.HTTPStatus.OK
- project_id = response.json.get("project_id")
- project = project_row(project_id=project_id)
-
- # Release project
- release_data = {"new_status": "Available"}
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
- query_string={"project": project_id},
- json=release_data,
+ project_id, project = create_and_release_project(
+ client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
)
- assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 0
time.sleep(1) # tests are too fast
@@ -1339,31 +1253,11 @@ def test_extend_deadline_no_deadline(module_client, boto3_session):
def test_extend_deadline_not_enough_time_left(module_client, boto3_session):
"""If there are more than 10 days left, extend deadline should not be possible"""
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- if current_unit_admins < 3:
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins >= 3
-
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
- json=proj_data,
+ current_deadline = 90
+ release_data_long_deadline = {"new_status": "Available", "deadline": current_deadline}
+ project_id, project = create_and_release_project(
+ client=module_client, proj_data=proj_data, release_data=release_data_long_deadline
)
- assert response.status_code == http.HTTPStatus.OK
- project_id = response.json.get("project_id")
- project = project_row(project_id=project_id)
-
- # Release project
- current_deadline = 30
- release_data = {"new_status": "Available", "deadline": current_deadline}
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
- query_string={"project": project_id},
- json=release_data,
- )
- assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 0
time.sleep(1) # tests are too fast
@@ -1384,29 +1278,9 @@ def test_extend_deadline_not_enough_time_left(module_client, boto3_session):
def test_extend_deadline_too_much_days(module_client, boto3_session):
"""If the new deadline together with the time left already is more than 90 days it should not work"""
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- if current_unit_admins < 3:
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins >= 3
-
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
- json=proj_data,
- )
- assert response.status_code == http.HTTPStatus.OK
- project_id = response.json.get("project_id")
- project = project_row(project_id=project_id)
-
- # Release project with a small deadline
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
- query_string={"project": project_id},
- json=release_project_small_deadline,
+ project_id, project = create_and_release_project(
+ client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
)
- assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 0
time.sleep(1) # tests are too fast
@@ -1426,34 +1300,14 @@ def test_extend_deadline_too_much_days(module_client, boto3_session):
def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session):
"""If the deadline has been extended more than 2 times it should not work"""
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- if current_unit_admins < 3:
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins >= 3
-
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
- json=proj_data,
- )
- assert response.status_code == http.HTTPStatus.OK
- project_id = response.json.get("project_id")
- project = project_row(project_id=project_id)
-
- # Release project with a small deadline
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
- query_string={"project": project_id},
- json=release_project_small_deadline,
+ project_id, project = create_and_release_project(
+ client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
)
- assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 0
time.sleep(1) # tests are too fast
deadline = project.current_deadline
- new_deadline_in = 1
+ new_deadline_in = 1 # small new deadline
for i in range(1, 4):
time.sleep(1) # tests are too fast
@@ -1482,33 +1336,15 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se
def test_extend_deadline_ok(module_client, boto3_session):
"""Extend a project deadline of a project already release"""
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- if current_unit_admins < 3:
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins >= 3
-
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
- json=proj_data,
+ # release with a small deadline
+ project_id, project = create_and_release_project(
+ client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
)
- assert response.status_code == http.HTTPStatus.OK
- project_id = response.json.get("project_id")
- project = project_row(project_id=project_id)
-
- # Release project with a small deadline
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
- query_string={"project": project_id},
- json=release_project_small_deadline,
- )
- assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 0
time.sleep(1) # tests are too fast
deadline = project.current_deadline
+
# extend deadline
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
@@ -1529,29 +1365,9 @@ def test_extend_deadline_ok(module_client, boto3_session):
def test_extend_deadline_mock_database_error(module_client, boto3_session):
"""Mock error when updating the database"""
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- if current_unit_admins < 3:
- create_unit_admins(num_admins=2)
- current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
- assert current_unit_admins >= 3
-
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_CREATE,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unituser"]).token(module_client),
- json=proj_data,
- )
- assert response.status_code == http.HTTPStatus.OK
- project_id = response.json.get("project_id")
- project = project_row(project_id=project_id)
-
- # Release project with a small deadline
- response = module_client.post(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
- query_string={"project": project_id},
- json=release_project_small_deadline,
+ project_id, project = create_and_release_project(
+ client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
)
- assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 0
time.sleep(1) # tests are too fast
From 0ad3d5da9e59b09e2af5f08f68137cacd8903365 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Wed, 11 Oct 2023 14:25:55 +0200
Subject: [PATCH 111/155] better comments
---
tests/api/test_project.py | 36 +++++++++++++++++++-----------------
1 file changed, 19 insertions(+), 17 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 5700e16ce..59146f695 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1130,7 +1130,7 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session):
assert project.times_expired == 0
time.sleep(1) # tests are too fast
- # extend deadline
+ # try to extend deadline with a string as confirmed - should fail
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
@@ -1142,7 +1142,7 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session):
def test_extend_deadline_no_confirmed(module_client, boto3_session):
- """Try to extend a deadline before confirmation should sent a warning and no operation perfrom"""
+ """Try to extend a deadline before confirmation - should sent a warning and no operation is perfrom"""
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
@@ -1150,14 +1150,14 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session):
assert project.times_expired == 0
time.sleep(1) # tests are too fast
- # extend deadline
+ # try to extend deadline
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
json=extend_deadline_data_no_confirmed,
)
- # status code is ok but no operation perfrom
+ # status code is ok but no operation perform
assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == 0
@@ -1198,7 +1198,7 @@ def test_extend_deadline_when_busy(module_client, boto3_session):
def test_extend_deadline_project_not_available(module_client, boto3_session):
- """Is not possible to extend deadline to project in other status than available."""
+ """Is not possible to extend deadline to a project in another status than available."""
# create a new project and never release it
current_unit_admins = models.UnitUser.query.filter_by(unit_id=1, is_admin=True).count()
@@ -1239,7 +1239,7 @@ def test_extend_deadline_no_deadline(module_client, boto3_session):
assert project.times_expired == 0
time.sleep(1) # tests are too fast
- # try to extend deadline
+ # try to extend deadline - no new deadline provided
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
@@ -1251,8 +1251,9 @@ def test_extend_deadline_no_deadline(module_client, boto3_session):
def test_extend_deadline_not_enough_time_left(module_client, boto3_session):
- """If there are more than 10 days left, extend deadline should not be possible"""
+ """If there are still too much days left, extend deadline should not be yet possible"""
+ # create and release a new project with a long time left as available
current_deadline = 90
release_data_long_deadline = {"new_status": "Available", "deadline": current_deadline}
project_id, project = create_and_release_project(
@@ -1278,13 +1279,14 @@ def test_extend_deadline_not_enough_time_left(module_client, boto3_session):
def test_extend_deadline_too_much_days(module_client, boto3_session):
"""If the new deadline together with the time left already is more than 90 days it should not work"""
+ # create project with small deadline so it can be extended
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
)
assert project.times_expired == 0
time.sleep(1) # tests are too fast
- # try to extend deadline a lot of days
+ # try to extend deadline by a lot of days
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
@@ -1300,18 +1302,18 @@ def test_extend_deadline_too_much_days(module_client, boto3_session):
def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session):
"""If the deadline has been extended more than 2 times it should not work"""
+ # create project with small deadline so it can be extended
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
)
assert project.times_expired == 0
- time.sleep(1) # tests are too fast
-
- deadline = project.current_deadline
+ deadline = project.current_deadline # current deadline
new_deadline_in = 1 # small new deadline
+
for i in range(1, 4):
time.sleep(1) # tests are too fast
- # extend deadline with a small new deadline so we can do it several times
+ # extend deadline by a small new deadline so we can do it several times
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
@@ -1322,7 +1324,7 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se
assert response.status_code == http.HTTPStatus.OK
assert project.times_expired == i
assert project.current_deadline == deadline + datetime.timedelta(days=new_deadline_in)
- deadline = project.current_deadline
+ deadline = project.current_deadline # update current deadline
assert f"{project_id} has been given a new deadline" in response.json["message"]
assert "An e-mail notification has not been sent." in response.json["message"]
else:
@@ -1334,16 +1336,16 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se
def test_extend_deadline_ok(module_client, boto3_session):
- """Extend a project deadline of a project already release"""
+ """Extend a project deadline of a project - it should work ok"""
- # release with a small deadline
+ # release with a small deadline so it can be extended
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
)
assert project.times_expired == 0
time.sleep(1) # tests are too fast
- deadline = project.current_deadline
+ deadline = project.current_deadline # save current deadline
# extend deadline
response = module_client.patch(
@@ -1363,7 +1365,7 @@ def test_extend_deadline_ok(module_client, boto3_session):
def test_extend_deadline_mock_database_error(module_client, boto3_session):
- """Mock error when updating the database"""
+ """Mock error when performing the request"""
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
From c7976cb12187b569a81ebd31f93d0658baca1904 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 12 Oct 2023 11:56:23 +0200
Subject: [PATCH 112/155] more coverage
---
dds_web/api/project.py | 17 +----------------
tests/api/test_project.py | 8 +++++++-
2 files changed, 8 insertions(+), 17 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index a0e24e17f..c6eea1d41 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -252,11 +252,6 @@ def patch(self):
message="The new deadline needs to be less than (or equal to) 90 days."
)
- if project.times_expired > 2:
- raise DDSArgumentError(
- "Project availability limit: Project cannot be made Available any more times."
- )
-
try:
# add a fake archived status to mimick a re-release in order to have an udpated deadline
new_status_row = self.expire_project(
@@ -280,17 +275,7 @@ def patch(self):
except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.SQLAlchemyError) as err:
flask.current_app.logger.exception(err)
db.session.rollback()
- raise DatabaseError(
- message=str(err),
- alt_message=(
- "Status was not updated"
- + (
- ": Database malfunction."
- if isinstance(err, sqlalchemy.exc.OperationalError)
- else ": Server Error."
- )
- ),
- ) from err
+ raise err
return_message = f"{project.public_id} has been given a new deadline"
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 59146f695..ed79d7707 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -4,6 +4,7 @@
import http
from sqlite3 import OperationalError
import pytest
+from _pytest.logging import LogCaptureFixture
import datetime
import time
import unittest.mock
@@ -1364,7 +1365,9 @@ def test_extend_deadline_ok(module_client, boto3_session):
assert "An e-mail notification has not been sent." in response.json["message"]
-def test_extend_deadline_mock_database_error(module_client, boto3_session):
+def test_extend_deadline_mock_database_error(
+ module_client, boto3_session, capfd: LogCaptureFixture
+):
"""Mock error when performing the request"""
project_id, project = create_and_release_project(
@@ -1385,6 +1388,9 @@ def test_extend_deadline_mock_database_error(module_client, boto3_session):
assert response.status_code == http.HTTPStatus.INTERNAL_SERVER_ERROR
assert "Saving database changes failed." in response.json["message"]
+ _, err = capfd.readouterr()
+ assert "500 Internal Server Error: Saving database changes failed." in err
+
def test_projectstatus_post_deletion_and_archivation_errors(module_client, boto3_session):
"""Mock the different expections that can occur when deleting project."""
From 19ec5e22ea07d8a84168be7ac5fcd8604bcf80e9 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 12 Oct 2023 14:34:25 +0200
Subject: [PATCH 113/155] coverage
---
dds_web/api/project.py | 2 +-
tests/api/test_project.py | 38 +++++++++++++++++++++++++-------------
2 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index c6eea1d41..96856b988 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -273,7 +273,7 @@ def patch(self):
db.session.commit()
except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.SQLAlchemyError) as err:
- flask.current_app.logger.exception(err)
+ flask.current_app.logger.exception("Failed to extend deadline")
db.session.rollback()
raise err
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index ed79d7707..f4c8b3518 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -5,6 +5,7 @@
from sqlite3 import OperationalError
import pytest
from _pytest.logging import LogCaptureFixture
+import logging
import datetime
import time
import unittest.mock
@@ -1368,7 +1369,7 @@ def test_extend_deadline_ok(module_client, boto3_session):
def test_extend_deadline_mock_database_error(
module_client, boto3_session, capfd: LogCaptureFixture
):
- """Mock error when performing the request"""
+ """Operation fails when trying to save in the Database"""
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
@@ -1377,19 +1378,30 @@ def test_extend_deadline_mock_database_error(
time.sleep(1) # tests are too fast
token = tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client)
- with unittest.mock.patch("dds_web.db.session.commit", mock_sqlalchemyerror):
- # extend deadline
- response = module_client.patch(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=token,
- query_string={"project": project_id},
- json=extend_deadline_data,
- )
- assert response.status_code == http.HTTPStatus.INTERNAL_SERVER_ERROR
- assert "Saving database changes failed." in response.json["message"]
- _, err = capfd.readouterr()
- assert "500 Internal Server Error: Saving database changes failed." in err
+ with unittest.mock.patch.object(db.session, "rollback") as rollback:
+ with unittest.mock.patch("dds_web.db.session.commit") as mock_commit:
+ # we need this because the first time the commit function is called is when set_busy()
+ def side_effect_generator():
+ yield None # First call, no exception
+ while True:
+ yield sqlalchemy.exc.SQLAlchemyError() # Subsequent calls, exception
+
+ mock_commit.side_effect = side_effect_generator()
+
+ # extend deadline
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=token,
+ query_string={"project": project_id},
+ json=extend_deadline_data,
+ )
+ assert response.status_code == http.HTTPStatus.INTERNAL_SERVER_ERROR
+ assert "Saving database changes failed." in response.json["message"]
+
+ assert rollback.called
+ _, err = capfd.readouterr()
+ assert "Failed to extend deadline" in err
def test_projectstatus_post_deletion_and_archivation_errors(module_client, boto3_session):
From fa4c46c63ce14aff39e24d419bfed5d3f0011b27 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 13 Oct 2023 09:23:35 +0200
Subject: [PATCH 114/155] sprintlog
---
SPRINTLOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index 8001f5ca5..b6c399177 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -314,3 +314,4 @@ _Nothing merged in CLI during this sprint_
- Use full DDS name in MOTD email subject ([#1477](https://github.com/ScilifelabDataCentre/dds_web/pull/1477))
- Add flag --verify-checksum to the comand in email template ([#1478])(https://github.com/ScilifelabDataCentre/dds_web/pull/1478)
- Improved email layout; Highlighted information and commands when project is released ([#1479])(https://github.com/ScilifelabDataCentre/dds_web/pull/1479)
+- Added new API endpoint ProjectStatus.patch to extend the deadline ([#1480])(https://github.com/ScilifelabDataCentre/dds_web/pull/1480)
From 261fbcfc5e48deb6ea64d9b923e6271e05e847e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Fri, 13 Oct 2023 09:27:11 +0200
Subject: [PATCH 115/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 96856b988..c2a3af5af 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -233,7 +233,7 @@ def patch(self):
# deadline can only be extended from Available
if not project.current_status == "Available":
raise DDSArgumentError(
- "you can only extend the deadline for a project that has the status Available."
+ "You can only extend the deadline for a project that has the status 'Available'."
)
new_deadline_in = json_input.get("new_deadline_in")
From 0b531b57896fe4498649276d7bd1925e4a37a714 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Fri, 13 Oct 2023 09:27:22 +0200
Subject: [PATCH 116/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index c2a3af5af..6ed141edb 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -243,10 +243,6 @@ def patch(self):
raise DDSArgumentError(
message="No new deadline provived, cannot perform operation."
)
- if current_deadline > 10:
- raise DDSArgumentError(
- message=f"There are still {current_deadline} days left, it is not possible to extend deadline yet."
- )
if new_deadline_in + current_deadline > 90:
raise DDSArgumentError(
message="The new deadline needs to be less than (or equal to) 90 days."
From 5cca377ce59a17a14736d60cf9536eeca7004520 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Fri, 13 Oct 2023 09:27:34 +0200
Subject: [PATCH 117/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 6ed141edb..196ceebff 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -271,7 +271,7 @@ def patch(self):
except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.SQLAlchemyError) as err:
flask.current_app.logger.exception("Failed to extend deadline")
db.session.rollback()
- raise err
+ raise
return_message = f"{project.public_id} has been given a new deadline"
From a8efc2e19b966cf26cf8203ca0248f44ef3979d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Fri, 13 Oct 2023 09:27:45 +0200
Subject: [PATCH 118/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 196ceebff..4b4c5e23c 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -199,7 +199,7 @@ def patch(self):
project = dds_web.utils.collect_project(project_id=project_id)
dds_web.utils.verify_project_access(project=project)
- # get atributes
+ # Get json input from request
json_input = flask.request.get_json(silent=True) # Already checked by json_required
# false by default - operation must be confirmed by the user
From 7cb1d43ef175ffe40608f28b09f2675afeceacc6 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 13 Oct 2023 09:28:37 +0200
Subject: [PATCH 119/155] modified test according to comments
---
tests/api/test_project.py | 29 +----------------------------
1 file changed, 1 insertion(+), 28 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index f4c8b3518..c87e8cc4e 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1227,7 +1227,7 @@ def test_extend_deadline_project_not_available(module_client, boto3_session):
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert (
- "you can only extend the deadline for a project that has the status Available."
+ "You can only extend the deadline for a project that has the status 'Available'."
in response.json["message"]
)
@@ -1251,33 +1251,6 @@ def test_extend_deadline_no_deadline(module_client, boto3_session):
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert "No new deadline provived, cannot perform operation." in response.json["message"]
-
-def test_extend_deadline_not_enough_time_left(module_client, boto3_session):
- """If there are still too much days left, extend deadline should not be yet possible"""
-
- # create and release a new project with a long time left as available
- current_deadline = 90
- release_data_long_deadline = {"new_status": "Available", "deadline": current_deadline}
- project_id, project = create_and_release_project(
- client=module_client, proj_data=proj_data, release_data=release_data_long_deadline
- )
- assert project.times_expired == 0
- time.sleep(1) # tests are too fast
-
- # try to extend upon such deadline
- response = module_client.patch(
- tests.DDSEndpoint.PROJECT_STATUS,
- headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
- query_string={"project": project_id},
- json={"extend_deadline": True, "new_deadline_in": 20, "confirmed": True},
- )
- assert response.status_code == http.HTTPStatus.BAD_REQUEST
- assert (
- f"There are still {current_deadline} days left, it is not possible to extend deadline yet."
- in response.json["message"]
- )
-
-
def test_extend_deadline_too_much_days(module_client, boto3_session):
"""If the new deadline together with the time left already is more than 90 days it should not work"""
From a3939148d5f4f815c06aacbed59e54cca970b4bc Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 13 Oct 2023 09:28:56 +0200
Subject: [PATCH 120/155] modified test according to comments
---
tests/api/test_project.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index c87e8cc4e..d437dd592 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1251,6 +1251,7 @@ def test_extend_deadline_no_deadline(module_client, boto3_session):
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert "No new deadline provived, cannot perform operation." in response.json["message"]
+
def test_extend_deadline_too_much_days(module_client, boto3_session):
"""If the new deadline together with the time left already is more than 90 days it should not work"""
From 125e6651b77f581fc539c51f296cc09d20aac39a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Fri, 13 Oct 2023 09:29:19 +0200
Subject: [PATCH 121/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 4b4c5e23c..89ddffd3a 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -202,7 +202,7 @@ def patch(self):
# Get json input from request
json_input = flask.request.get_json(silent=True) # Already checked by json_required
- # false by default - operation must be confirmed by the user
+ # Operation must be confirmed by the user - False by default
confirmed_operation = json_input.get("confirmed", False)
if not isinstance(confirmed_operation, bool):
raise DDSArgumentError(message="`confirmed` is a boolean value: True or False.")
From 96e736826f8bfc153c71518e645357664c4e3133 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Fri, 13 Oct 2023 11:11:03 +0200
Subject: [PATCH 122/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 89ddffd3a..b7f06a443 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -221,6 +221,7 @@ def patch(self):
self.set_busy(project=project, busy=True)
+ # Extend deadline
try:
extend_deadline = json_input.get("extend_deadline", False) # False by default
From 55d40a9f4395aaf69128e8a30ff672b97fe6b79e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Fri, 13 Oct 2023 11:11:39 +0200
Subject: [PATCH 123/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index b7f06a443..cefdcdd5c 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -246,7 +246,7 @@ def patch(self):
)
if new_deadline_in + current_deadline > 90:
raise DDSArgumentError(
- message="The new deadline needs to be less than (or equal to) 90 days."
+ message=f"You requested the deadline to be extended with {new_deadline_in} days (from {current_deadline}), giving a new total deadline of {new_deadline_in + current_deadline} days. The new deadline needs to be less than (or equal to) 90 days."
)
try:
From 4f26b2dc005015905bbd7a9f2e3adfb738487acd Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 13 Oct 2023 11:45:34 +0200
Subject: [PATCH 124/155] modified acording to comments
---
dds_web/api/project.py | 24 ++++++++-------
tests/api/test_project.py | 63 +++++++++++++++++++++++++++++----------
2 files changed, 61 insertions(+), 26 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index cefdcdd5c..a16f6a726 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -223,32 +223,33 @@ def patch(self):
# Extend deadline
try:
- extend_deadline = json_input.get("extend_deadline", False) # False by default
+ new_deadline_in = json_input.get(
+ "new_deadline_in", None
+ ) # if not provided is None -> deadlie is not updated
# some variable definition
curr_date = dds_web.utils.current_time()
send_email = False
- # Update the deadline
- if extend_deadline:
+ # Update the deadline functionality
+ if new_deadline_in:
# deadline can only be extended from Available
if not project.current_status == "Available":
raise DDSArgumentError(
"You can only extend the deadline for a project that has the status 'Available'."
)
- new_deadline_in = json_input.get("new_deadline_in")
+ # it shouldnt surpass 90 days
current_deadline = (project.current_deadline - curr_date).days
-
- if not new_deadline_in:
- raise DDSArgumentError(
- message="No new deadline provived, cannot perform operation."
- )
if new_deadline_in + current_deadline > 90:
raise DDSArgumentError(
message=f"You requested the deadline to be extended with {new_deadline_in} days (from {current_deadline}), giving a new total deadline of {new_deadline_in + current_deadline} days. The new deadline needs to be less than (or equal to) 90 days."
)
+ if type(new_deadline_in) is not int:
+ raise DDSArgumentError(
+ message=" The deadline atribute passed should be of type Int (i.e a number)."
+ )
try:
# add a fake archived status to mimick a re-release in order to have an udpated deadline
new_status_row = self.expire_project(
@@ -257,7 +258,6 @@ def patch(self):
deadline_in=project.responsible_unit.days_in_expired,
)
project.project_statuses.append(new_status_row)
- db.session.commit()
new_status_row = self.release_project(
project=project,
@@ -279,7 +279,9 @@ def patch(self):
return_message += (
f". An e-mail notification has{' not ' if not send_email else ' '}been sent."
)
-
+ else:
+ # leave it for future new functionality of updating the status
+ return_message = "Nothing to update."
except:
self.set_busy(project=project, busy=False)
raise
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index d437dd592..a44b4cedd 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -49,7 +49,6 @@
release_project_small_deadline = {"new_status": "Available", "deadline": 5}
extend_deadline_data_no_confirmed = {
- "extend_deadline": True,
"new_deadline_in": 20,
}
@@ -1126,6 +1125,7 @@ def test_projectstatus_post_invalid_deadline_expire(module_client, boto3_session
def test_extend_deadline_bad_confirmed(module_client, boto3_session):
"""Try to extend a deadline and send a not boolean for confirmation"""
+ # create project and release it
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
)
@@ -1146,6 +1146,7 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session):
def test_extend_deadline_no_confirmed(module_client, boto3_session):
"""Try to extend a deadline before confirmation - should sent a warning and no operation is perfrom"""
+ # create project and release it
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
)
@@ -1169,6 +1170,7 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session):
def test_extend_deadline_when_busy(module_client, boto3_session):
"""Request should not be possible when project is busy."""
+ # create project and release it
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
)
@@ -1199,6 +1201,28 @@ def test_extend_deadline_when_busy(module_client, boto3_session):
)
+def test_extend_deadline_no_deadline(module_client, boto3_session):
+ """If no deadline has been provided it should not be executed anything"""
+
+ # create project and release it
+ project_id, project = create_and_release_project(
+ client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
+ )
+ assert project.times_expired == 0
+ time.sleep(1) # tests are too fast
+
+ # try to extend deadline - no new deadline provided
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json={"confirmed": True},
+ )
+ assert response.status_code == http.HTTPStatus.OK
+ assert project.times_expired == 0
+ assert "Nothing to update." in response.json["message"]
+
+
def test_extend_deadline_project_not_available(module_client, boto3_session):
"""Is not possible to extend deadline to a project in another status than available."""
@@ -1232,42 +1256,47 @@ def test_extend_deadline_project_not_available(module_client, boto3_session):
)
-def test_extend_deadline_no_deadline(module_client, boto3_session):
- """If no deadline has been provided it should fail"""
+def test_extend_deadline_too_much_days(module_client, boto3_session):
+ """If the new deadline together with the time left already is more than 90 days it should not work"""
+ # create project and release it
project_id, project = create_and_release_project(
- client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
+ client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
)
assert project.times_expired == 0
time.sleep(1) # tests are too fast
- # try to extend deadline - no new deadline provided
+ # try to extend deadline by a lot of days
+ extend_deadline_data = {**extend_deadline_data, "new_deadline_in": 90}
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json={"extend_deadline": True, "confirmed": True},
+ json=extend_deadline_data,
)
assert response.status_code == http.HTTPStatus.BAD_REQUEST
- assert "No new deadline provived, cannot perform operation." in response.json["message"]
+ assert (
+ "The new deadline needs to be less than (or equal to) 90 days." in response.json["message"]
+ )
-def test_extend_deadline_too_much_days(module_client, boto3_session):
- """If the new deadline together with the time left already is more than 90 days it should not work"""
+def test_extend_deadline_bad_new_deadline(module_client, boto3_session):
+ """If the new deadlien provided is not an integer it should fail"""
- # create project with small deadline so it can be extended
+ # create project and release it
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
)
assert project.times_expired == 0
time.sleep(1) # tests are too fast
- # try to extend deadline by a lot of days
+ # try to extend deadline with a bad new deadline
+ extend_deadline_data = {**extend_deadline_data, "new_deadline_in": "20"}
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json={"extend_deadline": True, "new_deadline_in": 90, "confirmed": True},
+ json=extend_deadline_data,
)
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert (
@@ -1278,7 +1307,7 @@ def test_extend_deadline_too_much_days(module_client, boto3_session):
def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session):
"""If the deadline has been extended more than 2 times it should not work"""
- # create project with small deadline so it can be extended
+ # create project and release it
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
)
@@ -1290,11 +1319,15 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se
time.sleep(1) # tests are too fast
# extend deadline by a small new deadline so we can do it several times
+ extend_deadline_data_small_deadline = {
+ **extend_deadline_data,
+ "new_deadline_in": new_deadline_in,
+ }
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json={**extend_deadline_data, "new_deadline_in": new_deadline_in},
+ json=extend_deadline_data_small_deadline,
)
if i < 3:
assert response.status_code == http.HTTPStatus.OK
@@ -1314,7 +1347,7 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se
def test_extend_deadline_ok(module_client, boto3_session):
"""Extend a project deadline of a project - it should work ok"""
- # release with a small deadline so it can be extended
+ # create project and release it
project_id, project = create_and_release_project(
client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
)
From 4141b12c0178b2cb04e6de2b925223593ead4d77 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 13 Oct 2023 11:52:46 +0200
Subject: [PATCH 125/155] small fix
---
dds_web/api/project.py | 9 +++++----
tests/api/test_project.py | 11 ++++++-----
2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index a16f6a726..e99f090da 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -239,6 +239,11 @@ def patch(self):
"You can only extend the deadline for a project that has the status 'Available'."
)
+ if type(new_deadline_in) is not int:
+ raise DDSArgumentError(
+ message="The deadline atribute passed should be of type Int (i.e a number)."
+ )
+
# it shouldnt surpass 90 days
current_deadline = (project.current_deadline - curr_date).days
if new_deadline_in + current_deadline > 90:
@@ -246,10 +251,6 @@ def patch(self):
message=f"You requested the deadline to be extended with {new_deadline_in} days (from {current_deadline}), giving a new total deadline of {new_deadline_in + current_deadline} days. The new deadline needs to be less than (or equal to) 90 days."
)
- if type(new_deadline_in) is not int:
- raise DDSArgumentError(
- message=" The deadline atribute passed should be of type Int (i.e a number)."
- )
try:
# add a fake archived status to mimick a re-release in order to have an udpated deadline
new_status_row = self.expire_project(
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index a44b4cedd..9cdfddb00 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1267,12 +1267,12 @@ def test_extend_deadline_too_much_days(module_client, boto3_session):
time.sleep(1) # tests are too fast
# try to extend deadline by a lot of days
- extend_deadline_data = {**extend_deadline_data, "new_deadline_in": 90}
+ extend_deadline_data_big_deadline = {**extend_deadline_data, "new_deadline_in": 90}
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json=extend_deadline_data,
+ json=extend_deadline_data_big_deadline,
)
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert (
@@ -1291,16 +1291,17 @@ def test_extend_deadline_bad_new_deadline(module_client, boto3_session):
time.sleep(1) # tests are too fast
# try to extend deadline with a bad new deadline
- extend_deadline_data = {**extend_deadline_data, "new_deadline_in": "20"}
+ extend_deadline_data_bad_deadline = {**extend_deadline_data, "new_deadline_in": "20"}
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
query_string={"project": project_id},
- json=extend_deadline_data,
+ json=extend_deadline_data_bad_deadline,
)
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert (
- "The new deadline needs to be less than (or equal to) 90 days." in response.json["message"]
+ "The deadline atribute passed should be of type Int (i.e a number)."
+ in response.json["message"]
)
From 1d0478cadcf47cd623b6bf1e7d21acb95a2f03c7 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Fri, 13 Oct 2023 11:53:10 +0200
Subject: [PATCH 126/155] black
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index e99f090da..977fad7ab 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -243,7 +243,7 @@ def patch(self):
raise DDSArgumentError(
message="The deadline atribute passed should be of type Int (i.e a number)."
)
-
+
# it shouldnt surpass 90 days
current_deadline = (project.current_deadline - curr_date).days
if new_deadline_in + current_deadline > 90:
From 0f0bb1f9d6b7dbb54cde4691c06a5b591be847b4 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 16 Oct 2023 15:16:05 +0200
Subject: [PATCH 127/155] unconfirmed returns project info
---
dds_web/api/project.py | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 977fad7ab..b40b7df01 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -208,7 +208,15 @@ def patch(self):
raise DDSArgumentError(message="`confirmed` is a boolean value: True or False.")
if not confirmed_operation:
warning_message = "Operation must be confirmed before proceding."
- return {"warning": warning_message}
+ project_info = ProjectInfo().get()
+ project_status = self.get()
+ json_returned = {
+ **project_info,
+ "project_status": project_status,
+ "warning": warning_message,
+ "default_unit_days": project.responsible_unit.days_in_expired,
+ }
+ return json_returned
# Cannot change project status if project is busy
if project.busy:
From 0cab328da2c844cf18419a7502b28b58c5c18297 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ina=20Od=C3=A9n=20=C3=96sterbo?=
Date: Tue, 17 Oct 2023 11:23:37 +0200
Subject: [PATCH 128/155] changelog
---
CHANGELOG.rst | 8 ++++++++
SPRINTLOG.md | 4 ++--
doc/procedures/new_release.md | 6 +++---
3 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d9b86cf5f..69752ed7b 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,14 @@
Changelog
==========
+.. _2.5.2:
+
+2.5.2 - 2023-10-25
+~~~~~~~~~~~~~~~~~~~~~
+
+- Users can revoke project access given to unaccepted invites (e.g. after a mistake).
+- Email layout changed. When project is released, important information is now highlighted, and the Project Title is displayed along with the DDS project ID.
+
.. _2.5.1:
2.5.1 - 2023-09-27
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index 8001f5ca5..cf0c7e19e 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -300,13 +300,13 @@ _Nothing merged in CLI during this sprint_
- Dependency: Bump `MariaDB` to LTS version 10.11.5 ([#1465](https://github.com/ScilifelabDataCentre/dds_web/pull/1465))
- Bug fixed: Row in `ProjectUsers` should also be added if it doesn't exist when giving Researcher access to a specific project ([#1464](https://github.com/ScilifelabDataCentre/dds_web/pull/1464))
- Workflow: Update PR template and clarify sections ([#1467](https://github.com/ScilifelabDataCentre/dds_web/pull/1467))
-- Revoke project access for unaccepted invites ([#1192])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13/backlog?epics=visible&selectedIssue=DDS-1192)
# 2023-09-18 - 2023-09-29
-- Column `sto4_start_time` is automatically set when the create-unit command is run ([#1668])(https://scilifelab.atlassian.net/jira/software/projects/DDS/boards/13?selectedIssue=DDS-1668)
+- Column `sto4_start_time` is automatically set when the create-unit command is run ([#1469](https://github.com/ScilifelabDataCentre/dds_web/pull/1469))
- Replace expired invites when there's a new invitation attempt ([#1466](https://github.com/ScilifelabDataCentre/dds_web/pull/1466))
- New version: 2.5.1 ([#1471](https://github.com/ScilifelabDataCentre/dds_web/pull/1471))
+- Revoke project access for unaccepted invites ([#1468](https://github.com/ScilifelabDataCentre/dds_web/pull/1468))
# 2023-10-02 - 2023-10-13
diff --git a/doc/procedures/new_release.md b/doc/procedures/new_release.md
index cdb83dc91..8fc7b1387 100644
--- a/doc/procedures/new_release.md
+++ b/doc/procedures/new_release.md
@@ -1,14 +1,14 @@
# How to create a new release
1. Create a PR from `dev` to `master`: "New release"
-2. Confirm that the development instance works and that the newest changes have been deployed
+2. Confirm that the development instance works and that the newest changes have been deployed. If not, make a new redeployment of dds-dev (via argocd).
1. _In general_, e.g. that it's up and running
2. _Specific feature has been added or changed:_ Confirm that it also works in the development instance
3. _The change is in the API:_ Confirm that the development instance works together with the CLI
-3. Fork a new branch from `dev`
-4. Update the version [changelog](../../CHANGELOG.rst)
+3. Fork a new branch from `dev` (locally)
+4. Update the version [changelog](../../CHANGELOG.rst), located at `dds_web/CHANGELOG.rst`
**Tip:** Use the PR to `master` to see all changes since last release.
From ee23e090d8f83fad63ed96b2740f3adc9d81a61c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ina=20Od=C3=A9n=20=C3=96sterbo?=
Date: Tue, 17 Oct 2023 11:27:44 +0200
Subject: [PATCH 129/155] update release info
---
doc/procedures/new_release.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/procedures/new_release.md b/doc/procedures/new_release.md
index 8fc7b1387..9a62b54d4 100644
--- a/doc/procedures/new_release.md
+++ b/doc/procedures/new_release.md
@@ -20,7 +20,7 @@
- _Minor changes, e.g. bug fix_: Minor version upgrade, e.g. `1.0.1 --> 1.0.2`
- _Small changes, e.g. new feature_: Mid version upgrade, e.g. `1.1.0 --> 1.2.0`
- - _Breaking changes or large new feature(s)_: Major version upgrade, e.g. `1.0.0 --> 2.0.0`
+ - _Breaking changes or large new feature(s)_: Major version upgrade, e.g. `1.0.0 --> 2.0.0` _AVOID THIS -- NEED TO INFORM USERS WELL IN ADVANCE IN THAT CASE SINCE IT WILL BLOCK THE USERS FROM USING ANY OLDER VERSIONS_
> Will break if CLI version not bumped as well
From 40696ed1a74bd8a38f40e2f107b57fc1b567fb93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ina=20Od=C3=A9n=20=C3=96sterbo?=
Date: Tue, 17 Oct 2023 11:31:33 +0200
Subject: [PATCH 130/155] version
---
dds_web/version.py | 2 +-
tests/test_version.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/dds_web/version.py b/dds_web/version.py
index 7a2056f56..667b52f95 100644
--- a/dds_web/version.py
+++ b/dds_web/version.py
@@ -1 +1 @@
-__version__ = "2.5.1"
+__version__ = "2.5.2"
diff --git a/tests/test_version.py b/tests/test_version.py
index 02a5bc16a..534eab711 100644
--- a/tests/test_version.py
+++ b/tests/test_version.py
@@ -2,4 +2,4 @@
def test_version():
- assert version.__version__ == "2.5.1"
+ assert version.__version__ == "2.5.2"
From e042eca19cf92ca8662c620a10b88052fbe542d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ina=20Od=C3=A9n=20=C3=96sterbo?=
Date: Tue, 17 Oct 2023 11:34:23 +0200
Subject: [PATCH 131/155] sprintlog
---
SPRINTLOG.md | 1 +
doc/procedures/new_release.md | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index cf0c7e19e..a5afa6219 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -314,3 +314,4 @@ _Nothing merged in CLI during this sprint_
- Use full DDS name in MOTD email subject ([#1477](https://github.com/ScilifelabDataCentre/dds_web/pull/1477))
- Add flag --verify-checksum to the comand in email template ([#1478])(https://github.com/ScilifelabDataCentre/dds_web/pull/1478)
- Improved email layout; Highlighted information and commands when project is released ([#1479])(https://github.com/ScilifelabDataCentre/dds_web/pull/1479)
+- New version: 2.5.2 ([#1482](https://github.com/ScilifelabDataCentre/dds_web/pull/1482))
diff --git a/doc/procedures/new_release.md b/doc/procedures/new_release.md
index 9a62b54d4..82bd4fbe4 100644
--- a/doc/procedures/new_release.md
+++ b/doc/procedures/new_release.md
@@ -25,7 +25,7 @@
> Will break if CLI version not bumped as well
6. Push version change to branch
-7. Create a new PR from `` to `dev`
+7. Create a new PR from `` to `dev`: "New version & changelog"
Wait for approval and merge by Product Owner or admin.
From 5df20c92a9d25362b317ac900950a62cc870fa4f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Tue, 17 Oct 2023 11:42:59 +0200
Subject: [PATCH 132/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index b40b7df01..ff086f0ff 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -233,7 +233,7 @@ def patch(self):
try:
new_deadline_in = json_input.get(
"new_deadline_in", None
- ) # if not provided is None -> deadlie is not updated
+ ) # if not provided --> is None -> deadline is not updated
# some variable definition
curr_date = dds_web.utils.current_time()
From 69693d5c9974e197c594642579ed91374be96f1e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Tue, 17 Oct 2023 11:43:24 +0200
Subject: [PATCH 133/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index ff086f0ff..044803b72 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -192,6 +192,7 @@ def post(self):
@logging_bind_request
@json_required
@handle_validation_errors
+ @handle_db_error
def patch(self):
"""Partially update a the project status"""
# Get project ID, project and verify access
From 470b3a126fb2e3ced68ca6476b44a6b9e5aa5b02 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 17 Oct 2023 15:11:52 +0200
Subject: [PATCH 134/155] moved set busy
---
dds_web/api/project.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 044803b72..873c5e661 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -228,10 +228,10 @@ def patch(self):
)
)
- self.set_busy(project=project, busy=True)
-
# Extend deadline
try:
+ self.set_busy(project=project, busy=True)
+
new_deadline_in = json_input.get(
"new_deadline_in", None
) # if not provided --> is None -> deadline is not updated
From 29d8eda1b2cf6dd406ebb7936e64a0b5b3bbb62b Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Thu, 19 Oct 2023 16:07:40 +0200
Subject: [PATCH 135/155] rollout
---
dds_web/api/project.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 873c5e661..af42d42fd 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -209,6 +209,7 @@ def patch(self):
raise DDSArgumentError(message="`confirmed` is a boolean value: True or False.")
if not confirmed_operation:
warning_message = "Operation must be confirmed before proceding."
+ # When not confirmed, return information about the project
project_info = ProjectInfo().get()
project_status = self.get()
json_returned = {
@@ -228,10 +229,10 @@ def patch(self):
)
)
+ self.set_busy(project=project, busy=True)
+
# Extend deadline
try:
- self.set_busy(project=project, busy=True)
-
new_deadline_in = json_input.get(
"new_deadline_in", None
) # if not provided --> is None -> deadline is not updated
From 4f4ff9155e71cf84dc7e9bc728242b5993cc819e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Fri, 20 Oct 2023 13:40:37 +0200
Subject: [PATCH 136/155] Update SPRINTLOG.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
SPRINTLOG.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/SPRINTLOG.md b/SPRINTLOG.md
index b6c399177..065b8b294 100644
--- a/SPRINTLOG.md
+++ b/SPRINTLOG.md
@@ -314,4 +314,7 @@ _Nothing merged in CLI during this sprint_
- Use full DDS name in MOTD email subject ([#1477](https://github.com/ScilifelabDataCentre/dds_web/pull/1477))
- Add flag --verify-checksum to the comand in email template ([#1478])(https://github.com/ScilifelabDataCentre/dds_web/pull/1478)
- Improved email layout; Highlighted information and commands when project is released ([#1479])(https://github.com/ScilifelabDataCentre/dds_web/pull/1479)
+
+# 2023-10-16 - 2023-10-27
+
- Added new API endpoint ProjectStatus.patch to extend the deadline ([#1480])(https://github.com/ScilifelabDataCentre/dds_web/pull/1480)
From ca9709459b3c304c0daaddb2498dc072b52c17e0 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 23 Oct 2023 10:42:48 +0200
Subject: [PATCH 137/155] new exceptions
---
dds_web/api/project.py | 9 ++++++-
tests/api/test_project.py | 52 +++++++++++++++++++++++++++++++--------
2 files changed, 50 insertions(+), 11 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index af42d42fd..423e9453b 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -240,6 +240,7 @@ def patch(self):
# some variable definition
curr_date = dds_web.utils.current_time()
send_email = False
+ default_unit_days = project.responsible_unit.days_in_expired
# Update the deadline functionality
if new_deadline_in:
@@ -254,7 +255,13 @@ def patch(self):
message="The deadline atribute passed should be of type Int (i.e a number)."
)
- # it shouldnt surpass 90 days
+ # New deadline shouldnt surpass the default unit days
+ if new_deadline_in > default_unit_days:
+ raise DDSArgumentError(
+ message=f"You requested the deadline to be extended {new_deadline_in}. The number of days has to be lower than the default deadline extension number of {default_unit_days} days"
+ )
+
+ # the new deadline + days left shouldnt surpass 90 days
current_deadline = (project.current_deadline - curr_date).days
if new_deadline_in + current_deadline > 90:
raise DDSArgumentError(
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 9cdfddb00..3bfb18519 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -46,7 +46,9 @@
# "date_updated",
]
-release_project_small_deadline = {"new_status": "Available", "deadline": 5}
+release_project = {"new_status": "Available"}
+release_project_small_deadline = {**release_project, "deadline": 5}
+release_project_big_deadline = {**release_project, "deadline": 80}
extend_deadline_data_no_confirmed = {
"new_deadline_in": 20,
@@ -1127,7 +1129,7 @@ def test_extend_deadline_bad_confirmed(module_client, boto3_session):
# create project and release it
project_id, project = create_and_release_project(
- client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
+ client=module_client, proj_data=proj_data, release_data=release_project
)
assert project.times_expired == 0
time.sleep(1) # tests are too fast
@@ -1148,7 +1150,7 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session):
# create project and release it
project_id, project = create_and_release_project(
- client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
+ client=module_client, proj_data=proj_data, release_data=release_project
)
assert project.times_expired == 0
time.sleep(1) # tests are too fast
@@ -1172,7 +1174,7 @@ def test_extend_deadline_when_busy(module_client, boto3_session):
# create project and release it
project_id, project = create_and_release_project(
- client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
+ client=module_client, proj_data=proj_data, release_data=release_project
)
assert project.times_expired == 0
time.sleep(1) # tests are too fast
@@ -1206,7 +1208,7 @@ def test_extend_deadline_no_deadline(module_client, boto3_session):
# create project and release it
project_id, project = create_and_release_project(
- client=module_client, proj_data=proj_data, release_data={"new_status": "Available"}
+ client=module_client, proj_data=proj_data, release_data=release_project
)
assert project.times_expired == 0
time.sleep(1) # tests are too fast
@@ -1259,15 +1261,15 @@ def test_extend_deadline_project_not_available(module_client, boto3_session):
def test_extend_deadline_too_much_days(module_client, boto3_session):
"""If the new deadline together with the time left already is more than 90 days it should not work"""
- # create project and release it
+ # create project and release it with big dealdine
project_id, project = create_and_release_project(
- client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
+ client=module_client, proj_data=proj_data, release_data=release_project_big_deadline
)
assert project.times_expired == 0
time.sleep(1) # tests are too fast
- # try to extend deadline by a lot of days
- extend_deadline_data_big_deadline = {**extend_deadline_data, "new_deadline_in": 90}
+ # try to extend deadline -> 80 + 11 > 90
+ extend_deadline_data_big_deadline = {**extend_deadline_data, "new_deadline_in": 11}
response = module_client.patch(
tests.DDSEndpoint.PROJECT_STATUS,
headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
@@ -1285,7 +1287,7 @@ def test_extend_deadline_bad_new_deadline(module_client, boto3_session):
# create project and release it
project_id, project = create_and_release_project(
- client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
+ client=module_client, proj_data=proj_data, release_data=release_project
)
assert project.times_expired == 0
time.sleep(1) # tests are too fast
@@ -1305,6 +1307,36 @@ def test_extend_deadline_bad_new_deadline(module_client, boto3_session):
)
+def test_extend_deadline_more_than_default(module_client, boto3_session):
+ """If the new deadline provided is more than the default unit days to release a project it should fail"""
+
+ # create project and release it
+ project_id, project = create_and_release_project(
+ client=module_client, proj_data=proj_data, release_data=release_project_small_deadline
+ )
+ assert project.times_expired == 0
+ time.sleep(1) # tests are too fast
+
+ default_unit_days = project.responsible_unit.days_in_expired
+
+ # try to extend deadline with a bigger deadline that it is suppose to have
+ extend_deadline_data_bad_deadline = {
+ **extend_deadline_data,
+ "new_deadline_in": default_unit_days + 1,
+ }
+ response = module_client.patch(
+ tests.DDSEndpoint.PROJECT_STATUS,
+ headers=tests.UserAuth(tests.USER_CREDENTIALS["unitadmin"]).token(module_client),
+ query_string={"project": project_id},
+ json=extend_deadline_data_bad_deadline,
+ )
+ assert response.status_code == http.HTTPStatus.BAD_REQUEST
+ assert (
+ "The number of days has to be lower than the default deadline extension number"
+ in response.json["message"]
+ )
+
+
def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_session):
"""If the deadline has been extended more than 2 times it should not work"""
From 7cc7b17ee948fc28f023049c58a20b164918ab7d Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 23 Oct 2023 10:59:02 +0200
Subject: [PATCH 138/155] new exceptions
---
dds_web/api/project.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 423e9453b..8967ee650 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -216,7 +216,6 @@ def patch(self):
**project_info,
"project_status": project_status,
"warning": warning_message,
- "default_unit_days": project.responsible_unit.days_in_expired,
}
return json_returned
From 806e12cd6a87bfec3540f6600fd5298c69a42874 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 23 Oct 2023 11:20:20 +0200
Subject: [PATCH 139/155] default unti days
---
dds_web/api/project.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 8967ee650..423e9453b 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -216,6 +216,7 @@ def patch(self):
**project_info,
"project_status": project_status,
"warning": warning_message,
+ "default_unit_days": project.responsible_unit.days_in_expired,
}
return json_returned
From 181ebbc9c7cbce15bec5b48cc81a17193d662911 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 23 Oct 2023 12:51:30 +0200
Subject: [PATCH 140/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 423e9453b..7f601bc6a 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -252,7 +252,7 @@ def patch(self):
if type(new_deadline_in) is not int:
raise DDSArgumentError(
- message="The deadline atribute passed should be of type Int (i.e a number)."
+ message="The deadline attribute passed should be of type Int (i.e a number)."
)
# New deadline shouldnt surpass the default unit days
From 681b4af920349c45acd412cd238477e08afaf205 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 23 Oct 2023 12:51:40 +0200
Subject: [PATCH 141/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 7f601bc6a..7132ccd2d 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -225,7 +225,7 @@ def patch(self):
raise ProjectBusyError(
message=(
f"The deadline for the project '{project_id}' is already in the process of being changed. "
- "Please try again later. \n\nIf you know the project is not busy, contact support."
+ "Please try again later. \n\nIf you know that the project is not busy, contact support."
)
)
From de4b7c505f594a66e4004bf898247b7dd2bca676 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 23 Oct 2023 12:51:57 +0200
Subject: [PATCH 142/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 7132ccd2d..3ff013968 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -292,10 +292,9 @@ def patch(self):
db.session.rollback()
raise
- return_message = f"{project.public_id} has been given a new deadline"
-
- return_message += (
- f". An e-mail notification has{' not ' if not send_email else ' '}been sent."
+ return_message = (
+ f"{project.public_id} has been given a new deadline. "
+ f"An e-mail notification has{' not ' if not send_email else ' '}been sent."
)
else:
# leave it for future new functionality of updating the status
From 1cc9c4d6f68b1b16747d9c66f8514c0824b21ecf Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 23 Oct 2023 12:56:31 +0200
Subject: [PATCH 143/155] test to match changes
---
tests/api/test_project.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 3bfb18519..a1479eebc 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1198,7 +1198,7 @@ def test_extend_deadline_when_busy(module_client, boto3_session):
in response.json["message"]
)
assert (
- "Please try again later. \n\nIf you know the project is not busy, contact support."
+ "Please try again later. \n\nIf you know that the project is not busy, contact support."
in response.json["message"]
)
@@ -1302,7 +1302,7 @@ def test_extend_deadline_bad_new_deadline(module_client, boto3_session):
)
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert (
- "The deadline atribute passed should be of type Int (i.e a number)."
+ "The deadline attribute passed should be of type Int (i.e a number)."
in response.json["message"]
)
From a4fb0f5dec253244399b38c4a546ad7a516b5b8d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 23 Oct 2023 15:49:51 +0200
Subject: [PATCH 144/155] Update tests/api/test_project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
tests/api/test_project.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index a1479eebc..944683d1d 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1167,6 +1167,7 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session):
assert project.times_expired == 0
assert "Operation must be confirmed before proceding." in response.json["warning"]
+ assert all(item in response.json for item in ["project_info", "project_status", "warning", "default_unit_days"])
def test_extend_deadline_when_busy(module_client, boto3_session):
From 703fa91124249cd9d8cb801f8399a4653852c0d9 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 23 Oct 2023 16:14:23 +0200
Subject: [PATCH 145/155] feedback
---
dds_web/api/project.py | 9 +++++++--
tests/api/test_project.py | 5 ++++-
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 3ff013968..d4cc65c43 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -267,9 +267,14 @@ def patch(self):
raise DDSArgumentError(
message=f"You requested the deadline to be extended with {new_deadline_in} days (from {current_deadline}), giving a new total deadline of {new_deadline_in + current_deadline} days. The new deadline needs to be less than (or equal to) 90 days."
)
-
+ # the dealine has changed at least two times, next time it expires
+ # wont change again -> error
+ if project.times_expired >= 2:
+ raise DDSArgumentError(
+ "Project availability limit: Project cannot be made Available any more times"
+ )
try:
- # add a fake archived status to mimick a re-release in order to have an udpated deadline
+ # add a fake expire status to mimick a re-release in order to have an udpated deadline
new_status_row = self.expire_project(
project=project,
current_time=curr_date,
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 944683d1d..638bb25d9 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1167,7 +1167,10 @@ def test_extend_deadline_no_confirmed(module_client, boto3_session):
assert project.times_expired == 0
assert "Operation must be confirmed before proceding." in response.json["warning"]
- assert all(item in response.json for item in ["project_info", "project_status", "warning", "default_unit_days"])
+ assert all(
+ item in response.json
+ for item in ["project_info", "project_status", "warning", "default_unit_days"]
+ )
def test_extend_deadline_when_busy(module_client, boto3_session):
From b3f478b8759ddd7d2a345f9626e8513fb4466913 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 23 Oct 2023 16:27:04 +0200
Subject: [PATCH 146/155] more explicative error
---
dds_web/api/project.py | 2 +-
tests/api/test_project.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index d4cc65c43..5bacb7069 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -271,7 +271,7 @@ def patch(self):
# wont change again -> error
if project.times_expired >= 2:
raise DDSArgumentError(
- "Project availability limit: Project cannot be made Available any more times"
+ "Project availability limit: The maximun number of changes in data availability has been reached."
)
try:
# add a fake expire status to mimick a re-release in order to have an udpated deadline
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 638bb25d9..99531c792 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1376,7 +1376,7 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se
else:
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert (
- "Project availability limit: Project cannot be made Available any more times"
+ "Project availability limit: The maximun number of changes in data availability has been reached."
in response.json["message"]
)
From 75987af295a2d89db6ae15075b3231437467f70b Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 23 Oct 2023 16:40:58 +0200
Subject: [PATCH 147/155] move check for data availability
---
dds_web/api/project.py | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 5bacb7069..cb63a2714 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -203,6 +203,13 @@ def patch(self):
# Get json input from request
json_input = flask.request.get_json(silent=True) # Already checked by json_required
+ # the status has changed at least two times,
+ # next time the project expires it wont change again -> error
+ if project.times_expired >= 2:
+ raise DDSArgumentError(
+ "Project availability limit: The maximun number of changes in data availability has been reached."
+ )
+
# Operation must be confirmed by the user - False by default
confirmed_operation = json_input.get("confirmed", False)
if not isinstance(confirmed_operation, bool):
@@ -267,12 +274,6 @@ def patch(self):
raise DDSArgumentError(
message=f"You requested the deadline to be extended with {new_deadline_in} days (from {current_deadline}), giving a new total deadline of {new_deadline_in + current_deadline} days. The new deadline needs to be less than (or equal to) 90 days."
)
- # the dealine has changed at least two times, next time it expires
- # wont change again -> error
- if project.times_expired >= 2:
- raise DDSArgumentError(
- "Project availability limit: The maximun number of changes in data availability has been reached."
- )
try:
# add a fake expire status to mimick a re-release in order to have an udpated deadline
new_status_row = self.expire_project(
From bbffbd59396d56abb9379abfb1f271e61fb81e5b Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Mon, 23 Oct 2023 16:43:12 +0200
Subject: [PATCH 148/155] typo
---
dds_web/api/project.py | 2 +-
tests/api/test_project.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index cb63a2714..a2cbf94ff 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -207,7 +207,7 @@ def patch(self):
# next time the project expires it wont change again -> error
if project.times_expired >= 2:
raise DDSArgumentError(
- "Project availability limit: The maximun number of changes in data availability has been reached."
+ "Project availability limit: The maximum number of changes in data availability has been reached."
)
# Operation must be confirmed by the user - False by default
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 99531c792..e7d614607 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1376,7 +1376,7 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se
else:
assert response.status_code == http.HTTPStatus.BAD_REQUEST
assert (
- "Project availability limit: The maximun number of changes in data availability has been reached."
+ "Project availability limit: The maximum number of changes in data availability has been reached."
in response.json["message"]
)
From 118708c10246d0546ab46b56c5d4a1c502140ffe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Mon, 23 Oct 2023 17:09:30 +0200
Subject: [PATCH 149/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index a2cbf94ff..12e6cd421 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -223,7 +223,7 @@ def patch(self):
**project_info,
"project_status": project_status,
"warning": warning_message,
- "default_unit_days": project.responsible_unit.days_in_expired,
+ "default_unit_days": project.responsible_unit.days_in_available,
}
return json_returned
From a5ab01adfb29b740fd97af36de2d3d6408865eee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Tue, 24 Oct 2023 09:05:25 +0200
Subject: [PATCH 150/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 12e6cd421..a568dd8de 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -247,7 +247,7 @@ def patch(self):
# some variable definition
curr_date = dds_web.utils.current_time()
send_email = False
- default_unit_days = project.responsible_unit.days_in_expired
+ default_unit_days = project.responsible_unit.days_in_available
# Update the deadline functionality
if new_deadline_in:
From 78f7418e5845fac95f245d8a4d3a126533de8259 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Tue, 24 Oct 2023 09:05:35 +0200
Subject: [PATCH 151/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index a568dd8de..541dda856 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -299,7 +299,7 @@ def patch(self):
raise
return_message = (
- f"{project.public_id} has been given a new deadline. "
+ f"The project '{project.public_id}' has been given a new deadline. "
f"An e-mail notification has{' not ' if not send_email else ' '}been sent."
)
else:
From dd50770446a60851e830a2b61412573d03e8f40c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Revuelta?=
<46089290+rv0lt@users.noreply.github.com>
Date: Tue, 24 Oct 2023 09:05:48 +0200
Subject: [PATCH 152/155] Update dds_web/api/project.py
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Ina Odén Österbo <35953392+i-oden@users.noreply.github.com>
---
dds_web/api/project.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 541dda856..9a016c350 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -265,7 +265,7 @@ def patch(self):
# New deadline shouldnt surpass the default unit days
if new_deadline_in > default_unit_days:
raise DDSArgumentError(
- message=f"You requested the deadline to be extended {new_deadline_in}. The number of days has to be lower than the default deadline extension number of {default_unit_days} days"
+ message=f"You requested the deadline to be extended {new_deadline_in} days. The number of days has to be lower than the default deadline extension number of {default_unit_days} days"
)
# the new deadline + days left shouldnt surpass 90 days
From 33e78efe8094c0276961e25a3529013a00d45102 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 24 Oct 2023 09:49:53 +0200
Subject: [PATCH 153/155] fix tests
---
tests/api/test_project.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index e7d614607..6baee43af 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1321,7 +1321,7 @@ def test_extend_deadline_more_than_default(module_client, boto3_session):
assert project.times_expired == 0
time.sleep(1) # tests are too fast
- default_unit_days = project.responsible_unit.days_in_expired
+ default_unit_days = project.responsible_unit.days_in_available
# try to extend deadline with a bigger deadline that it is suppose to have
extend_deadline_data_bad_deadline = {
@@ -1371,7 +1371,10 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se
assert project.times_expired == i
assert project.current_deadline == deadline + datetime.timedelta(days=new_deadline_in)
deadline = project.current_deadline # update current deadline
- assert f"{project_id} has been given a new deadline" in response.json["message"]
+ assert (
+ f"The project '{project_id}' has been given a new deadline"
+ in response.json["message"]
+ )
assert "An e-mail notification has not been sent." in response.json["message"]
else:
assert response.status_code == http.HTTPStatus.BAD_REQUEST
@@ -1406,7 +1409,7 @@ def test_extend_deadline_ok(module_client, boto3_session):
days=extend_deadline_data.get("new_deadline_in")
)
- assert f"{project_id} has been given a new deadline" in response.json["message"]
+ assert f"The project '{project_id}' has been given a new deadline" in response.json["message"]
assert "An e-mail notification has not been sent." in response.json["message"]
From 494b7bc4afbec6e42b6ffb821856280a27b54b25 Mon Sep 17 00:00:00 2001
From: rv0lt
Date: Tue, 24 Oct 2023 10:32:48 +0200
Subject: [PATCH 154/155] feedback
---
dds_web/api/project.py | 10 ++++++++--
tests/api/test_project.py | 2 ++
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/dds_web/api/project.py b/dds_web/api/project.py
index 9a016c350..ffe2c95ef 100644
--- a/dds_web/api/project.py
+++ b/dds_web/api/project.py
@@ -245,7 +245,6 @@ def patch(self):
) # if not provided --> is None -> deadline is not updated
# some variable definition
- curr_date = dds_web.utils.current_time()
send_email = False
default_unit_days = project.responsible_unit.days_in_available
@@ -269,6 +268,7 @@ def patch(self):
)
# the new deadline + days left shouldnt surpass 90 days
+ curr_date = dds_web.utils.current_time()
current_deadline = (project.current_deadline - curr_date).days
if new_deadline_in + current_deadline > 90:
raise DDSArgumentError(
@@ -276,13 +276,19 @@ def patch(self):
)
try:
# add a fake expire status to mimick a re-release in order to have an udpated deadline
+ curr_date = (
+ dds_web.utils.current_time()
+ ) # call current_time before each call so it is stored with different timestamps
new_status_row = self.expire_project(
project=project,
current_time=curr_date,
- deadline_in=project.responsible_unit.days_in_expired,
+ deadline_in=1, # some dummy deadline bc it will re-release now again
)
project.project_statuses.append(new_status_row)
+ curr_date = (
+ dds_web.utils.current_time()
+ ) # call current_time before each call so it is stored with different timestamps
new_status_row = self.release_project(
project=project,
current_time=curr_date,
diff --git a/tests/api/test_project.py b/tests/api/test_project.py
index 6baee43af..18fcfcb10 100644
--- a/tests/api/test_project.py
+++ b/tests/api/test_project.py
@@ -1371,6 +1371,7 @@ def test_extend_deadline_maxium_number_available_exceded(module_client, boto3_se
assert project.times_expired == i
assert project.current_deadline == deadline + datetime.timedelta(days=new_deadline_in)
deadline = project.current_deadline # update current deadline
+ assert project.current_status == "Available"
assert (
f"The project '{project_id}' has been given a new deadline"
in response.json["message"]
@@ -1408,6 +1409,7 @@ def test_extend_deadline_ok(module_client, boto3_session):
assert project.current_deadline == deadline + datetime.timedelta(
days=extend_deadline_data.get("new_deadline_in")
)
+ assert project.current_status == "Available"
assert f"The project '{project_id}' has been given a new deadline" in response.json["message"]
assert "An e-mail notification has not been sent." in response.json["message"]
From 59e44d5cf6f4eea1dc5cec8cec5da94d44549362 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ina=20Od=C3=A9n=20=C3=96sterbo?=
Date: Tue, 24 Oct 2023 11:44:06 +0200
Subject: [PATCH 155/155] changelog
---
CHANGELOG.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 69752ed7b..30c19b0dc 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -8,6 +8,7 @@ Changelog
- Users can revoke project access given to unaccepted invites (e.g. after a mistake).
- Email layout changed. When project is released, important information is now highlighted, and the Project Title is displayed along with the DDS project ID.
+- New endpoint `ProjectStatus.patch`: Unit Admins / Personnel can extend the project deadline.
.. _2.5.1: