diff --git a/backend/magic_plan/handler.py b/backend/magic_plan/handler.py index f2c03b90..5fd90b7a 100644 --- a/backend/magic_plan/handler.py +++ b/backend/magic_plan/handler.py @@ -20,7 +20,9 @@ def handler(body: dict[str, Any], context: Any) -> str: api_key=settings.MAGICPLAN_API_KEY, ) # TODO: read s3_bucket from env var so staging/prod use the correct bucket - plan: Plan = MagicPlanService(client, s3_bucket="retrofit-energy-assessments-dev").run(payload) + plan: Plan = MagicPlanService( + client, s3_bucket="retrofit-energy-assessments-dev" + ).run(payload) logger.info("Saved MagicPlan plan uid=%s", plan.uid) return plan.uid @@ -30,7 +32,6 @@ if __name__ == "__main__": "Records": [ { "body": '{"address": "2 Laburnum Way Bromley BR2 8BZ", "hubspot_deal_id": "local-test-deal"}', - "messageId": "local-test", } ] } diff --git a/backend/magic_plan/handler/requirements.txt b/backend/magic_plan/handler/requirements.txt index cfacf455..29123caa 100644 --- a/backend/magic_plan/handler/requirements.txt +++ b/backend/magic_plan/handler/requirements.txt @@ -5,3 +5,7 @@ sqlmodel psycopg2-binary==2.9.10 pydantic-settings==2.6.0 boto3==1.35.44 + +pytz==2024.2 +pandas==2.2.2 +numpy==2.1.2 diff --git a/backend/magic_plan/local_handler/docker-compose.yml b/backend/magic_plan/local_handler/docker-compose.yml new file mode 100644 index 00000000..5a42d259 --- /dev/null +++ b/backend/magic_plan/local_handler/docker-compose.yml @@ -0,0 +1,11 @@ +version: "3.9" + +services: + ecmk-fetcher-lambda: + build: + context: ../../../ + dockerfile: backend/magic_plan/handler/Dockerfile + ports: + - "9000:8080" + env_file: + - ../../../.env \ No newline at end of file diff --git a/backend/magic_plan/local_handler/invoke_local_lambda.py b/backend/magic_plan/local_handler/invoke_local_lambda.py new file mode 100644 index 00000000..146951fe --- /dev/null +++ b/backend/magic_plan/local_handler/invoke_local_lambda.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +import json +import requests + +HOST = "localhost" +PORT = "9000" + +LAMBDA_URL = f"http://{HOST}:{PORT}/2015-03-31/functions/function/invocations" + +payload = { + "Records": [ + { + "messageId": "test-message-id", + "body": json.dumps( + # { + # "address": "2 Laburnum Way, Rombley, BR2 8BZ | Retrofit Assessment", + # "hubspot_deal_id": "500262906061", + # } + {"address": "33 Wallaby Way, Sydney", "hubspot_deal_id": "123456789"} + ), + } + ] +} + +response = requests.post(LAMBDA_URL, json=payload) + +print("Status code:", response.status_code) +print("Response:") +print(response.text) diff --git a/backend/magic_plan/magic_plan_client.py b/backend/magic_plan/magic_plan_client.py index 06905e6a..2880bf43 100644 --- a/backend/magic_plan/magic_plan_client.py +++ b/backend/magic_plan/magic_plan_client.py @@ -7,12 +7,11 @@ _BASE_URL = "https://cloud.magicplan.app/api/v2" class MagicPlanClient: def __init__(self, customer_id: str, api_key: str) -> None: - self._api_key = api_key self._session = requests.Session() - self._session.headers.update({"customer": customer_id}) + self._session.headers.update({"customer": customer_id, "key": api_key}) def get_plans(self) -> PlansListResponse: - r = self._session.get(f"{_BASE_URL}/plans", params={"key": self._api_key}) + r = self._session.get(f"{_BASE_URL}/plans") r.raise_for_status() return PlansListResponse.model_validate(r.json()["data"]) @@ -23,8 +22,6 @@ class MagicPlanClient: return self._fetch_plan(plan_id).content def _fetch_plan(self, plan_id: str) -> requests.Response: - r = self._session.get( - f"{_BASE_URL}/plans/{plan_id}", params={"key": self._api_key} - ) + r = self._session.get(f"{_BASE_URL}/plans/{plan_id}") r.raise_for_status() return r diff --git a/backend/magic_plan/tests/test_handler.py b/backend/magic_plan/tests/test_handler.py index 366f3ded..b0365f5b 100644 --- a/backend/magic_plan/tests/test_handler.py +++ b/backend/magic_plan/tests/test_handler.py @@ -54,7 +54,7 @@ def test_handler_raises_on_missing_address(mock_plan: MagicMock) -> None: def test_handler_constructs_client_from_settings(mock_service: MagicMock) -> None: # Arrange - body = {"address": ADDRESS} + body = {"address": ADDRESS, "hubspot_deal_id": "deal-123"} with patch("backend.magic_plan.handler.get_settings", return_value=_make_settings(customer_id="cust-xyz", api_key="key-xyz")), \ patch("backend.magic_plan.handler.MagicPlanClient") as MockClient, \ patch("backend.magic_plan.handler.MagicPlanService", return_value=mock_service): @@ -69,31 +69,37 @@ def test_handler_constructs_client_from_settings(mock_service: MagicMock) -> Non def test_handler_calls_service_run_with_address(mock_service: MagicMock) -> None: # Arrange - body = {"address": ADDRESS} + body = {"address": ADDRESS, "hubspot_deal_id": "deal-123"} with patch("backend.magic_plan.handler.get_settings", return_value=_make_settings()), \ patch("backend.magic_plan.handler.MagicPlanClient"), \ patch("backend.magic_plan.handler.MagicPlanService", return_value=mock_service): # Act _call_handler(body) # Assert - mock_service.run.assert_called_once_with(ADDRESS, None) + mock_service.run.assert_called_once() + request = mock_service.run.call_args.args[0] + assert request.address == ADDRESS + assert request.uprn is None def test_handler_passes_uprn_to_service(mock_service: MagicMock) -> None: # Arrange - body = {"address": ADDRESS, "uprn": "100023336956"} + body = {"address": ADDRESS, "uprn": "100023336956", "hubspot_deal_id": "deal-123"} with patch("backend.magic_plan.handler.get_settings", return_value=_make_settings()), \ patch("backend.magic_plan.handler.MagicPlanClient"), \ patch("backend.magic_plan.handler.MagicPlanService", return_value=mock_service): # Act _call_handler(body) # Assert - mock_service.run.assert_called_once_with(ADDRESS, "100023336956") + mock_service.run.assert_called_once() + request = mock_service.run.call_args.args[0] + assert request.address == ADDRESS + assert request.uprn == "100023336956" def test_handler_returns_plan_uid(mock_service: MagicMock) -> None: # Arrange - body = {"address": ADDRESS} + body = {"address": ADDRESS, "hubspot_deal_id": "deal-123"} with patch("backend.magic_plan.handler.get_settings", return_value=_make_settings()), \ patch("backend.magic_plan.handler.MagicPlanClient"), \ patch("backend.magic_plan.handler.MagicPlanService", return_value=mock_service): diff --git a/backend/magic_plan/tests/test_magic_plan_client.py b/backend/magic_plan/tests/test_magic_plan_client.py index c96b9cdf..a0827bee 100644 --- a/backend/magic_plan/tests/test_magic_plan_client.py +++ b/backend/magic_plan/tests/test_magic_plan_client.py @@ -20,6 +20,7 @@ def _load_fixture(name: str) -> dict[str, Any]: def _make_client(mock_session: MagicMock) -> MagicPlanClient: + mock_session.headers = {} with patch( "backend.magic_plan.magic_plan_client.requests.Session", return_value=mock_session, @@ -44,7 +45,14 @@ def test_customer_header_set_on_session(mock_session: MagicMock) -> None: # Act _make_client(mock_session) # Assert - mock_session.headers.update.assert_called_once_with({"customer": CUSTOMER_ID}) + assert mock_session.headers["customer"] == CUSTOMER_ID + + +def test_api_key_header_set_on_session(mock_session: MagicMock) -> None: + # Act + _make_client(mock_session) + # Assert + assert mock_session.headers["key"] == API_KEY # --- get_plans --- @@ -62,9 +70,7 @@ def test_get_plans_calls_correct_url( # Act client.get_plans() # Assert - mock_session.get.assert_called_once_with( - f"{BASE_URL}/plans", params={"key": API_KEY} - ) + mock_session.get.assert_called_once_with(f"{BASE_URL}/plans") def test_get_plans_calls_raise_for_status( @@ -126,9 +132,7 @@ def test_get_plan_calls_correct_url( # Act client.get_plan(plan_id) # Assert - mock_session.get.assert_called_once_with( - f"{BASE_URL}/plans/{plan_id}", params={"key": API_KEY} - ) + mock_session.get.assert_called_once_with(f"{BASE_URL}/plans/{plan_id}") def test_get_plan_calls_raise_for_status( @@ -198,9 +202,7 @@ def test_get_plan_raw_calls_correct_url( # Act client.get_plan_raw(plan_id) # Assert - mock_session.get.assert_called_once_with( - f"{BASE_URL}/plans/{plan_id}", params={"key": API_KEY} - ) + mock_session.get.assert_called_once_with(f"{BASE_URL}/plans/{plan_id}") def test_get_plan_raw_calls_raise_for_status(