From 963b7d70fe304337711240f6e9e1198ae78bcf5e Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Mon, 15 Jun 2026 14:06:54 +0000 Subject: [PATCH 1/5] fix terraform error and pass handler bool for dry runs --- applications/sharepoint_renamer/handler.py | 4 ++- .../sharepoint_renamer_request.py | 5 +++ .../lambda/sharepoint_renamer/variables.tf | 5 +++ .../sharepoint_renamer_orchestrator.py | 32 ++++++++++++------- tests/scripts/test_rename_sharepoint_files.py | 24 +++++++++++++- 5 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 applications/sharepoint_renamer/sharepoint_renamer_request.py diff --git a/applications/sharepoint_renamer/handler.py b/applications/sharepoint_renamer/handler.py index 998458bc..67e64cf7 100644 --- a/applications/sharepoint_renamer/handler.py +++ b/applications/sharepoint_renamer/handler.py @@ -1,5 +1,6 @@ from typing import Any +from applications.sharepoint_renamer.sharepoint_renamer_request import SharepointRenamerRequest from orchestration.sharepoint_renamer_orchestrator import SharepointRenamerOrchestrator from utils.sharepoint.domna_sharepoint_client import DomnaSharepointClient from utils.sharepoint.domna_sites import DomnaSites @@ -8,6 +9,7 @@ CSV_PATH = "applications/sharepoint_renamer/sero_address_list.csv" def handler(event: dict[str, Any], context: Any) -> None: + request = SharepointRenamerRequest.model_validate(event) sp_client = DomnaSharepointClient(DomnaSites.SOCIAL_HOUSING_WAVE_3) - orchestrator = SharepointRenamerOrchestrator(sp_client, CSV_PATH) + orchestrator = SharepointRenamerOrchestrator(sp_client, CSV_PATH, dry_run=request.dry_run) orchestrator.run() diff --git a/applications/sharepoint_renamer/sharepoint_renamer_request.py b/applications/sharepoint_renamer/sharepoint_renamer_request.py new file mode 100644 index 00000000..6a10447b --- /dev/null +++ b/applications/sharepoint_renamer/sharepoint_renamer_request.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class SharepointRenamerRequest(BaseModel): + dry_run: bool = False diff --git a/deployment/terraform/lambda/sharepoint_renamer/variables.tf b/deployment/terraform/lambda/sharepoint_renamer/variables.tf index 97cca538..192dbafa 100644 --- a/deployment/terraform/lambda/sharepoint_renamer/variables.tf +++ b/deployment/terraform/lambda/sharepoint_renamer/variables.tf @@ -1,3 +1,8 @@ +variable "lambda_name" { + type = string + description = "Logical name of the lambda (e.g. sharepoint_renamer)" +} + variable "stage" { description = "Deployment stage (e.g. dev, prod)" type = string diff --git a/orchestration/sharepoint_renamer_orchestrator.py b/orchestration/sharepoint_renamer_orchestrator.py index 764776ae..b73c41b5 100644 --- a/orchestration/sharepoint_renamer_orchestrator.py +++ b/orchestration/sharepoint_renamer_orchestrator.py @@ -1,9 +1,9 @@ import csv -import logging import os from typing import Optional from backend.pashub_fetcher.sharepoint_subfolders import SharepointSubfolders +from utilities.logger import setup_logger from utils.sharepoint.domna_sharepoint_client import DomnaSharepointClient BASE_PATH = ( @@ -12,7 +12,7 @@ BASE_PATH = ( ) ASSESSMENT_SUBFOLDER = "A. Assessment" -logger = logging.getLogger(__name__) +logger = setup_logger() def build_canonical_filename( @@ -58,9 +58,12 @@ def build_canonical_filename( class SharepointRenamerOrchestrator: - def __init__(self, sp_client: DomnaSharepointClient, csv_path: str) -> None: + def __init__( + self, sp_client: DomnaSharepointClient, csv_path: str, dry_run: bool = False + ) -> None: self._sp_client = sp_client self._csv_path = csv_path + self._dry_run = dry_run def run(self) -> None: with open(self._csv_path, newline="", encoding="utf-8-sig") as f: @@ -97,17 +100,24 @@ class SharepointRenamerOrchestrator: ) elif "file" in item: original_name: str = item["name"] - new_name = build_canonical_filename(uprn, address, postcode, original_name) + new_name = build_canonical_filename( + uprn, address, postcode, original_name + ) if new_name is None: continue - try: - self._sp_client.rename_file(item["id"], new_name) + if self._dry_run: logger.info( - f'Renamed: "{original_name}" → "{new_name}" (UPRN: {uprn})' - ) - except Exception as e: - logger.error( - f'Failed to rename "{original_name}" → "{new_name}" (UPRN: {uprn}): {e}' + f'Would rename: "{original_name}" → "{new_name}" (UPRN: {uprn})' ) + else: + try: + self._sp_client.rename_file(item["id"], new_name) + logger.info( + f'Renamed: "{original_name}" → "{new_name}" (UPRN: {uprn})' + ) + except Exception as e: + logger.error( + f'Failed to rename "{original_name}" → "{new_name}" (UPRN: {uprn}): {e}' + ) diff --git a/tests/scripts/test_rename_sharepoint_files.py b/tests/scripts/test_rename_sharepoint_files.py index 7b3e6587..5affea7e 100644 --- a/tests/scripts/test_rename_sharepoint_files.py +++ b/tests/scripts/test_rename_sharepoint_files.py @@ -21,9 +21,10 @@ def _make_package(name: str) -> dict[str, Any]: return {"name": name, "package": {}} -def _make_orchestrator(sp: MagicMock) -> SharepointRenamerOrchestrator: +def _make_orchestrator(sp: MagicMock, dry_run: bool = False) -> SharepointRenamerOrchestrator: orchestrator = SharepointRenamerOrchestrator.__new__(SharepointRenamerOrchestrator) orchestrator._sp_client = sp + orchestrator._dry_run = dry_run return orchestrator @@ -144,3 +145,24 @@ def test_skips_already_canonical_files() -> None: _make_orchestrator(sp)._process_folder("some/path", "500", "5 Pine Ln", "BB3 3CC") sp.rename_file.assert_not_called() + + +# --------------------------------------------------------------------------- +# _process_folder — dry_run=True logs intent but never calls rename_file +# --------------------------------------------------------------------------- + + +def test_dry_run_logs_would_rename_without_calling_api( + caplog: pytest.LogCaptureFixture, +) -> None: + sp = MagicMock() + sp.get_folders_in_path.return_value = { + "value": [_make_file("Survey.pdf", "id-1")] + } + + _make_orchestrator(sp, dry_run=True)._process_folder( + "some/path", "100", "1 High St", "AB1 2CD" + ) + + sp.rename_file.assert_not_called() + assert any("Would rename" in r.message for r in caplog.records) From 8b27a5fda22cf3b3977e9bc212eaa1655477628d Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Mon, 15 Jun 2026 14:08:40 +0000 Subject: [PATCH 2/5] correct lambda name --- deployment/terraform/lambda/sharepoint_renamer/variables.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/terraform/lambda/sharepoint_renamer/variables.tf b/deployment/terraform/lambda/sharepoint_renamer/variables.tf index 192dbafa..79b1a8d4 100644 --- a/deployment/terraform/lambda/sharepoint_renamer/variables.tf +++ b/deployment/terraform/lambda/sharepoint_renamer/variables.tf @@ -1,6 +1,6 @@ variable "lambda_name" { type = string - description = "Logical name of the lambda (e.g. sharepoint_renamer)" + description = "sharepoint_renamer" } variable "stage" { From b31db4b58b9f7ef7b4614b0b62f4e718e163c840 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Mon, 15 Jun 2026 14:29:04 +0000 Subject: [PATCH 3/5] correct Dockerfile imports --- applications/sharepoint_renamer/handler/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/sharepoint_renamer/handler/Dockerfile b/applications/sharepoint_renamer/handler/Dockerfile index a81294f9..6a0a28b4 100644 --- a/applications/sharepoint_renamer/handler/Dockerfile +++ b/applications/sharepoint_renamer/handler/Dockerfile @@ -6,6 +6,7 @@ COPY applications/sharepoint_renamer/handler/requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY utils/ utils/ +COPY utilities/ utilities/ COPY backend/__init__.py backend/__init__.py COPY backend/pashub_fetcher/ backend/pashub_fetcher/ COPY orchestration/ orchestration/ From 9b21cc55126a21d84e3f147d44db2ca3ca983394 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Mon, 15 Jun 2026 14:52:48 +0000 Subject: [PATCH 4/5] remove breaking init file --- applications/sharepoint_renamer/handler/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 applications/sharepoint_renamer/handler/__init__.py diff --git a/applications/sharepoint_renamer/handler/__init__.py b/applications/sharepoint_renamer/handler/__init__.py deleted file mode 100644 index e69de29b..00000000 From 03dc0a3eef98b5a7c24fb7213af8fe18541fed94 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Mon, 15 Jun 2026 15:03:07 +0000 Subject: [PATCH 5/5] add local handler and missing requirement --- applications/sharepoint_renamer/handler.py | 4 ++++ .../sharepoint_renamer/handler/requirements.txt | 1 + .../local_handler/docker-compose.yml | 9 +++++++++ .../local_handler/invoke_local_lambda.py | 15 +++++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 applications/sharepoint_renamer/local_handler/docker-compose.yml create mode 100644 applications/sharepoint_renamer/local_handler/invoke_local_lambda.py diff --git a/applications/sharepoint_renamer/handler.py b/applications/sharepoint_renamer/handler.py index 67e64cf7..d6094ca3 100644 --- a/applications/sharepoint_renamer/handler.py +++ b/applications/sharepoint_renamer/handler.py @@ -13,3 +13,7 @@ def handler(event: dict[str, Any], context: Any) -> None: sp_client = DomnaSharepointClient(DomnaSites.SOCIAL_HOUSING_WAVE_3) orchestrator = SharepointRenamerOrchestrator(sp_client, CSV_PATH, dry_run=request.dry_run) orchestrator.run() + + +if __name__ == "__main__": + handler({"dry_run": True}, None) diff --git a/applications/sharepoint_renamer/handler/requirements.txt b/applications/sharepoint_renamer/handler/requirements.txt index 94317b81..6b7cf3ba 100644 --- a/applications/sharepoint_renamer/handler/requirements.txt +++ b/applications/sharepoint_renamer/handler/requirements.txt @@ -1,2 +1,3 @@ msal requests +pydantic-settings==2.6.0 diff --git a/applications/sharepoint_renamer/local_handler/docker-compose.yml b/applications/sharepoint_renamer/local_handler/docker-compose.yml new file mode 100644 index 00000000..9c448307 --- /dev/null +++ b/applications/sharepoint_renamer/local_handler/docker-compose.yml @@ -0,0 +1,9 @@ +services: + sharepoint-renamer: + build: + context: ../../../ + dockerfile: applications/sharepoint_renamer/handler/Dockerfile + ports: + - "9003:8080" + env_file: + - ../../../.env diff --git a/applications/sharepoint_renamer/local_handler/invoke_local_lambda.py b/applications/sharepoint_renamer/local_handler/invoke_local_lambda.py new file mode 100644 index 00000000..073808eb --- /dev/null +++ b/applications/sharepoint_renamer/local_handler/invoke_local_lambda.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +import requests + +HOST = "localhost" +PORT = "9003" + +LAMBDA_URL = f"http://{HOST}:{PORT}/2015-03-31/functions/function/invocations" + +payload = {"dry_run": True} + +response = requests.post(LAMBDA_URL, json=payload) + +print("Status code:", response.status_code) +print("Response:") +print(response.text)