mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Merge pull request #1074 from Hestia-Homes/feature/magicplan-trigger
Magicplan service: Correct query URLs and implement pagination in plans response
This commit is contained in:
commit
272bfbde13
4 changed files with 62 additions and 29 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from datatypes.magicplan.api.response import MagicPlanPlan, PlansListResponse
|
from datatypes.magicplan.api.response import MagicPlanPlan, PlanSummary, PlansListResponse
|
||||||
|
|
||||||
_BASE_URL = "https://cloud.magicplan.app/api/v2"
|
_BASE_URL = "https://cloud.magicplan.app/api/v2"
|
||||||
|
|
||||||
|
|
@ -10,10 +10,18 @@ class MagicPlanClient:
|
||||||
self._session = requests.Session()
|
self._session = requests.Session()
|
||||||
self._session.headers.update({"customer": customer_id, "key": api_key})
|
self._session.headers.update({"customer": customer_id, "key": api_key})
|
||||||
|
|
||||||
def get_plans(self) -> PlansListResponse:
|
def get_plans(self) -> list[PlanSummary]:
|
||||||
r = self._session.get(f"{_BASE_URL}/plans")
|
all_plans: list[PlanSummary] = []
|
||||||
r.raise_for_status()
|
page = 1
|
||||||
return PlansListResponse.model_validate(r.json()["data"])
|
while True:
|
||||||
|
r = self._session.get(f"{_BASE_URL}/workgroups/plans", params={"page": page})
|
||||||
|
r.raise_for_status()
|
||||||
|
response = PlansListResponse.model_validate(r.json()["data"])
|
||||||
|
all_plans.extend(response.plans)
|
||||||
|
if not response.paging.next_page:
|
||||||
|
break
|
||||||
|
page += 1
|
||||||
|
return all_plans
|
||||||
|
|
||||||
def get_plan(self, plan_id: str) -> MagicPlanPlan:
|
def get_plan(self, plan_id: str) -> MagicPlanPlan:
|
||||||
return MagicPlanPlan.model_validate(self._fetch_plan(plan_id).json()["data"])
|
return MagicPlanPlan.model_validate(self._fetch_plan(plan_id).json()["data"])
|
||||||
|
|
@ -22,6 +30,6 @@ class MagicPlanClient:
|
||||||
return self._fetch_plan(plan_id).content
|
return self._fetch_plan(plan_id).content
|
||||||
|
|
||||||
def _fetch_plan(self, plan_id: str) -> requests.Response:
|
def _fetch_plan(self, plan_id: str) -> requests.Response:
|
||||||
r = self._session.get(f"{_BASE_URL}/plans/{plan_id}")
|
r = self._session.get(f"{_BASE_URL}/plans/get/{plan_id}")
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
return r
|
return r
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,7 @@ import json
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from datatypes.magicplan.api.response import (
|
from datatypes.magicplan.api.response import MagicPlanPlan, PlanSummary
|
||||||
MagicPlanPlan,
|
|
||||||
PlanSummary,
|
|
||||||
PlansListResponse,
|
|
||||||
)
|
|
||||||
from datatypes.magicplan.domain.mapper import map_plan
|
from datatypes.magicplan.domain.mapper import map_plan
|
||||||
from datatypes.magicplan.domain.models import Plan
|
from datatypes.magicplan.domain.models import Plan
|
||||||
|
|
||||||
|
|
@ -39,10 +35,8 @@ class MagicPlanService:
|
||||||
if uprn is not None:
|
if uprn is not None:
|
||||||
logger.info("MagicPlanService.run uprn=%s", uprn)
|
logger.info("MagicPlanService.run uprn=%s", uprn)
|
||||||
|
|
||||||
plans_response: PlansListResponse = self._client.get_plans()
|
plans: list[PlanSummary] = self._client.get_plans()
|
||||||
matched: Optional[PlanSummary] = find_matching_plan(
|
matched: Optional[PlanSummary] = find_matching_plan(plans, address)
|
||||||
plans_response.plans, address
|
|
||||||
)
|
|
||||||
|
|
||||||
if matched is None:
|
if matched is None:
|
||||||
raise ValueError(f"No MagicPlan found for address: {address!r}")
|
raise ValueError(f"No MagicPlan found for address: {address!r}")
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import pytest
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from backend.magic_plan.magic_plan_client import MagicPlanClient
|
from backend.magic_plan.magic_plan_client import MagicPlanClient
|
||||||
from datatypes.magicplan.api.response import MagicPlanPlan, PlansListResponse
|
from datatypes.magicplan.api.response import MagicPlanPlan, PlanSummary
|
||||||
|
|
||||||
FIXTURE_DIR = Path(__file__).parents[2] / "magic_plan"
|
FIXTURE_DIR = Path(__file__).parents[2] / "magic_plan"
|
||||||
BASE_URL = "https://cloud.magicplan.app/api/v2"
|
BASE_URL = "https://cloud.magicplan.app/api/v2"
|
||||||
|
|
@ -70,7 +70,9 @@ def test_get_plans_calls_correct_url(
|
||||||
# Act
|
# Act
|
||||||
client.get_plans()
|
client.get_plans()
|
||||||
# Assert
|
# Assert
|
||||||
mock_session.get.assert_called_once_with(f"{BASE_URL}/plans")
|
mock_session.get.assert_called_once_with(
|
||||||
|
f"{BASE_URL}/workgroups/plans", params={"page": 1}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_get_plans_calls_raise_for_status(
|
def test_get_plans_calls_raise_for_status(
|
||||||
|
|
@ -88,7 +90,7 @@ def test_get_plans_calls_raise_for_status(
|
||||||
mock_session.get.return_value.raise_for_status.assert_called_once()
|
mock_session.get.return_value.raise_for_status.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
def test_get_plans_returns_plans_list_response(
|
def test_get_plans_returns_list_of_plan_summaries(
|
||||||
client: MagicPlanClient, mock_session: MagicMock
|
client: MagicPlanClient, mock_session: MagicMock
|
||||||
) -> None:
|
) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
@ -100,8 +102,9 @@ def test_get_plans_returns_plans_list_response(
|
||||||
# Act
|
# Act
|
||||||
result = client.get_plans()
|
result = client.get_plans()
|
||||||
# Assert
|
# Assert
|
||||||
assert isinstance(result, PlansListResponse)
|
assert isinstance(result, list)
|
||||||
assert len(result.plans) == 1
|
assert len(result) == 1
|
||||||
|
assert isinstance(result[0], PlanSummary)
|
||||||
|
|
||||||
|
|
||||||
def test_get_plans_propagates_http_error(
|
def test_get_plans_propagates_http_error(
|
||||||
|
|
@ -116,6 +119,34 @@ def test_get_plans_propagates_http_error(
|
||||||
client.get_plans()
|
client.get_plans()
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_plans_multi_page_fetches_all_pages(
|
||||||
|
client: MagicPlanClient, mock_session: MagicMock
|
||||||
|
) -> None:
|
||||||
|
# Arrange
|
||||||
|
page1_plan = _load_fixture("magicplan_api_plans_response_example.json")["data"][
|
||||||
|
"plans"
|
||||||
|
][0]
|
||||||
|
page2_plan = {**page1_plan, "id": "page-2-plan-id"}
|
||||||
|
page1_response = MagicMock()
|
||||||
|
page1_response.json.return_value = {
|
||||||
|
"data": {"paging": {"page": 1, "next_page": True, "count": 2}, "plans": [page1_plan]}
|
||||||
|
}
|
||||||
|
page2_response = MagicMock()
|
||||||
|
page2_response.json.return_value = {
|
||||||
|
"data": {"paging": {"page": 2, "next_page": False, "count": 2}, "plans": [page2_plan]}
|
||||||
|
}
|
||||||
|
mock_session.get.side_effect = [page1_response, page2_response]
|
||||||
|
# Act
|
||||||
|
result = client.get_plans()
|
||||||
|
# Assert
|
||||||
|
assert mock_session.get.call_count == 2
|
||||||
|
mock_session.get.assert_any_call(f"{BASE_URL}/workgroups/plans", params={"page": 1})
|
||||||
|
mock_session.get.assert_any_call(f"{BASE_URL}/workgroups/plans", params={"page": 2})
|
||||||
|
assert len(result) == 2
|
||||||
|
assert result[0].id == page1_plan["id"]
|
||||||
|
assert result[1].id == "page-2-plan-id"
|
||||||
|
|
||||||
|
|
||||||
# --- get_plan ---
|
# --- get_plan ---
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -132,7 +163,7 @@ def test_get_plan_calls_correct_url(
|
||||||
# Act
|
# Act
|
||||||
client.get_plan(plan_id)
|
client.get_plan(plan_id)
|
||||||
# Assert
|
# Assert
|
||||||
mock_session.get.assert_called_once_with(f"{BASE_URL}/plans/{plan_id}")
|
mock_session.get.assert_called_once_with(f"{BASE_URL}/plans/get/{plan_id}")
|
||||||
|
|
||||||
|
|
||||||
def test_get_plan_calls_raise_for_status(
|
def test_get_plan_calls_raise_for_status(
|
||||||
|
|
@ -202,7 +233,7 @@ def test_get_plan_raw_calls_correct_url(
|
||||||
# Act
|
# Act
|
||||||
client.get_plan_raw(plan_id)
|
client.get_plan_raw(plan_id)
|
||||||
# Assert
|
# Assert
|
||||||
mock_session.get.assert_called_once_with(f"{BASE_URL}/plans/{plan_id}")
|
mock_session.get.assert_called_once_with(f"{BASE_URL}/plans/get/{plan_id}")
|
||||||
|
|
||||||
|
|
||||||
def test_get_plan_raw_calls_raise_for_status(
|
def test_get_plan_raw_calls_raise_for_status(
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ def test_run_fetches_plan_with_matched_id(
|
||||||
domain_plan: Plan,
|
domain_plan: Plan,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
mock_client.get_plans.return_value.plans = [plan_summary]
|
mock_client.get_plans.return_value = [plan_summary]
|
||||||
mock_client.get_plan.return_value = api_magic_plan
|
mock_client.get_plan.return_value = api_magic_plan
|
||||||
service = _make_service(mock_client)
|
service = _make_service(mock_client)
|
||||||
with patch(
|
with patch(
|
||||||
|
|
@ -114,7 +114,7 @@ def test_run_returns_mapped_plan(
|
||||||
domain_plan: Plan,
|
domain_plan: Plan,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
mock_client.get_plans.return_value.plans = [plan_summary]
|
mock_client.get_plans.return_value = [plan_summary]
|
||||||
mock_client.get_plan.return_value = api_magic_plan
|
mock_client.get_plan.return_value = api_magic_plan
|
||||||
service = _make_service(mock_client)
|
service = _make_service(mock_client)
|
||||||
with patch(
|
with patch(
|
||||||
|
|
@ -137,7 +137,7 @@ def test_run_calls_save_plan_with_mapped_plan(
|
||||||
plan_summary: PlanSummary,
|
plan_summary: PlanSummary,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
mock_client.get_plans.return_value.plans = [plan_summary]
|
mock_client.get_plans.return_value = [plan_summary]
|
||||||
mock_client.get_plan.return_value = api_magic_plan
|
mock_client.get_plan.return_value = api_magic_plan
|
||||||
service = _make_service(mock_client)
|
service = _make_service(mock_client)
|
||||||
with patch(
|
with patch(
|
||||||
|
|
@ -161,7 +161,7 @@ def test_run_accepts_uprn_without_error(
|
||||||
plan_summary: PlanSummary,
|
plan_summary: PlanSummary,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
mock_client.get_plans.return_value.plans = [plan_summary]
|
mock_client.get_plans.return_value = [plan_summary]
|
||||||
mock_client.get_plan.return_value = api_magic_plan
|
mock_client.get_plan.return_value = api_magic_plan
|
||||||
service = _make_service(mock_client)
|
service = _make_service(mock_client)
|
||||||
with patch(
|
with patch(
|
||||||
|
|
@ -184,7 +184,7 @@ def test_run_uploads_to_s3_with_uprn_key(
|
||||||
plan_summary: PlanSummary,
|
plan_summary: PlanSummary,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
mock_client.get_plans.return_value.plans = [plan_summary]
|
mock_client.get_plans.return_value = [plan_summary]
|
||||||
request = _make_request(uprn="100023336956")
|
request = _make_request(uprn="100023336956")
|
||||||
service = MagicPlanService(client=mock_client, s3_bucket=S3_BUCKET)
|
service = MagicPlanService(client=mock_client, s3_bucket=S3_BUCKET)
|
||||||
with patch(
|
with patch(
|
||||||
|
|
@ -211,7 +211,7 @@ def test_run_uploads_to_s3_with_deal_id_key_when_uprn_absent(
|
||||||
plan_summary: PlanSummary,
|
plan_summary: PlanSummary,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
mock_client.get_plans.return_value.plans = [plan_summary]
|
mock_client.get_plans.return_value = [plan_summary]
|
||||||
mock_client.get_plan.return_value = api_magic_plan
|
mock_client.get_plan.return_value = api_magic_plan
|
||||||
request = _make_request(hubspot_deal_id="deal-456", uprn=None)
|
request = _make_request(hubspot_deal_id="deal-456", uprn=None)
|
||||||
service = MagicPlanService(client=mock_client, s3_bucket=S3_BUCKET)
|
service = MagicPlanService(client=mock_client, s3_bucket=S3_BUCKET)
|
||||||
|
|
@ -242,7 +242,7 @@ def test_run_creates_uploaded_file_record(
|
||||||
plan_summary: PlanSummary,
|
plan_summary: PlanSummary,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
mock_client.get_plans.return_value.plans = [plan_summary]
|
mock_client.get_plans.return_value = [plan_summary]
|
||||||
mock_client.get_plan.return_value = api_magic_plan
|
mock_client.get_plan.return_value = api_magic_plan
|
||||||
request = _make_request(hubspot_deal_id="deal-789", uprn="100023336956")
|
request = _make_request(hubspot_deal_id="deal-789", uprn="100023336956")
|
||||||
service = MagicPlanService(client=mock_client, s3_bucket=S3_BUCKET)
|
service = MagicPlanService(client=mock_client, s3_bucket=S3_BUCKET)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue