From 56c616c3eeeea29373d58c187fa132c9052ad1a1 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Thu, 27 Apr 2017 18:15:18 -0400 Subject: [PATCH] Factor set_paypal_address out of endpoint --- gratipay/models/participant/email.py | 8 ++++---- gratipay/models/participant/routes.py | 21 +++++++++++++++++++++ tests/py/test_participant_routes.py | 19 +++++++++++++++++++ www/~/%username/routes/associate.json.spt | 9 +-------- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/gratipay/models/participant/email.py b/gratipay/models/participant/email.py index fcd4985721..49c74219c1 100644 --- a/gratipay/models/participant/email.py +++ b/gratipay/models/participant/email.py @@ -338,10 +338,10 @@ def get_email(self, address, cursor=None, and_lock=False): return (cursor or self.db).one(sql, (self.id, address)) - def get_emails(self): + def get_emails(self, cursor=None): """Return a list of all email addresses on file for this participant. """ - return self.db.all(""" + return (cursor or self.db).all(""" SELECT * FROM emails WHERE participant_id=%s @@ -349,10 +349,10 @@ def get_emails(self): """, (self.id,)) - def get_verified_email_addresses(self): + def get_verified_email_addresses(self, cursor=None): """Return a list of verified email addresses on file for this participant. """ - return [email.address for email in self.get_emails() if email.verified] + return [email.address for email in self.get_emails(cursor) if email.verified] def remove_email(self, address): diff --git a/gratipay/models/participant/routes.py b/gratipay/models/participant/routes.py index 1bf0d1ce56..13ee5b894a 100644 --- a/gratipay/models/participant/routes.py +++ b/gratipay/models/participant/routes.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals import braintree +from psycopg2 import IntegrityError, errorcodes from ..exchange_route import ExchangeRoute from ...billing.instruments import CreditCard @@ -12,22 +13,26 @@ class Routes(object): """Participants have payment routes to get money into and out of Gratipay. """ + def get_paypal_error(self): """Return the error associated with the participant's PayPal account, or ``None``. """ return getattr(ExchangeRoute.from_network(self, 'paypal'), 'error', None) + def get_credit_card_error(self): """Return the error associated with the participant's credit card, or ``None``. """ return getattr(ExchangeRoute.from_network(self, 'braintree-cc'), 'error', None) + @property def has_payout_route(self): """A boolean computed property, whether the participant has a known-working payout route. """ return bool(self.get_payout_routes(good_only=True)) + def get_payout_routes(self, good_only=False, cursor=None): """Return a list of payout routes. If ``good_only`` evaluates to rue then only known-working payout routes are included. @@ -42,6 +47,7 @@ def get_payout_routes(self, good_only=False, cursor=None): out.append(route) return out + def get_braintree_account(self): """Fetch or create a braintree account for this participant. """ @@ -64,6 +70,7 @@ def get_braintree_account(self): customer = braintree.Customer.find(self.braintree_customer_id) return customer + def get_braintree_token(self): """Return the braintree token for this participant. """ @@ -72,6 +79,7 @@ def get_braintree_token(self): token = braintree.ClientToken.generate({'customer_id': account.id}) return token + def credit_card_expiring(self): """Return a boolean, whether the participant's credit card is set to expire soon. """ @@ -83,3 +91,16 @@ def credit_card_expiring(self): if not (year and month): return False return is_card_expiring(int(year), int(month)) + + + def set_paypal_address(self, address, cursor=None): + """Given an email address as a string, set it as the participant's PayPal address. + """ + assert address in self.get_verified_email_addresses(cursor) + try: + ExchangeRoute.insert(self, 'paypal', address, cursor=cursor) + except IntegrityError as e: + if e.pgcode != errorcodes.UNIQUE_VIOLATION: + raise + existing_route = ExchangeRoute.from_address(self, 'paypal', address) + existing_route.revive() diff --git a/tests/py/test_participant_routes.py b/tests/py/test_participant_routes.py index 669e2dae5f..a5c75a8fe2 100644 --- a/tests/py/test_participant_routes.py +++ b/tests/py/test_participant_routes.py @@ -25,3 +25,22 @@ def test_scopes_to_cursor(self): assert alice.get_payout_routes() == [] assert alice.get_payout_routes(cursor=cursor) == [route] assert alice.get_payout_routes() == [route] + + +class SetPayPalAddress(Harness): + + def test_sets_paypal_address(self): + alice = self.make_participant('alice', claimed_time='now') + self.add_and_verify_email(alice, 'alice@example.com') + alice.set_paypal_address('alice@example.com') + paypal = alice.get_payout_routes()[0] + assert paypal.network == 'paypal' + assert paypal.address == 'alice@example.com' + + def test_scopes_to_cursor(self): + alice = self.make_participant('alice', claimed_time='now') + self.add_and_verify_email(alice, 'alice@example.com') + with self.db.get_cursor() as cursor: + alice.set_paypal_address('alice@example.com', cursor) + assert alice.get_payout_routes() == [] + assert alice.get_payout_routes(cursor=cursor)[0].address == 'alice@example.com' diff --git a/www/~/%username/routes/associate.json.spt b/www/~/%username/routes/associate.json.spt index 5fc98c50bb..092de66092 100644 --- a/www/~/%username/routes/associate.json.spt +++ b/www/~/%username/routes/associate.json.spt @@ -7,7 +7,6 @@ import braintree from gratipay.billing.exchanges import repr_exception from gratipay.models.exchange_route import ExchangeRoute from gratipay.utils import bitcoin, get_participant -from psycopg2 import IntegrityError, errorcodes [---] @@ -40,13 +39,7 @@ if change: elif network == 'paypal': if address not in participant.get_verified_email_addresses(): raise Response(400, _("Only verified email addresses allowed.")) - try: - ExchangeRoute.insert(participant, network, address) - except IntegrityError as e: - if e.pgcode != errorcodes.UNIQUE_VIOLATION: - raise - existing_route = ExchangeRoute.from_address(participant, network, address) - existing_route.revive() + participant.set_paypal_address(address) elif network == 'bitcoin': if not bitcoin.validate(address):