Skip to content

Commit

Permalink
[BE] optimize refresh_customers command
Browse files Browse the repository at this point in the history
  • Loading branch information
poxip committed Jun 6, 2018
1 parent f82c196 commit b790bc9
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 12 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.txt
@@ -1,5 +1,9 @@
# Change Log
All notable changes to this project will be documented in this file.
## [0.6.4]
### Added
- refresh_customers command optimization

## [0.6.3]
### Fixed
- saving StripeCustomer.default_source
Expand Down
2 changes: 1 addition & 1 deletion aa_stripe/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
__title__ = "Arabella Stripe"
__version__ = "0.6.3"
__version__ = "0.6.4"
__author__ = "Jacek Ostanski"
__license__ = "MIT"
__copyright__ = "Copyright 2017 Arabella"
Expand Down
37 changes: 34 additions & 3 deletions aa_stripe/management/commands/refresh_customers.py
@@ -1,6 +1,9 @@
# -*- coding: utf-8 -*-
import sys

import stripe
from django.core.management.base import BaseCommand
from django.utils import timezone

from aa_stripe.models import StripeCustomer
from aa_stripe.settings import stripe_settings
Expand All @@ -11,8 +14,36 @@ class Command(BaseCommand):

def handle(self, *args, **options):
stripe.api_key = stripe_settings.API_KEY
for customer in StripeCustomer.objects.filter(is_active=True, is_created_at_stripe=True):
last_customer = None
retry_count = 0
updated_count = 0
start_time = timezone.now()
print("Began refreshing customers")
while True:
try:
customer.refresh_from_stripe()
response = stripe.Customer.list(limit=100, starting_after=last_customer)
except stripe.StripeError:
pass
if retry_count > 5:
raise
retry_count += 1
continue
else:
retry_count = 0

for stripe_customer in response["data"]:
updated_count += StripeCustomer.objects.filter(stripe_customer_id=stripe_customer["id"]).update(
sources=stripe_customer["sources"], default_source=stripe_customer["default_source"]
)

if not response["has_more"]:
break

sys.stdout.write(".") # indicate that the command did not hung up
sys.stdout.flush()
last_customer = response["data"][-1]

if updated_count:
print("\nCustomers updated: {} (took {:2f}s)".format(
updated_count, (timezone.now() - start_time).total_seconds()))
else:
print("No customers were updated.")
51 changes: 43 additions & 8 deletions tests/test_customers.py
Expand Up @@ -142,16 +142,51 @@ def test_change_description(self):
class TestRefreshCustomersCommand(BaseTestCase):
def setUp(self):
self._create_customer(is_active=False, is_created_at_stripe=False)
self._create_customer()

@mock.patch("aa_stripe.models.StripeCustomer.refresh_from_stripe")
def test_command(self, mocked_update):
call_command("refresh_customers")
mocked_update.assert_called_once()
self.active_customer = self._create_customer()

# the command should not exit in case of an error during update (for example customer does not exist)
mocked_update.side_effect = stripe.APIError
@requests_mock.Mocker()
def test_command(self, m):
customer1_data = {
"id": "cus_xyz",
"object": "customer",
"sources": [
{"id": "card_1"}
],
"default_source": "card_1"
}
customer2_data = customer1_data.copy()
customer2_data["id"] = "cus_2"
customer2_data["sources"] = [{"id": "card_2"}]
customer2_data["default_source"] = "card_2"
customer2_data = customer1_data.copy()
customer2_data["id"] = "cus_2"
customer2_data["sources"] = []
customer2_data["default_source"] = None
stripe_response_part1 = {
"object": "list",
"url": "/v1/customers",
"has_more": False,
"data": [
customer1_data
]
}
stripe_response_part2 = stripe_response_part1.copy()
stripe_response_part2["has_more"] = False
stripe_response_part2["data"] = [customer2_data]
m.register_uri("GET", "https://api.stripe.com/v1/customers", text=json.dumps(stripe_response_part1))
m.register_uri("GET", "https://api.stripe.com/v1/customers?starting_after=cus_xyz", [
{"text": "", "status_code": 500}, # make sure the command will try again
{"text": json.dumps(stripe_response_part2), "status_code": "200"}
])
call_command("refresh_customers")
self.active_customer.refresh_from_db()
self.assertEqual(self.active_customer.get_default_source_data(), {"id": "card_1"})

# the command should fail if call to api fails more than 5 times
with mock.patch("stripe.Customer.list") as mocked_list:
mocked_list.side_effect = stripe.APIError()
with self.assertRaises(stripe.APIError):
call_command("refresh_customers")

@requests_mock.Mocker()
def test_customer_refresh_from_stripe(self, m):
Expand Down

0 comments on commit b790bc9

Please sign in to comment.