forked from dj-stripe/dj-stripe
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add management command for re-processing Events (dj-stripe#954)
Resolves dj-stripe#89
- Loading branch information
1 parent
7254acc
commit e8ae93c
Showing
4 changed files
with
163 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
djstripe/management/commands/djstripe_process_events.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
from django.core.management.base import BaseCommand | ||
|
||
from ... import models | ||
from ... import settings as djstripe_settings | ||
from ...mixins import VerbosityAwareOutputMixin | ||
|
||
|
||
class Command(VerbosityAwareOutputMixin, BaseCommand): | ||
"""Command to process all Events. | ||
Optional arguments are provided to limit the number of Events processed. | ||
Note: this is only guaranteed go back at most 30 days based on the | ||
current limitation of stripe's events API. See: https://stripe.com/docs/api/events | ||
""" | ||
|
||
help = ( | ||
"Process all Events. Use optional arguments to limit the Events to process. " | ||
"Note: this is only guaranteed go back at most 30 days based on the current " | ||
"limitation of stripe's events API. See: https://stripe.com/docs/api/events" | ||
) | ||
|
||
def add_arguments(self, parser): | ||
"""Add optional arugments to filter Events by.""" | ||
# Use a mutually exclusive group to prevent multiple arguments being | ||
# specified together. | ||
group = parser.add_mutually_exclusive_group() | ||
group.add_argument( | ||
"--ids", | ||
nargs="*", | ||
help="An optional space separated list of specific Event IDs to sync.", | ||
) | ||
group.add_argument( | ||
"--failed", | ||
action="store_true", | ||
help="Syncs and processes only the events that have failed webhooks.", | ||
) | ||
group.add_argument( | ||
"--type", | ||
help=( | ||
"A string containing a specific event name," | ||
" or group of events using * as a wildcard." | ||
" The list will be filtered to include only" | ||
" events with a matching event property." | ||
), | ||
) | ||
|
||
def handle(self, *args, **options): | ||
"""Try to process Events listed from the API.""" | ||
# Set the verbosity to determine how much we output, if at all. | ||
self.set_verbosity(options) | ||
|
||
event_ids = options["ids"] | ||
failed = options["failed"] | ||
type_filter = options["type"] | ||
|
||
# Args are mutually exclusive, | ||
# so output what we are doing based on that assumption. | ||
if failed: | ||
self.output("Processing all failed events") | ||
elif type_filter: | ||
self.output( | ||
"Processing all events that match {filter}".format(filter=type_filter) | ||
) | ||
elif event_ids: | ||
self.output("Processing specific events {events}".format(events=event_ids)) | ||
else: | ||
self.output("Processing all available events") | ||
|
||
# Either use the specific event IDs to retrieve data, or use the api_list | ||
# if no specific event IDs are specified. | ||
if event_ids: | ||
listed_events = ( | ||
models.Event.stripe_class.retrieve( | ||
id=event_id, api_key=djstripe_settings.STRIPE_SECRET_KEY | ||
) | ||
for event_id in event_ids | ||
) | ||
else: | ||
list_kwargs = {} | ||
if failed: | ||
list_kwargs["delivery_success"] = False | ||
|
||
if type_filter: | ||
list_kwargs["type"] = type_filter | ||
|
||
listed_events = models.Event.api_list(**list_kwargs) | ||
|
||
self.process_events(listed_events) | ||
|
||
def process_events(self, listed_events): | ||
# Process each listed event. Capture failures and continue, | ||
# outputting debug information as verbosity dictates. | ||
count = 0 | ||
total = 0 | ||
for event_data in listed_events: | ||
try: | ||
total += 1 | ||
event = models.Event.process(data=event_data) | ||
count += 1 | ||
self.verbose_output(" Synced Event {id}".format(id=event.id)) | ||
except Exception as exception: | ||
self.verbose_output( | ||
" Failed processing Event {id}".format(id=event_data["id"]) | ||
) | ||
self.output(" {exception}".format(exception=exception)) | ||
self.verbose_traceback() | ||
|
||
if total == 0: | ||
self.output(" (no results)") | ||
else: | ||
self.output( | ||
" Processed {count} out of {total} Events".format( | ||
count=count, total=total | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters