Skip to content

Commit

Permalink
Finished integration and unit tests for updating and deleting a discu…
Browse files Browse the repository at this point in the history
…ssion post
  • Loading branch information
JmScherer committed Jan 19, 2024
1 parent 66dfd2a commit 764a7d1
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 73 deletions.
14 changes: 9 additions & 5 deletions backend/src/models/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,19 +108,23 @@ def units_to_annotate(self):
return units

def find_discussion_post(self, discussion_post_id):
""" Finds a specific discussion post in an analysis by the discussion post id """
for discussion in self.discussions:
if discussion['post_id'] == discussion_post_id:
return discussion

return None

def find_authored_discussion_post(self, discussion_post_id, client_id):
""" Finds a discussion post from a user that authored the post in an analysis """
discussion_post = self.find_discussion_post(discussion_post_id)

if discussion_post == None:
print(discussion_post)

if discussion_post is None:
raise ValueError(f"Post '{discussion_post_id}' does not exist.")

if discussion_post['author_id'] == client_id:
return discussion_post
return None

return None
21 changes: 10 additions & 11 deletions backend/src/repository/analysis_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,25 +500,24 @@ def add_discussion_post(self, analysis_name: str, discussion_post: object):

def updated_discussion_post(self, discussion_post_id: str, discussion_content: str, analysis_name: str):
""" Edits a discussion post from an analysis to update the discussion post's content """

updated_document = self.collection.find_one_and_update(
{"name": analysis_name},
{"$set": { "discussions.$[item].content": discussion_content }},
array_filters = [{ "item.post_id": discussion_post_id }],
return_document=ReturnDocument.AFTER
)

updated_document = self.collection.find_one_and_update({"name": analysis_name}, {
"$set": {"discussions.$[item].content": discussion_content}
},
array_filters=[{"item.post_id": discussion_post_id}],
return_document=ReturnDocument.AFTER)

updated_document.pop("_id", None)

return updated_document['discussions']

def delete_discussion_post(self, discussion_post_id: str, analysis_name: str):
""" Removes a discussion post from an analysis """

updated_document = self.collection.find_one_and_update({"name": analysis_name}, {
"$pull": {"discussions": {"post_id": discussion_post_id}}},
return_document=ReturnDocument.AFTER
)
"$pull": {"discussions": {"post_id": discussion_post_id}}
},
return_document=ReturnDocument.AFTER)

updated_document.pop("_id", None)

Expand Down
62 changes: 31 additions & 31 deletions backend/src/routers/analysis_discussion_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,24 @@

router = APIRouter(tags=["analysis"], dependencies=[Depends(database)])


@router.get("/{analysis_name}/discussions")
def get_analysis_discussions(
analysis_name: str,
repositories=Depends(database)
):
def get_analysis_discussions(analysis_name: str, repositories=Depends(database)):
""" Returns a list of discussion posts for a given analysis """
logger.info("Retrieving the analysis '%s' discussions ", analysis_name)

found_analysis = repositories['analysis'].find_by_name(analysis_name)

if not found_analysis:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Analysis '{analysis_name}' does not exist.'"
)

status_code=status.HTTP_404_NOT_FOUND, detail=f"Analysis '{analysis_name}' does not exist.'"
)

analysis = Analysis(**found_analysis)

return analysis.discussions


@router.post("/{analysis_name}/discussions")
def add_analysis_discussion(
analysis_name: str,
Expand All @@ -52,10 +50,10 @@ def add_analysis_discussion(

if not found_analysis:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Analysis '{analysis_name}' does not exist.'"
)
status_code=status.HTTP_404_NOT_FOUND, detail=f"Analysis '{analysis_name}' does not exist.'"
)

analysis = Analysis(**found_analysis)
current_user = repositories["user"].find_by_client_id(client_id)

new_discussion_post = {
Expand All @@ -68,7 +66,8 @@ def add_analysis_discussion(
"thread": [],
}

return repositories['analysis'].add_discussion_post(found_analysis['name'], new_discussion_post)
return repositories['analysis'].add_discussion_post(analysis.name, new_discussion_post)


@router.put("/{analysis_name}/discussions/{discussion_post_id}")
def update_analysis_discussion_post(
Expand All @@ -78,26 +77,26 @@ def update_analysis_discussion_post(
repositories=Depends(database),
client_id: VerifyUser = Security(get_current_user)
):
logger.info("Editing post '%s' by user '%s' from the analysis '%s' with new content: '%s'",
discussion_post_id, client_id, analysis_name, discussion_content)
""" Updates a discussion post's content in an analysis by the discussion post id """
logger.info(
"Editing post '%s' by user '%s' from the analysis '%s' with new content: '%s'", discussion_post_id, client_id,
analysis_name, discussion_content
)

found_analysis = repositories['analysis'].find_by_name(analysis_name)

if not found_analysis:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Analysis '{analysis_name}' does not exist. Unable to update discussion post.'"
)
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Analysis '{analysis_name}' does not exist. Unable to update discussion post.'"
)

analysis = Analysis(**found_analysis)

try:
valid_post = analysis.find_authored_discussion_post(discussion_post_id, client_id)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(e)
)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) from e

if not valid_post:
raise HTTPException(
Expand All @@ -106,36 +105,37 @@ def update_analysis_discussion_post(

return repositories['analysis'].updated_discussion_post(valid_post['post_id'], discussion_content, analysis.name)


@router.delete("/{analysis_name}/discussions/{discussion_post_id}")
def delete_analysis_discussion(
analysis_name: str,
discussion_post_id: str,
repositories=Depends(database),
client_id: VerifyUser = Security(get_current_user)
):
""" Deletes a discussion post in an analysis by the discussion post id """
logger.info("Deleting post %s by user '%s' from the analysis '%s'", discussion_post_id, client_id, analysis_name)

found_analysis = repositories['analysis'].find_by_name(analysis_name)

if not found_analysis:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Analysis '{analysis_name}' does not exist. Unable to delete discussion post.'"
)
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Analysis '{analysis_name}' does not exist. Unable to delete discussion post.'"
)

analysis = Analysis(**found_analysis)

try:
valid_post = analysis.find_authored_discussion_post(discussion_post_id, client_id)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(e)
)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) from e

print(client_id)

if not valid_post:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="User cannot delete post they did not author."
)

return repositories['analysis'].delete_discussion_post(valid_post['post_id'], analysis.name)
2 changes: 1 addition & 1 deletion backend/tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def mock_current_user():
@pytest.fixture(name="mock_user")
def test_auth_user():
"""A mocked user that can be used to generate an OAuth2 access token"""
return {"sub": "johndoe", "scopes": ["read", "write"]}
return {"sub": "johndoe-client-id", "scopes": ["read", "write"]}


