diff --git a/commcare_connect/connect_id_client/__init__.py b/commcare_connect/connect_id_client/__init__.py new file mode 100644 index 00000000..eef3dd24 --- /dev/null +++ b/commcare_connect/connect_id_client/__init__.py @@ -0,0 +1 @@ +from .main import fetch_users # noqa: F401 diff --git a/commcare_connect/connect_id_client/main.py b/commcare_connect/connect_id_client/main.py new file mode 100644 index 00000000..0d12851c --- /dev/null +++ b/commcare_connect/connect_id_client/main.py @@ -0,0 +1,30 @@ +import httpx +from django.conf import settings +from httpx import BasicAuth, Response + +from commcare_connect.connect_id_client.models import ConnectIdUser + +GET = "GET" +POST = "POST" + + +def fetch_users(phone_number_list) -> list[ConnectIdUser]: + response = _make_request(GET, "/users/fetch_users", params={"phone_numbers": phone_number_list}) + data = response.json() + return [ConnectIdUser(**user_dict) for user_dict in data["found_users"]] + + +def _make_request(method, path, params=None, json=None) -> Response: + if json and not method == "POST": + raise ValueError("json can only be used with POST requests") + + auth = BasicAuth(settings.CONNECTID_CLIENT_ID, settings.CONNECTID_CLIENT_SECRET) + response = httpx.request( + method, + f"{settings.CONNECTID_URL}{path}", + params=params, + json=json, + auth=auth, + ) + response.raise_for_status() + return response diff --git a/commcare_connect/connect_id_client/models.py b/commcare_connect/connect_id_client/models.py new file mode 100644 index 00000000..3e76f3d6 --- /dev/null +++ b/commcare_connect/connect_id_client/models.py @@ -0,0 +1,11 @@ +import dataclasses + + +@dataclasses.dataclass +class ConnectIdUser: + name: str + username: str + phone_number: str + + def __str__(self) -> str: + return f"{self.name} ({self.username})" diff --git a/commcare_connect/connect_id_client/tests.py b/commcare_connect/connect_id_client/tests.py new file mode 100644 index 00000000..9761a4ef --- /dev/null +++ b/commcare_connect/connect_id_client/tests.py @@ -0,0 +1,25 @@ +from .main import fetch_users + + +def test_fetch_users(httpx_mock): + httpx_mock.add_response( + json={ + "found_users": [ + { + "username": "user_name1", + "name": "name1", + "phone_number": "phone_number1", + }, + { + "name": "name2", + "username": "user_name2", + "phone_number": "phone_number2", + }, + ] + } + ) + + users = fetch_users(["phone_number1", "phone_number2"]) + assert len(users) == 2 + assert users[0].name == "name1" + assert users[1].name == "name2" diff --git a/commcare_connect/opportunity/tasks.py b/commcare_connect/opportunity/tasks.py index 7233fbec..6bce0ba8 100644 --- a/commcare_connect/opportunity/tasks.py +++ b/commcare_connect/opportunity/tasks.py @@ -1,9 +1,8 @@ -import requests -from django.conf import settings from django.core.files.base import ContentFile from django.core.files.storage import default_storage from django.utils.timezone import now +from commcare_connect.connect_id_client import fetch_users from commcare_connect.opportunity.app_xml import get_connect_blocks_for_app from commcare_connect.opportunity.export import export_empty_payment_table, export_user_visit_data from commcare_connect.opportunity.forms import DateRanges @@ -33,15 +32,9 @@ def create_learn_modules_assessments(opportunity_id): @celery_app.task() def add_connect_users(user_list: list[str], opportunity_id: str): - result = requests.get( - f"{settings.CONNECTID_URL}/users/fetch_users", - auth=(settings.CONNECTID_CLIENT_ID, settings.CONNECTID_CLIENT_SECRET), - params={"phone_numbers": user_list}, - ) - data = result.json() - for user in data["found_users"]: + for user in fetch_users(user_list): u, _ = User.objects.update_or_create( - username=user["username"], defaults={"phone_number": user["phone_number"], "name": user["name"]} + username=user.username, defaults={"phone_number": user.phone_number, "name": user.name} ) opportunity_access, _ = OpportunityAccess.objects.get_or_create(user=u, opportunity_id=opportunity_id) invite_user(u, opportunity_access)