Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deseng-463:Poll Widget: Back-end #2363

Merged
merged 16 commits into from
Jan 25, 2024
Merged

Conversation

ratheesh-aot
Copy link
Collaborator

Issue #: https://apps.itsm.gov.bc.ca/jira/browse/DESENG-463

Description of changes:

  1. Created Database models for Widget Poll, Poll Answers, Poll Response.
  2. Created API to manage Widget Poll, Poll Answers, Poll Response.
  3. Created Unit tests to test the code.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the met-public license (Apache 2.0).

@ratheesh-aot ratheesh-aot requested a review from Baelx January 22, 2024 22:30
@codecov-commenter
Copy link

codecov-commenter commented Jan 23, 2024

Codecov Report

Attention: 19 lines in your changes are missing coverage. Please review.

Comparison is base (49819b6) 71.96% compared to head (76cff56) 72.44%.
Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2363      +/-   ##
==========================================
+ Coverage   71.96%   72.44%   +0.48%     
==========================================
  Files         488      497       +9     
  Lines       16045    16395     +350     
  Branches     1230     1230              
==========================================
+ Hits        11546    11877     +331     
- Misses       4273     4292      +19     
  Partials      226      226              
Flag Coverage Δ
metapi 86.19% <94.57%> (+0.39%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Δ
met-api/src/met_api/models/__init__.py 100.00% <100.00%> (ø)
met-api/src/met_api/models/poll_answers.py 100.00% <100.00%> (ø)
met-api/src/met_api/models/poll_responses.py 100.00% <100.00%> (ø)
met-api/src/met_api/models/widget_poll.py 100.00% <100.00%> (ø)
met-api/src/met_api/resources/__init__.py 100.00% <100.00%> (ø)
met-api/src/met_api/schemas/widget_poll.py 100.00% <100.00%> (ø)
...t-api/src/met_api/services/poll_answers_service.py 100.00% <100.00%> (ø)
met-api/src/met_api/utils/ip_util.py 100.00% <100.00%> (ø)
...-api/src/met_api/services/poll_response_service.py 88.46% <88.46%> (ø)
...et-api/src/met_api/services/widget_poll_service.py 93.33% <93.33%> (ø)
... and 1 more

Copy link
Collaborator

@Baelx Baelx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work so far! I'd like to discuss a few things before proceeding:

  • Can you let me know how the IP comparison is happening? I'm not seeing where the IP address of the current response is being compared to all other unhashed IP fragments. Is that functionality coming in a future PR?
  • I don't think we need a method to be able to update poll responses. Let me know if I'm missing something
  • I just want to make sure we're only allowing one response from a user for a given poll ID, not 10. I may be missing something though

return str(err), err.status_code

@staticmethod
def validate_request_format(data):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I ask why you went with a dedicated method for the schema validation?

@cors_preflight('POST')
@API.route('/<int:poll_widget_id>/responses')
class PollResponseRecord(Resource):
"""Resource for recording responses for a poll widget. Not require authentication."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Resource for recording responses for a poll widget. Not require authentication."""
"""Resource for recording responses for a poll widget. Does not require authentication."""


@staticmethod
def is_poll_limit_exceeded(poll_id, participant_id):
"""Check poll limt execeeded or not."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Check poll limt execeeded or not."""
"""Check poll limit execeeded or not."""

@staticmethod
def is_poll_limit_exceeded(poll_id, participant_id):
"""Check poll limt execeeded or not."""
return WidgetPollService.check_already_polled(poll_id, participant_id, 10)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that we're checking if a user has polled more than 10 times? We should be only allowing for a single response to a given poll

raise BusinessException(str(e), HTTPStatus.INTERNAL_SERVER_ERROR) from e

@staticmethod
def delete_poll_answers(poll_id: int):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the PO thinks we should allow polls to be changed while they're being voted on, then we may need to do a check to see if answers have been responded to and prevent deletion if so.

"""
This module provides utility functions for handling IP addresses in a Flask application.

It includes a function for hashing IP addresses using SHA256, ensuring secure and consistent
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great comments at the top of this file!

str: The resulting SHA256 hash as a hexadecimal string.
"""
# Retrieve the secret key from Flask configuration with a fallback empty string
secret_key = current_app.config.get('SECRET_KEY', '')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to change the naming of this environment variable to something more intuitive. Maybe IP_HASH_KEY ?

PollResponse.is_deleted == false()).all()

@classmethod
def update_response(cls, response_id, response_data: dict) -> PollResponse:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there will be a need to update user responses. Once a user votes, they can't change it. I don't think we need this method

secret_key = current_app.config.get('SECRET_KEY', '')

# Concatenate the IP address and secret key, and hash the resulting string
return sha256(f'{ip_address}{secret_key}'.encode()).hexdigest()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hashes the IP, but where is the un-hash or IP comparison method? Is that going to come later?

@ratheesh-aot
Copy link
Collaborator Author

Thank you for the review, @Baelx .

Quick summary based on our discussion: We're hashing IPs with our secret key and matching them against the database to flag any hash polled over 10 times. This threshold accounts for shared IPs in a network. We decided on the number 10 based on the requirement from the ticket.

We plan to add an 'is_deleted' metadata field to poll responses for better handling of suspicious entries, as per the epic ticket's requirements and thats why we had the update function to Poll Response.

I've corrected the typos you mentioned and included the missing code for IP fragmentation before hashing. The SECRET_KEY we're using is from the existing Flask configuration, not a new variable.

Regarding the logic to prevent admin users from editing/removing poll answers post-publication, I'll include that in the upcoming PR for the Poll widget UI. This was confirmed post the current PR.

Appreciate your feedback and support.

Copy link

Quality Gate Passed Quality Gate passed

Kudos, no new issues were introduced!

0 New issues
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarCloud

@ratheesh-aot ratheesh-aot merged commit ce34d48 into bcgov:main Jan 25, 2024
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants