diff --git a/stripe_checkout/stripe_checkout/generate_report.py b/stripe_checkout/stripe_checkout/generate_report.py index 15aae80b4be27c52ff1f28bd7729a83ee7782354..dd6dbd56e38a664f92f09a28bbd5fbc91f7d096d 100644 --- a/stripe_checkout/stripe_checkout/generate_report.py +++ b/stripe_checkout/stripe_checkout/generate_report.py @@ -131,7 +131,7 @@ class ReportField: path: str transform: Optional[Callable] = None - def get_value(self, reader: InvoiceWrapper): + def get_value(self, reader: StripeWrapper): value = getattr_or_none(reader, self.path.split(".")) if value is not None and self.transform: value = self.transform(value) diff --git a/stripe_checkout/stripe_checkout/visit.py b/stripe_checkout/stripe_checkout/visit.py index 1832d2144755a20753c3c4246d39919b2348f604..5ba5f06e5f9cc52dffc6cafbf93c106b237932ab 100644 --- a/stripe_checkout/stripe_checkout/visit.py +++ b/stripe_checkout/stripe_checkout/visit.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Optional, Union +from typing import Callable, Optional, Union from django.http import Http404 import requests from requests.auth import HTTPBasicAuth @@ -24,18 +24,45 @@ class VisitorAPI: response.raise_for_status() return response.json() - def list_visitors(self): - all_visitors = self._request( + def _request_visitors(self, from_revision=None): + params = {"showDeleted": "false"} + if from_revision is not None: + params["fromRevision"] = from_revision + return self._request( "get", f"{BASE_URL}/visitors/{self.expo_id}", - params={"showDeleted": "false"}, + params=params, ) + + def list_visitors(self): + all_visitors = self._request_visitors() return [ Visitor.from_api(data) for data in filter(lambda v: not v["deleted"], all_visitors) ] - def get_visitor(self, visitor_id: str): + def list_all_visitors(self, log_progress: Callable[[str]]): + last_revision = 0 + next_revision = None + visitors_by_id = {} + while True: + response = self._request_visitors(from_revision=next_revision) + if log_progress is not None: + log_progress(f"Retrieved {len(response)} visitors") + + next_revision = response[-1]["revision"] + for visitor in response: + visitors_by_id[visitor["id"]] = visitor + + if next_revision == last_revision: + break + last_revision = next_revision + return [ + Visitor.from_api(data) + for data in filter(lambda v: not v["deleted"], visitors_by_id.values()) + ] + + def get_visitor(self, visitor_id: str, allow_deleted=False): result = self._request( "get", f"{BASE_URL}/visitors/{self.expo_id}/{visitor_id}" ) @@ -45,7 +72,8 @@ class VisitorAPI: # properly raise if not isinstance(result, dict): raise Http404() - + if result['deleted'] and not allow_deleted: + raise Http404() return Visitor.from_api(result) def update_visitor( diff --git a/test/test_visit_client.py b/test/test_visit_client.py index 1e00118e0e9aacb66fa8ab460841b348b8d3ac0b..88a0e9a91f623672b648542b4c5302424b8b6ba0 100644 --- a/test/test_visit_client.py +++ b/test/test_visit_client.py @@ -29,3 +29,21 @@ def test_special_fake_visitor_id_that_return_list(api): ) with pytest.raises(Http404): api.get_visitor("fakevisitor") + + +@responses.activate +def test_raises_on_deleted_visitor(api): + responses.reset() + responses.add( + responses.GET, + re.compile(r"https://api.visitcloud.com/create/v2/visitors/[^/]+/[^/]+$"), + json={ + "deleted": True, + "expo": {"id": "18lm2fafttito", "name": "TNC25", "reference": ""}, + "id": "0keoqgpagjw9e", + "reference": "", + "revision": 11539734023, + }, + ) + with pytest.raises(Http404): + api.get_visitor("0keoqgpagjw9e")