Skip to content

Commit

Permalink
♻️ Refactor zgw_consumers client usage
Browse files Browse the repository at this point in the history
* create clients for each API with semantic methods
* replace `get_paginated_results` with `pagination_helper`
  • Loading branch information
stevenbal committed Feb 1, 2024
1 parent a89d2ac commit dc54e66
Show file tree
Hide file tree
Showing 11 changed files with 970 additions and 730 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ def test_categories_based_on_cases_for_eherkenning_user_with_vestigingsnummer(
self.assertEqual(context["categories"][3], self.category7)

@patch(
"open_inwoner.openzaak.cases.get_paginated_results",
"zgw_consumers.service.pagination_helper",
side_effect=RequestException,
)
def test_categories_fail_to_fetch_cases(self, m):
Expand Down
14 changes: 6 additions & 8 deletions src/open_inwoner/haalcentraal/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from open_inwoner.haalcentraal.api_models import BRPData
from open_inwoner.haalcentraal.models import HaalCentraalConfig
from open_inwoner.utils.api import ClientError, JSONParserClient
from open_inwoner.utils.api import ClientError, get_json_response

logger = logging.getLogger(__name__)

Expand All @@ -25,9 +25,7 @@ def __init__(self):
if not self.config.service:
logger.warning("no service defined for Haal Centraal")
else:
self.client = build_client(
self.config.service, client_factory=JSONParserClient
)
self.client = build_client(self.config.service)
self._is_ready = True

@abc.abstractmethod
Expand Down Expand Up @@ -74,7 +72,7 @@ def fetch_data(self, user_bsn: str) -> Optional[dict]:
headers["x-doelbinding"] = self.config.api_doelbinding

try:
data = self.client.get(
response = self.client.get(
url=url,
headers=headers,
params={
Expand All @@ -87,7 +85,7 @@ def fetch_data(self, user_bsn: str) -> Optional[dict]:
},
verify=False,
)
return data
return get_json_response(response)
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return None
Expand Down Expand Up @@ -121,7 +119,7 @@ class BRP_2_1(BRPAPI):
def fetch_data(self, user_bsn: str) -> Optional[dict]:
url = urljoin(self.client.base_url, "personen")
try:
data = self.client.post(
response = self.client.post(
url=url,
data={
"fields": [
Expand All @@ -145,7 +143,7 @@ def fetch_data(self, user_bsn: str) -> Optional[dict]:
headers={"Accept": "application/json"},
verify=False,
)
return data
return get_json_response(response)
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return None
Expand Down
234 changes: 227 additions & 7 deletions src/open_inwoner/openklant/clients.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,246 @@
import logging
from typing import Optional
from typing import List, Optional

from ape_pie.client import APIClient
from requests.exceptions import RequestException
from zgw_consumers.api_models.base import factory
from zgw_consumers.client import build_client as _build_client
from zgw_consumers.utils import pagination_helper

from open_inwoner.utils.api import JSONParserClient
from open_inwoner.openzaak.cases import fetch_case_by_url_no_cache
from open_inwoner.utils.api import ClientError, get_json_response

from .api_models import (
ContactMoment,
ContactMomentCreateData,
Klant,
KlantContactMoment,
KlantContactRol,
KlantCreateData,
ObjectContactMoment,
)
from .models import OpenKlantConfig

logger = logging.getLogger(__name__)


class KlantenClient(APIClient):
def create_klant(self, data: KlantCreateData) -> Optional[Klant]:
try:
response = self.post("klanten", json=data)
data = get_json_response(response)
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return

klant = factory(Klant, data)

return klant

def get_klant(
self, user_bsn: Optional[str] = None, user_kvk_or_rsin: Optional[str] = None
) -> Optional[Klant]:
if not user_bsn and not user_kvk_or_rsin:
return

# this is technically a search operation and could return multiple records
if user_bsn:
klanten = self._fetch_klanten_for_bsn(user_bsn)
elif user_kvk_or_rsin:
klanten = self._fetch_klanten_for_kvk_or_rsin(user_kvk_or_rsin)

if klanten:
# let's use the first one
return klanten[0]
else:
return

def _fetch_klanten_for_bsn(self, user_bsn: str) -> List[Klant]:
try:
response = self.get(
"klanten",
params={"subjectNatuurlijkPersoon__inpBsn": user_bsn},
)
data = get_json_response(response)
all_data = list(pagination_helper(self, data))
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return []

klanten = factory(Klant, all_data)

return klanten

def _fetch_klanten_for_kvk_or_rsin(
self, user_kvk_or_rsin: str, *, vestigingsnummer=None
) -> List[Klant]:
params = {"subjectNietNatuurlijkPersoon__innNnpId": user_kvk_or_rsin}

if vestigingsnummer:
params = {
"subjectVestiging__vestigingsNummer": vestigingsnummer,
}

try:
response = self.get(
"klanten",
params=params,
)
data = get_json_response(response)
all_data = list(pagination_helper(self, data))
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return []

klanten = factory(Klant, all_data)

return klanten

def patch_klant(self, klant: Klant, update_data) -> Optional[Klant]:
try:
response = self.patch(url=klant.url, json=update_data)
data = get_json_response(response)
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return

klant = factory(Klant, data)

return klant


class ContactmomentenClient(APIClient):
def create_contactmoment(
self,
data: ContactMomentCreateData,
*,
klant: Optional[Klant] = None,
rol: Optional[str] = KlantContactRol.BELANGHEBBENDE,
) -> Optional[ContactMoment]:
try:
response = self.post("contactmomenten", json=data)
data = get_json_response(response)
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return

contactmoment = factory(ContactMoment, data)

if klant:
# relate contact to klant though a klantcontactmoment
try:
response = self.post(
"klantcontactmomenten",
json={
"klant": klant.url,
"contactmoment": contactmoment.url,
"rol": rol,
},
)
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return

return contactmoment

def get_contactmoment(self, url) -> Optional[ContactMoment]:
try:
response = self.get(url)
data = get_json_response(response)
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return

contact_moment = factory(ContactMoment, data)

return contact_moment

def get_objectcontactmomenten_for_contactmoment(
self, contactmoment: ContactMoment
) -> List[ObjectContactMoment]:
try:
response = self.get(
"objectcontactmomenten", params={"contactmoment": contactmoment.url}
)
data = get_json_response(response)
all_data = list(pagination_helper(self, data))
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return []

object_contact_momenten = factory(ObjectContactMoment, all_data)

# resolve linked resources
object_mapping = {}
for ocm in object_contact_momenten:
assert ocm.contactmoment == contactmoment.url
ocm.contactmoment = contactmoment
if ocm.object_type == "zaak":
object_url = ocm.object
# Avoid fetching the same object, if multiple relations wit the same object exist
if ocm.object in object_mapping:
ocm.object = object_mapping[object_url]
else:
ocm.object = fetch_case_by_url_no_cache(ocm.object)
object_mapping[object_url] = ocm.object

return object_contact_momenten

def get_klantcontactmomenten_for_klant(
self, klant: Klant
) -> List[KlantContactMoment]:
try:
response = self.get(
"klantcontactmomenten",
params={"klant": klant.url},
)
data = get_json_response(response)
all_data = list(pagination_helper(self, data))
except (RequestException, ClientError) as e:
logger.exception("exception while making request", exc_info=e)
return []

klanten_contact_moments = factory(KlantContactMoment, all_data)

# resolve linked resources
for kcm in klanten_contact_moments:
assert kcm.klant == klant.url
kcm.klant = klant
kcm.contactmoment = self.get_contactmoment(kcm.contactmoment)

return klanten_contact_moments

def get_objectcontactmomenten_for_object_type(
self, contactmoment: ContactMoment, object_type: str
) -> List[ObjectContactMoment]:

moments = self.get_objectcontactmomenten_for_contactmoment(contactmoment)

# eSuite doesn't implement a `object_type` query parameter
ret = [moment for moment in moments if moment.object_type == object_type]

return ret

def get_objectcontactmoment(
self, contactmoment: ContactMoment, object_type: str
) -> Optional[ObjectContactMoment]:
ocms = self.get_objectcontactmomenten_for_object_type(
contactmoment, object_type
)
if ocms:
return ocms[0]


def build_client(type_) -> Optional[APIClient]:
config = OpenKlantConfig.get_solo()
services = {
"klanten",
"contactmomenten",
services_to_client_mapping = {
"klanten": KlantenClient,
"contactmomenten": ContactmomentenClient,
}
if type_ in services:
if client_class := services_to_client_mapping.get(type_):
service = getattr(config, f"{type_}_service")
if service:
client = _build_client(service, client_factory=JSONParserClient)
client = _build_client(service, client_factory=client_class)
return client

logger.warning(f"no service defined for {type_}")
Expand Down
Loading

0 comments on commit dc54e66

Please sign in to comment.