From 6c337a3a59983f6b802952e58a5592378421cf71 Mon Sep 17 00:00:00 2001 From: Pelle Koster <pelle.koster@geant.org> Date: Thu, 20 Mar 2025 10:46:32 +0100 Subject: [PATCH] better get/list visitor handling --- .../stripe_checkout/generate_report.py | 2 +- stripe_checkout/stripe_checkout/visit.py | 40 ++++++++++++++++--- test/test_visit_client.py | 18 +++++++++ 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/stripe_checkout/stripe_checkout/generate_report.py b/stripe_checkout/stripe_checkout/generate_report.py index 15aae80..dd6dbd5 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 1832d21..5ba5f06 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 1e00118..88a0e9a 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") -- GitLab