@pytest.fixture(name="mock_access_token")
Expand Down
97 changes: 84 additions & 13 deletions backend/tests/integration/test_analysis_routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def test_import_analysis_with_phenotips_json(
assert response.status_code == 200
response_data = json.loads(response.text)
assert response_data['latest_status'] == "Preparation"
assert response_data['timeline'][0]['username'] == 'johndoe'
assert response_data['timeline'][0]['username'] == 'johndoe-client-id'


def test_update_analysis_section(client, mock_access_token, mock_repositories, update_analysis_section_response_json):
Expand Down Expand Up @@ -380,6 +380,7 @@ def valid_query_side_effect(*args, **kwargs): # pylint: disable=unused-argument
return analysis

mock_repositories["user"].collection.find_one.return_value = {"full_name": new_post_user}
mock_repositories['analysis'].collection.find_one.return_value = cpam0002_analysis_json
mock_repositories["analysis"].collection.find_one_and_update.side_effect = valid_query_side_effect

response = client.post(
Expand All @@ -397,30 +398,100 @@ def valid_query_side_effect(*args, **kwargs): # pylint: disable=unused-argument
assert actual_most_recent_post['author_fullname'] == new_post_user
assert actual_most_recent_post['content'] == new_post_content


def test_update_discussion_post_in_analysis(client, mock_access_token, mock_repositories, cpam0002_analysis_json):
""" Tests successfully updating an existing post in the discussions with the user being the author """
cpam_analysis = "CPAM0002"
update_discussion_post_id = "9027ec8d-6298-4afb-add5-6ef710eb5e98"
update_post_content = "New text"
discussion_post_id = "fake-post-id"
discussion_content = "I am an integration test post. Look at me!"

def valid_query_side_effect(*args, **kwargs): # pylint: disable=unused-argument
# Inject a new discussion post by John Doe
def valid_query_side_effect_one(*args, **kwargs): # pylint: disable=unused-argument
analysis = cpam0002_analysis_json

new_discussion_post = {
"post_id": "fake-post-id", "author_id": "johndoe-client-id", "author_fullname": 'johndoe',
"content": "Hello, I am a discussion post."
}

analysis['discussions'].append(new_discussion_post)
analysis['_id'] = 'fake-mongo-object-id'
return analysis

def valid_query_side_effect_two(*args, **kwargs): # pylint: disable=unused-argument
find, query = args # pylint: disable=unused-variable
query_filter = kwargs

analysis = cpam0002_analysis_json
# analysis['discussions'].append(query['$push']['discussions'])
for discussion in analysis['discussions']:
if discussion['post_id'] == query['discussion_post_id']:
return discussion
fake_post_content = query['$set']['discussions.$[item].content']
fake_post_id = query_filter['array_filters'][0]['item.post_id']

for d in analysis['discussions']:
if d['post_id'] == fake_post_id:
d['content'] = fake_post_content

analysis['_id'] = 'fake-mongo-object-id'

return analysis

mock_repositories["analysis"].collection.find_one_and_update.side_effect

mock_repositories['analysis'].collection.find_one.side_effect = valid_query_side_effect_one
mock_repositories["analysis"].collection.find_one_and_update.side_effect = valid_query_side_effect_two

response = client.put(
"/analysis" + cpam_analysis + "/discussions",
"/analysis/" + cpam_analysis + "/discussions/" + discussion_post_id,
headers={"Authorization": "Bearer " + mock_access_token},
data={}
data={"discussion_content": discussion_content}
)

actual_post = None

for d in response.json():
if d['post_id'] == discussion_post_id:
actual_post = d

assert len(response.json()) == 3
assert actual_post['content'] == discussion_content

def test_delete_discussion_post_in_analysis(client, mock_access_token, mock_repositories, cpam0002_analysis_json):
""" Tests successfully deleting an existing post in the discussions with the user being the author """
cpam_analysis = "CPAM0002"
discussion_post_id = "fake-post-id"

# Inject a new discussion post by John Doe
def valid_query_side_effect_one(*args, **kwargs): # pylint: disable=unused-argument
analysis = cpam0002_analysis_json

new_discussion_post = {
"post_id": "fake-post-id",
"author_id": "johndoe-client-id",
"author_fullname": 'johndoe',
}

analysis['discussions'].append(new_discussion_post)
analysis['_id'] = 'fake-mongo-object-id'
return analysis

def valid_query_side_effect_two(*args, **kwargs): # pylint: disable=unused-argument
find, query = args # pylint: disable=unused-variable

analysis = cpam0002_analysis_json
fake_post_id = query['$pull']['discussions']['post_id']

analysis['discussions'] = [x for x in analysis['discussions'] if fake_post_id not in x['post_id']]
analysis['_id'] = 'fake-mongo-object-id'

return analysis

mock_repositories['analysis'].collection.find_one.side_effect = valid_query_side_effect_one
mock_repositories["analysis"].collection.find_one_and_update.side_effect = valid_query_side_effect_two

response = client.delete(
"/analysis/" + cpam_analysis + "/discussions/" + discussion_post_id,
headers={"Authorization": "Bearer " + mock_access_token}
)

assert True == True
assert len(response.json()) == 3


@pytest.fixture(name="analysis_updates_json")
def fixture_analysis_updates_json():
Expand Down
Loading

0 comments on commit 764a7d1

Please sign in to comment.