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

Adding source and submitted_by fields to privacy request #5206

Merged
merged 20 commits into from
Aug 26, 2024

Conversation

galvana
Copy link
Contributor

@galvana galvana commented Aug 15, 2024

Closes PROD-1809 and PROD-1788

❗Contains migration to add new columns to the consentrequest and privacyrequest tables

Description Of Changes

Updating privacy requests to include an optional source field. The options are currently restricted to the following:

  • Privacy Center - The most common source, for requests sent from the Privacy Center
  • Request Manager - For request submitted via the Request manager page in the Admin UI (plus only)
  • Consent Webhook - For requests created as part of the consent webhook flow for bidirectional consent (plus only)

This is a plus feature so I tried to draw the line in a reasonable way that wouldn't add to much scope. For example, I decided against creating overrides for the /consent and /privacy-request endpoints in plus that accept the source value in the request.

Code Changes

  • Updated requests from the Privacy Center to have a source of Privacy Center
  • Updated request from the Request manager page to have a source of Request Manager and to store the submitting user ID under the new privacy request column, submitted_by
  • Updated the Admin UI to show the source column in the privacy request table and privacy request detail page if plus is enabled

Steps to Confirm

  • Start fidesplus
  • Submit a review from the Privacy Center and from the Request manager in the Admin UI
  • Verify the two requests show the source in the Privacy request table and the privacy request details

Pre-Merge Checklist

  • All CI Pipelines Succeeded
  • Issue Requirements are Met
  • Update CHANGELOG.md
  • If there are any database migrations:
    • Ensure that your downrev is up to date with the latest revision on main
    • Ensure that your downgrade() migration is correct and works
      • If a downgrade migration is not possible for this change, please call this out in the PR description!

Copy link

vercel bot commented Aug 15, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
fides-plus-nightly ⬜️ Ignored (Inspect) Visit Preview Aug 26, 2024 9:27pm

@galvana galvana marked this pull request as ready for review August 16, 2024 23:53
clients/admin-ui/src/types/api/models/CollectionMeta.ts Outdated Show resolved Hide resolved
getattr(privacy_request_data, "source")
== PrivacyRequestSource.request_manager
):
kwargs["submitted_by"] = user_id
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This ticket adds a new submitted_by field to privacy requests so we can track the user that submitted a request from the Request manager page within the Admin UI.

Copy link
Contributor

Choose a reason for hiding this comment

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

do we have an enum set of accepted optional privacy req kwargs somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No enums here unfortunately but I do have a test for this so we'll know if the kwarg name ever changes

Copy link

cypress bot commented Aug 17, 2024

fides    Run #9659

Run Properties:  status check passed Passed #9659  •  git commit cc6eb2b3a3 ℹ️: Merge fa75aa11264c5a08020860a630cec374d2d33fe7 into b47f171d9b5b4ee1dd1236e824c1...
Project fides
Branch Review refs/pull/5206/merge
Run status status check passed Passed #9659
Run duration 00m 38s
Commit git commit cc6eb2b3a3 ℹ️: Merge fa75aa11264c5a08020860a630cec374d2d33fe7 into b47f171d9b5b4ee1dd1236e824c1...
Committer Adrian Galvan
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 4
⚠️ You've recorded test results over your free plan limit.
Upgrade your plan to view test results.
View all changes introduced in this branch ↗︎

@eastandwestwind
Copy link
Contributor

Starting review...

Copy link
Contributor

@eastandwestwind eastandwestwind left a comment

Choose a reason for hiding this comment

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

Looks great @galvana , just a couple clean-up items / suggestions for improvement.

Also kudos for knocking out general improvements to the codebase as you come across them!!

@@ -183,7 +183,7 @@ export const patchUserPreference = async (
debugLog(options.debug, "Calling Fides save preferences API");
const fetchOptions: RequestInit = {
...PATCH_FETCH_OPTIONS,
body: JSON.stringify(preferences),
body: JSON.stringify({ ...preferences, source: "Fides.js" }),
Copy link
Contributor

Choose a reason for hiding this comment

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

possible to use a shared CONST here? instead of "Fides.js"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

// See: PrivacyRequestSource enum in Fides
const REQUEST_SOURCE = "Fides.js";

Comment on lines +541 to +547
if not include_consent_webhook_requests:
query = query.filter(
or_(
PrivacyRequest.source != PrivacyRequestSource.consent_webhook,
PrivacyRequest.source.is_(None),
)
)
Copy link
Contributor

Choose a reason for hiding this comment

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

hmm do we want to filter where source is None? Won't this affect the ability to filter for privacy requests created before this work gets merged?

@@ -2003,6 +2021,9 @@ def create_privacy_request_func(
"Application redis cache required, but it is currently disabled! Please update your application configuration to enable integration with a redis cache."
)

if privacy_preferences is None:
privacy_preferences = []
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we set this default in the func signature instead?

privacy_preferences: Optional[
        List[PrivacyPreferenceHistory]
    ] = []

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I set this as a None value to avoid this gotcha with mutable default arguments https://docs.python-guide.org/writing/gotchas/. We don't mutate privacy_preferences in our function right now but I figured I'd do this now to avoid any pitfalls in the future.

I also changed the None check to this to let mypy know that privacy_preferences will be valued, even if it's an empty array

privacy_preferences = privacy_preferences or []

getattr(privacy_request_data, "source")
== PrivacyRequestSource.request_manager
):
kwargs["submitted_by"] = user_id
Copy link
Contributor

Choose a reason for hiding this comment

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

do we have an enum set of accepted optional privacy req kwargs somewhere?

@@ -294,6 +299,7 @@ class PrivacyRequest(
cancel_reason = Column(String(200))
canceled_at = Column(DateTime(timezone=True), nullable=True)
consent_preferences = Column(MutableList.as_mutable(JSONB), nullable=True)
source = Column(String, nullable=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we instead use a Column(EnumColumn(PrivacyRequestSource), nullable=True) here? This way we can still have the benefit of explicitly defining what we should expect in this column without having the enums formalized in the DB?

See how we do something similar with method = Column(EnumColumn(ConsentMethod)) in our PrivacyPreferenceHistory model

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion 👍

src/fides/api/models/privacy_request.py Outdated Show resolved Hide resolved
"""The source where the privacy request originated from"""

privacy_center = "Privacy Center"
request_manager = "Request Manager"
Copy link
Contributor

Choose a reason for hiding this comment

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

could you add a comment as to what each of these sources mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the docstring

The source where the privacy request originated from

- Privacy Center: Request created from the Privacy Center
- Request Manager: Request submitted from the Admin UI's Request manager page
- Consent Webhook: Request created as a side-effect of a consent webhook request (bidirectional consent)
- Fides.js: Request created as a side-effect of a privacy preference update from Fides.js

Copy link
Contributor

Choose a reason for hiding this comment

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

thanks! This is awesome!

Copy link
Contributor Author

@galvana galvana left a comment

Choose a reason for hiding this comment

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

Thanks for the review @eastandwestwind! I made the suggestions you recommended and I left some responses to your questions and concerns

@@ -183,7 +183,7 @@ export const patchUserPreference = async (
debugLog(options.debug, "Calling Fides save preferences API");
const fetchOptions: RequestInit = {
...PATCH_FETCH_OPTIONS,
body: JSON.stringify(preferences),
body: JSON.stringify({ ...preferences, source: "Fides.js" }),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

// See: PrivacyRequestSource enum in Fides
const REQUEST_SOURCE = "Fides.js";

@@ -294,6 +299,7 @@ class PrivacyRequest(
cancel_reason = Column(String(200))
canceled_at = Column(DateTime(timezone=True), nullable=True)
consent_preferences = Column(MutableList.as_mutable(JSONB), nullable=True)
source = Column(String, nullable=True)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion 👍

"""The source where the privacy request originated from"""

privacy_center = "Privacy Center"
request_manager = "Request Manager"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the docstring

The source where the privacy request originated from

- Privacy Center: Request created from the Privacy Center
- Request Manager: Request submitted from the Admin UI's Request manager page
- Consent Webhook: Request created as a side-effect of a consent webhook request (bidirectional consent)
- Fides.js: Request created as a side-effect of a privacy preference update from Fides.js

getattr(privacy_request_data, "source")
== PrivacyRequestSource.request_manager
):
kwargs["submitted_by"] = user_id
Copy link
Contributor Author

Choose a reason for hiding this comment

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

No enums here unfortunately but I do have a test for this so we'll know if the kwarg name ever changes

Comment on lines +541 to +547
if not include_consent_webhook_requests:
query = query.filter(
or_(
PrivacyRequest.source != PrivacyRequestSource.consent_webhook,
PrivacyRequest.source.is_(None),
)
)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This threw me off at first too but if I only do

PrivacyRequest.source != PrivacyRequestSource.consent_webhook,

The query will only return privacy requests where source is valued and doesn't equal consent_webook but it won't automatically include any privacy requests with null source values. So the full statement should read:

If we're not including consent webhook requests, then give me all the privacy requests where source (valued) does not equal consent_webhook or all privacy requests where source is null (inclusive or).

@@ -2003,6 +2021,9 @@ def create_privacy_request_func(
"Application redis cache required, but it is currently disabled! Please update your application configuration to enable integration with a redis cache."
)

if privacy_preferences is None:
privacy_preferences = []
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I set this as a None value to avoid this gotcha with mutable default arguments https://docs.python-guide.org/writing/gotchas/. We don't mutate privacy_preferences in our function right now but I figured I'd do this now to avoid any pitfalls in the future.

I also changed the None check to this to let mypy know that privacy_preferences will be valued, even if it's an empty array

privacy_preferences = privacy_preferences or []

Copy link
Contributor Author

@galvana galvana left a comment

Choose a reason for hiding this comment

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

Thanks for the review @eastandwestwind! I made the suggestions you recommended and I left some responses to your questions and concerns

@@ -2003,6 +2021,8 @@ def create_privacy_request_func(
"Application redis cache required, but it is currently disabled! Please update your application configuration to enable integration with a redis cache."
)

privacy_preferences = privacy_preferences or []
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed the way I did this check so my response to your old comment got flagged as "outdated" but I wanted to make sure you read my response.


I set this as a None value to avoid this gotcha with mutable default arguments https://docs.python-guide.org/writing/gotchas/. We don't mutate privacy_preferences in our function right now but I figured I'd do this now to avoid any pitfalls in the future.

I also changed the None check to this to let mypy know that privacy_preferences will be valued, even if it's an empty array

privacy_preferences = privacy_preferences or []

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh noooo this is the worst gotcha! Wow, thanks for explaining!

Copy link

codecov bot commented Aug 24, 2024

Codecov Report

Attention: Patch coverage is 94.44444% with 1 line in your changes missing coverage. Please review.

Project coverage is 86.41%. Comparing base (b47f171) to head (fa75aa1).
Report is 1 commits behind head on main.

Files Patch % Lines
.../api/api/v1/endpoints/privacy_request_endpoints.py 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #5206   +/-   ##
=======================================
  Coverage   86.41%   86.41%           
=======================================
  Files         362      362           
  Lines       22765    22781   +16     
  Branches     3056     3058    +2     
=======================================
+ Hits        19672    19687   +15     
  Misses       2537     2537           
- Partials      556      557    +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@galvana galvana merged commit 619ca85 into main Aug 26, 2024
20 checks passed
@galvana galvana deleted the PROD-1788-track-request-origin branch August 26, 2024 21:27
Copy link

cypress bot commented Aug 26, 2024

fides    Run #9660

Run Properties:  status check passed Passed #9660  •  git commit 619ca8589e: Adding source and submitted_by fields to privacy request (#5206)
Project fides
Branch Review main
Run status status check passed Passed #9660
Run duration 00m 39s
Commit git commit 619ca8589e: Adding source and submitted_by fields to privacy request (#5206)
Committer Adrian Galvan
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 4
⚠️ You've recorded test results over your free plan limit.
Upgrade your plan to view test results.
View all changes introduced in this branch ↗︎

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.

2 participants