From 1913162d1e6ccd269eee442ac99230786023416b Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Mon, 2 Mar 2026 10:57:22 +0000 Subject: [PATCH 01/16] Update dockerfile --- plotting-service/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotting-service/Dockerfile b/plotting-service/Dockerfile index f2b6c6bd..7148dc19 100644 --- a/plotting-service/Dockerfile +++ b/plotting-service/Dockerfile @@ -7,4 +7,4 @@ COPY . /plotting-service RUN python -m pip install --upgrade pip \ && python -m pip install --no-cache-dir . -CMD ["uvicorn", "plotting_service.plotting_api:app", "--host", "0.0.0.0", "--port", "80"] \ No newline at end of file +CMD ["uvicorn", "plotting_service.plotting_api:app", "--host", "0.0.0.0", "--port", "80", "--root-path", "/plottingapi"] \ No newline at end of file From 90872365ee40b4f221e5de9781fbcc7774dde291 Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Mon, 2 Mar 2026 10:58:09 +0000 Subject: [PATCH 02/16] temp build push --- .github/workflows/build-push.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index 5f429679..b03a8172 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -1,10 +1,10 @@ --- name: Build and Push Docker Images -on: - push: - branches: - - main +on: push +# push: +# branches: +# - main env: REGISTRY: ghcr.io From 34dc2bfd8ddcc19132bb53c29c6b8d22ec8f547c Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Mon, 2 Mar 2026 11:05:08 +0000 Subject: [PATCH 03/16] Add middleware to remove trailing slashes and update Dockerfile CMD --- plotting-service/Dockerfile | 2 +- .../plotting_service/plotting_api.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/plotting-service/Dockerfile b/plotting-service/Dockerfile index 7148dc19..f2b6c6bd 100644 --- a/plotting-service/Dockerfile +++ b/plotting-service/Dockerfile @@ -7,4 +7,4 @@ COPY . /plotting-service RUN python -m pip install --upgrade pip \ && python -m pip install --no-cache-dir . -CMD ["uvicorn", "plotting_service.plotting_api:app", "--host", "0.0.0.0", "--port", "80", "--root-path", "/plottingapi"] \ No newline at end of file +CMD ["uvicorn", "plotting_service.plotting_api:app", "--host", "0.0.0.0", "--port", "80"] \ No newline at end of file diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index 555136a0..50536405 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -8,6 +8,7 @@ from pathlib import Path from fastapi import FastAPI, HTTPException +from fastapi.openapi.models import Response from h5grove.fastapi_utils import router, settings # type: ignore from starlette.middleware.cors import CORSMiddleware from starlette.requests import Request @@ -185,6 +186,21 @@ async def check_live_permissions(request: Request, call_next: typing.Callable[.. logger.warning(f"User {user.user_number} denied access to live experiment {current_rb_int}") raise HTTPException(HTTPStatus.FORBIDDEN, detail="Forbidden: You do not have access to the current live experiment") +@app.middleware("http") +async def remove_trailing_slash(request: Request, call_next) -> Response: + path = request.scope.get("path", "") + + # If the path ends with a slash (and isn't just the root "/"), strip it + if path != "/" and path.endswith("/"): + new_path = path.rstrip("/") + + request.scope["path"] = new_path + + if "raw_path" in request.scope: + request.scope["raw_path"] = new_path.encode("utf-8") + + return await call_next(request) + app.include_router(router) app.include_router(HealthRouter) From 66144b63d87b3dfac12da6215d8ed74151e73fd4 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 2 Mar 2026 11:05:40 +0000 Subject: [PATCH 04/16] Formatting and linting commit --- plotting-service/plotting_service/plotting_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index 50536405..32ce2d38 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -186,6 +186,7 @@ async def check_live_permissions(request: Request, call_next: typing.Callable[.. logger.warning(f"User {user.user_number} denied access to live experiment {current_rb_int}") raise HTTPException(HTTPStatus.FORBIDDEN, detail="Forbidden: You do not have access to the current live experiment") + @app.middleware("http") async def remove_trailing_slash(request: Request, call_next) -> Response: path = request.scope.get("path", "") From e15f0e4ae68ed0414cc4b21dcea4818c89c8ba0c Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Mon, 2 Mar 2026 11:12:53 +0000 Subject: [PATCH 05/16] Update path trimming logic to handle /live endpoint --- plotting-service/plotting_service/plotting_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index 32ce2d38..44dfa45a 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -192,7 +192,7 @@ async def remove_trailing_slash(request: Request, call_next) -> Response: path = request.scope.get("path", "") # If the path ends with a slash (and isn't just the root "/"), strip it - if path != "/" and path.endswith("/"): + if path.startswith("/live") and path.endswith("/"): new_path = path.rstrip("/") request.scope["path"] = new_path From 730ab58adc1d5f7dc85ff086be2e92bc88558329 Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Mon, 2 Mar 2026 11:13:47 +0000 Subject: [PATCH 06/16] Update middleware to specify `call_next` type annotation --- plotting-service/plotting_service/plotting_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index 44dfa45a..392d1c84 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -188,7 +188,7 @@ async def check_live_permissions(request: Request, call_next: typing.Callable[.. @app.middleware("http") -async def remove_trailing_slash(request: Request, call_next) -> Response: +async def remove_trailing_slash(request: Request, call_next:typing.Callable[..., typing.Any]) -> Response: path = request.scope.get("path", "") # If the path ends with a slash (and isn't just the root "/"), strip it From 2729c4be7f6c3b6b9a3cf13bb7e430974830f345 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 2 Mar 2026 11:14:17 +0000 Subject: [PATCH 07/16] Formatting and linting commit --- plotting-service/plotting_service/plotting_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index 392d1c84..0d77f7e2 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -188,7 +188,7 @@ async def check_live_permissions(request: Request, call_next: typing.Callable[.. @app.middleware("http") -async def remove_trailing_slash(request: Request, call_next:typing.Callable[..., typing.Any]) -> Response: +async def remove_trailing_slash(request: Request, call_next: typing.Callable[..., typing.Any]) -> Response: path = request.scope.get("path", "") # If the path ends with a slash (and isn't just the root "/"), strip it From 6bd74ed24839885278bf019c4e3812255e28f668 Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Mon, 2 Mar 2026 11:30:59 +0000 Subject: [PATCH 08/16] Refine path trimming logic to handle specific "livereduce" paths --- plotting-service/plotting_service/plotting_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index 0d77f7e2..28ad4c69 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -192,7 +192,7 @@ async def remove_trailing_slash(request: Request, call_next: typing.Callable[... path = request.scope.get("path", "") # If the path ends with a slash (and isn't just the root "/"), strip it - if path.startswith("/live") and path.endswith("/"): + if path != "/" and path.endswith("/") and "livereduce" in path.lower(): new_path = path.rstrip("/") request.scope["path"] = new_path From 9ed021365df36561bc531e72dd997e97e2f317ea Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Mon, 2 Mar 2026 11:36:23 +0000 Subject: [PATCH 09/16] Add logging to middleware for trailing slash removal --- plotting-service/plotting_service/plotting_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index 28ad4c69..c9ea9498 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -190,7 +190,7 @@ async def check_live_permissions(request: Request, call_next: typing.Callable[.. @app.middleware("http") async def remove_trailing_slash(request: Request, call_next: typing.Callable[..., typing.Any]) -> Response: path = request.scope.get("path", "") - + logger.info(f"Removing trailing slash from path: {path}") # If the path ends with a slash (and isn't just the root "/"), strip it if path != "/" and path.endswith("/") and "livereduce" in path.lower(): new_path = path.rstrip("/") From e7f57bc52af9c883accadd5d119bdb86e940dad9 Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Mon, 2 Mar 2026 11:46:40 +0000 Subject: [PATCH 10/16] Filter out /healthz and /ready logs from Uvicorn access logger --- plotting-service/plotting_service/plotting_api.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index c9ea9498..ea9092f4 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -33,6 +33,16 @@ logger = logging.getLogger(__name__) logger.info("Starting Plotting Service") +class EndpointFilter(logging.Filter): + """Filter out log messages containing /healthz or /ready.""" + + def filter(self, record: logging.LogRecord) -> bool: + """Filter out log messages containing /healthz or /ready.""" + return record.getMessage().find("/healthz") == -1 and record.getMessage().find("/ready") == -1 + + +logging.getLogger("uvicorn.access").addFilter(EndpointFilter()) + DEV_MODE = os.environ.get("DEV_MODE", "False").lower() == "true" if DEV_MODE: logger.info("Development only mode") From dcf831f109be3e64080a8d00c4c1b0ea8c9e6e1d Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 2 Mar 2026 11:47:11 +0000 Subject: [PATCH 11/16] Formatting and linting commit --- plotting-service/plotting_service/plotting_api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index ea9092f4..3157728e 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -33,6 +33,7 @@ logger = logging.getLogger(__name__) logger.info("Starting Plotting Service") + class EndpointFilter(logging.Filter): """Filter out log messages containing /healthz or /ready.""" From 24220ab754acb4a0baa437058ff306c77aade597 Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Mon, 2 Mar 2026 11:52:50 +0000 Subject: [PATCH 12/16] Deduplicate and relocate trailing slash removal middleware --- .../plotting_service/plotting_api.py | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index 3157728e..e34abb8e 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -66,6 +66,21 @@ def filter(self, record: logging.LogRecord) -> bool: settings.base_dir = Path(CEPH_DIR).resolve() +@app.middleware("http") +async def remove_trailing_slash(request: Request, call_next: typing.Callable[..., typing.Any]) -> Response: + path = request.scope.get("path", "") + logger.info(f"Removing trailing slash from path: {path}") + # If the path ends with a slash (and isn't just the root "/"), strip it + if path != "/" and path.endswith("/") and "livereduce" in path.lower(): + new_path = path.rstrip("/") + + request.scope["path"] = new_path + + if "raw_path" in request.scope: + request.scope["raw_path"] = new_path.encode("utf-8") + + return await call_next(request) + @app.middleware("http") async def check_permissions(request: Request, call_next: typing.Callable[..., typing.Any]) -> typing.Any: # noqa: C901, PLR0911 """Middleware that checks the requestee token has permissions for that @@ -198,20 +213,7 @@ async def check_live_permissions(request: Request, call_next: typing.Callable[.. raise HTTPException(HTTPStatus.FORBIDDEN, detail="Forbidden: You do not have access to the current live experiment") -@app.middleware("http") -async def remove_trailing_slash(request: Request, call_next: typing.Callable[..., typing.Any]) -> Response: - path = request.scope.get("path", "") - logger.info(f"Removing trailing slash from path: {path}") - # If the path ends with a slash (and isn't just the root "/"), strip it - if path != "/" and path.endswith("/") and "livereduce" in path.lower(): - new_path = path.rstrip("/") - - request.scope["path"] = new_path - - if "raw_path" in request.scope: - request.scope["raw_path"] = new_path.encode("utf-8") - return await call_next(request) app.include_router(router) From 954240e03cda9f9e03693c2feea43ef6404a30e7 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 2 Mar 2026 11:53:56 +0000 Subject: [PATCH 13/16] Formatting and linting commit --- plotting-service/plotting_service/plotting_api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index e34abb8e..a56a4346 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -81,6 +81,7 @@ async def remove_trailing_slash(request: Request, call_next: typing.Callable[... return await call_next(request) + @app.middleware("http") async def check_permissions(request: Request, call_next: typing.Callable[..., typing.Any]) -> typing.Any: # noqa: C901, PLR0911 """Middleware that checks the requestee token has permissions for that @@ -213,9 +214,6 @@ async def check_live_permissions(request: Request, call_next: typing.Callable[.. raise HTTPException(HTTPStatus.FORBIDDEN, detail="Forbidden: You do not have access to the current live experiment") - - - app.include_router(router) app.include_router(HealthRouter) app.include_router(PlottingRouter) From 0b2d791fe6c723bf11c048ec690df3883229b024 Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Tue, 3 Mar 2026 10:41:40 +0000 Subject: [PATCH 14/16] Refine trailing slash removal logic by removing "livereduce" path condition --- plotting-service/plotting_service/plotting_api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plotting-service/plotting_service/plotting_api.py b/plotting-service/plotting_service/plotting_api.py index a56a4346..824e82d5 100644 --- a/plotting-service/plotting_service/plotting_api.py +++ b/plotting-service/plotting_service/plotting_api.py @@ -69,9 +69,8 @@ def filter(self, record: logging.LogRecord) -> bool: @app.middleware("http") async def remove_trailing_slash(request: Request, call_next: typing.Callable[..., typing.Any]) -> Response: path = request.scope.get("path", "") - logger.info(f"Removing trailing slash from path: {path}") # If the path ends with a slash (and isn't just the root "/"), strip it - if path != "/" and path.endswith("/") and "livereduce" in path.lower(): + if path != "/" and path.endswith("/"): new_path = path.rstrip("/") request.scope["path"] = new_path From c084cd357342fb439cae45fb3107df0d3db9f6ff Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Tue, 3 Mar 2026 10:51:27 +0000 Subject: [PATCH 15/16] Remove temp build push --- .github/workflows/build-push.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index b03a8172..5f429679 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -1,10 +1,10 @@ --- name: Build and Push Docker Images -on: push -# push: -# branches: -# - main +on: + push: + branches: + - main env: REGISTRY: ghcr.io From 472ada67a4bd67d3d50638a65f57a34f0cf0b082 Mon Sep 17 00:00:00 2001 From: Keiran Price Date: Tue, 3 Mar 2026 11:25:27 +0000 Subject: [PATCH 16/16] change case --- data-viewer/cypress/e2e/basic-loading/loading.cy.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data-viewer/cypress/e2e/basic-loading/loading.cy.js b/data-viewer/cypress/e2e/basic-loading/loading.cy.js index f7c2a6c5..ef35d7ec 100644 --- a/data-viewer/cypress/e2e/basic-loading/loading.cy.js +++ b/data-viewer/cypress/e2e/basic-loading/loading.cy.js @@ -1,7 +1,7 @@ describe("Basic loading tests for test nexus file", () => { beforeEach(() => { cy.visit( - "http://localhost:3000/view/mari/20024/MAR29531_10.5meV_sa.nxspe", + "http://localhost:3000/view/MARI/20024/MAR29531_10.5meV_sa.nxspe", { failOnStatusCode: false }, ); }); @@ -18,7 +18,7 @@ describe("Test for loading nexus file with space in name", () => { beforeEach(() => { // This URL DOES have a whitespace, but the underline in most IDEs makes it look like an underscore cy.visit( - "http://localhost:3000/view/mari/20024/MAR29531 10.5meV_sa.nxspe", + "http://localhost:3000/view/MARI/20024/MAR29531 10.5meV_sa.nxspe", { failOnStatusCode: false }, ); });