-
Notifications
You must be signed in to change notification settings - Fork 195
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
Feature: add new routes for donations and donors #7699
base: epic/campaigns
Are you sure you want to change the base?
Feature: add new routes for donations and donors #7699
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool seeing headers and links for pagination! This is often overlooked, but good that we are starting to leverage this feature.
I added some feedback on the queries. In particular, we have the CampaignDonationQuery
which we should consider using - or at least update what is here to account for subscriptions and test payments.
@JasonTheAdams I liked the idea of renaming I believe it would be appropriate to apply this replacement everywhere, including the routes implemented in this PR as well as those for Campaigns. So, I think we can move forward with this change unless the other devs have concerns or objections about it, let's check with them just to make sure we are not missing something here. |
Sounds great, @glaubersilva! I like the idea of retroactively applying this so long as the endpoints we're applying them to are:
|
'onlyWithDonations' => [ | ||
'type' => 'boolean', | ||
'default' => true, | ||
], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@glaubersilva You alluded to this parameter. This weirds me out. Hahah! This would make sense if this was a constituents
endpoint, but having a "I want donors that made donations" is weird. It just naturally raises the question: "How is a person a donor if they didn't donate?" 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a specific use-case for this filter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JasonTheAdams In my local dev site, it happens all the time when I delete all donations using the list table bulk actions but don't do the same for donors - check the attached screenshots for reference.
I believe that this scenario it's an edge case for real users, but it can happen.
So if we need for some reason to return all donors to run a specific action or routine regardless of it they have or not donations, we can set this param to false
.
This was my logic to implement it here. Thoughts? 😊
$query->join(function (JoinQueryBuilder $builder) use ($mode) { | ||
// The donationmeta1.donation_id should be used in other "donationmeta" joins to make sure we are retrieving data from the proper donation | ||
$builder->innerJoin('give_donationmeta', 'donationmeta1') | ||
->joinRaw("ON donationmeta1.meta_key = '" . DonationMetaKeys::DONOR_ID . "' AND donationmeta1.meta_value = ID"); | ||
|
||
// Include only current payment "mode" | ||
$builder->innerJoin('give_donationmeta', 'donationmeta2') | ||
->joinRaw("ON donationmeta2.meta_key = '" . DonationMetaKeys::MODE . "' AND donationmeta2.meta_value = '{$mode}' AND donationmeta2.donation_id = donationmeta1.donation_id"); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@glaubersilva @jonwaldstein
Thinking ahead to database changes, I strongly recommend that we update (or create) a DonorQueryBuilder
that has declarative methods for things like this. So this craziness becomes:
$query->whereDonorsHaveDonations();
It's cool for the REST APIs to use the query builder, but we want to reduce modifications that are directly coupled to the database structure (such as referencing meta like we are here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JasonTheAdams If you don't mind, I would like to refactor this in a subsequent PR because these endpoints will not be immediately used in the codebase, but I'm afraid to spend too much time on it and delay the merge into the epic even more - as we are changing the namespace of the campaign routes, we need to merge it before the public release.
Also, I would like to let the folder structure changes mentioned by @jonwaldstein here be included in this subsequent PR as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So long as the API itself is finalized in this PR, I'm cool with moving under-the-hood changes to a subsequent PR. BUT! You must agree to actually plan to do it. None of this, "oh yeah, we'll do that later for sure" stuff. 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@glaubersilva there's one more update i'd like to request that I was discussing with @JasonTheAdams recently: Let's move the actual registration of the rest endpoints into a shared top-level domain. The reason is our REST API is really it's own domain that should live outside of the business logic of our other domains. It also makes it much easier to manage 😄 It looks like we already have an ![]() We do also have an existing Service Provider that registers our v2 REST endpoints. I'm not opposed of doing something similar within our new |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Appreciate the continued work here, @glaubersilva! 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I won't die on this hill, but I'm not personally a fan of separating the request registration from the callbacks. First, it's not the convention. Second, it makes it harder to figure out the expected parameters. I think it feels necessary now because there's so much query building going on, but once we abstract out the query details it will greatly reduce the length of the callback code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JasonTheAdams Yeah! I agree, I just followed this pattern because we already are using it on the campaign routes. But I think we can leave it this way right now and refactor it later in a subsequent PR to improve the code maintenance.
'sensitiveData' => [ | ||
'type' => 'string', | ||
'default' => 'exclude', | ||
'enum' => [ | ||
'exclude', | ||
'include', | ||
], | ||
], | ||
'anonymousDonations' => [ | ||
'type' => 'string', | ||
'default' => 'exclude', | ||
'enum' => [ | ||
'exclude', | ||
'include', | ||
'redact', | ||
], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@glaubersilva Can you help me understand the two modes, here? What's each one for? Why two?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JasonTheAdams The sensitiveData
is applied to all donations and the anonymousDonations
is only for anonymous donations.
Previously, I was checking inside the escDonation()
method if the current user was logged as admin, and if not I was removing the sensitiveData
from the results.
So to keep the consistency and the concept that we applied for the mode
and anonymousDonations
parameters, I moved this state control logic to the new sensitiveData
param.
Therefore, the reason to have it separated into two parameters is that the admins should be able to retrieve it if they need it but these values should never be visible for non-admin users:
$sensitiveData = [
'donorIp',
'email',
'phone',
'billingAddress',
];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see! Thanks for explaining, @glaubersilva! So do we foresee a mode wherein folks would want to distinguish anonymous data from other sensitive data? Could it all just live under a single "sensitiveData" parameter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we don't need to redact the sensitive data (we need only include or exclude it) I simplify it to use a boolean value instead of an enum. See: 183ec8c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JasonTheAdams I think about it a LOT because my initial idea was to put it together but I concluded that it's not possible (or maybe I'm missing something here) because the anonymousDonations
param is designed to include
(or included with redact
info) or exclude
entries from the query. But the includeSensitiveData
doesn't have the power to change the query, it only changes (include or remove) the properties from donations or donors that will be visible in the results.
So, as admin, I can:
-
Exclude anonymous donations/donors with sensitive data available
-
Exclude anonymous donations/donors without sensitive data available
-
Include anonymous donations/donors with sensitive data available
-
Include anonymous donations/donors without sensitive data available
-
Redact anonymous donations/donors with sensitive data available
-
Redact anonymous donations/donors without sensitive data available
The difference here between the sensitive data and the anonymous data is that the first one is too personal and can be used to contact/localize the donors so only admins can have access to it and the second one is more light info that the donor can choose display or not (Redact) - this last one change how the query is build.
@JasonTheAdams @jonwaldstein I think I implemented the most important things we discussed above and other items will be implemented in a subsequent PR as discussed here, so I updated the PR description and now this is ready for re-review. |
Related to GIVE-1392 and GIVE-1393
Description
This PR implements 4 new REST API endpoints to retrieve
Donations
andDonors
. In the endpoints that return multiple entries, is possible to filter the returned data using custom parameters in the request and also is possible use pagination and sort the results using thepage
,per_page
,sort
anddirection
parameters.Another thing to consider is that sensitive data will be returned only if the user making the request is the site administrator and the
includeSensitiveData
param is set totrue
.Sensitive data for donations:
Sensitive data for donors:
The new endpoints to retrieve a single entry:
The new endpoints to retrieve multiple entries:
Important: These endpoints allow filtering the returned data through the
campaignId
parameter. It's also possible to use theincludeSensitiveData
(only available for admins) oranonymousDonations
andanonymousDonors
(only admins can include anonymous donations/donors without redacted data) parameters to exclude from the results the donations/donors that made anonymous donations and/or sensitive data that can be used to contact/localize the donors. Beyond that, on the/givewp/v3/donors
endpoint, it is possible to use theonlyWithDonations
parameter to retrieve all donors or just the ones that have valid donations completed. For more details, check this comment: #7699 (comment)Sample request including anonymous donations in the results with redact data:
Sample request including anonymous donors in the results with redact data:
Sample request to retrieve the 5 most recent donations:
Sample request to retrieve the top 5 donors:
Affects
GiveWP Rest API endpoints available for public use.
Testing Instructions
In your terminal, run the following commands:
Pre-review Checklist
@unreleased
tags included in DocBlocks