Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1913162
Update dockerfile
keiranjprice101 Mar 2, 2026
9087236
temp build push
keiranjprice101 Mar 2, 2026
34dc2bf
Add middleware to remove trailing slashes and update Dockerfile CMD
keiranjprice101 Mar 2, 2026
66144b6
Formatting and linting commit
invalid-email-address Mar 2, 2026
e15f0e4
Update path trimming logic to handle /live endpoint
keiranjprice101 Mar 2, 2026
730ab58
Update middleware to specify `call_next` type annotation
keiranjprice101 Mar 2, 2026
2729c4b
Formatting and linting commit
invalid-email-address Mar 2, 2026
6bd74ed
Refine path trimming logic to handle specific "livereduce" paths
keiranjprice101 Mar 2, 2026
9ed0213
Add logging to middleware for trailing slash removal
keiranjprice101 Mar 2, 2026
e7f57bc
Filter out /healthz and /ready logs from Uvicorn access logger
keiranjprice101 Mar 2, 2026
dcf831f
Formatting and linting commit
invalid-email-address Mar 2, 2026
24220ab
Deduplicate and relocate trailing slash removal middleware
keiranjprice101 Mar 2, 2026
954240e
Formatting and linting commit
invalid-email-address Mar 2, 2026
0b2d791
Refine trailing slash removal logic by removing "livereduce" path con…
keiranjprice101 Mar 3, 2026
c084cd3
Remove temp build push
keiranjprice101 Mar 3, 2026
472ada6
change case
keiranjprice101 Mar 3, 2026
d17511a
Update yarn.lock to reflect dependency updates and align with latest …
keiranjprice101 Mar 3, 2026
80d3f55
temp build push
keiranjprice101 Mar 3, 2026
23d20a2
Update Node.js version to 25 in workflow and Dockerfile
keiranjprice101 Mar 3, 2026
4258c73
Update Cypress import to use react instead of react18
keiranjprice101 Mar 3, 2026
b56fe85
Update tsconfig: switch to react-jsx and adjust includes/excludes
keiranjprice101 Mar 3, 2026
ab6b967
Make `DataPage` async and update `params` type; improve `NexusViewer`…
keiranjprice101 Mar 3, 2026
3127350
Remove unused imports and commented-out middleware logic
keiranjprice101 Mar 5, 2026
ed08c23
Update build-push workflow: enable branch-specific push trigger
keiranjprice101 Mar 5, 2026
79369cc
Make `DataPage` async and refactor `params` destructuring logic for c…
keiranjprice101 Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 18
node-version: 25

- name: Install python dependencies
run: |
Expand Down
2 changes: 1 addition & 1 deletion data-viewer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:24-alpine AS base
FROM node:25-alpine AS base

FROM base AS deps
RUN apk add --no-cache libc6-compat
Expand Down
4 changes: 2 additions & 2 deletions data-viewer/cypress/e2e/basic-loading/loading.cy.js
Original file line number Diff line number Diff line change
@@ -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 },
);
});
Expand All @@ -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 },
);
});
Expand Down
2 changes: 1 addition & 1 deletion data-viewer/cypress/support/component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "./commands";

import { mount } from "cypress/react18";
import { mount } from "cypress/react";

declare global {
namespace Cypress {
Expand Down
32 changes: 16 additions & 16 deletions data-viewer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,24 @@
"lint": "next lint"
},
"dependencies": {
"@emotion/react": "11.13.3",
"@emotion/styled": "11.13.0",
"@h5web/app": "^12.0.1",
"@mui/material": "6.1.4",
"@mui/styled-engine-sc": "6.1.4",
"next": "14.2.30",
"react": "^18",
"react-dom": "^18",
"styled-components": "6.1.13"
"@emotion/react": "11.14.0",
"@emotion/styled": "11.14.1",
"@h5web/app": "^16.0.1",
"@mui/material": "7.3.8",
"@mui/styled-engine-sc": "7.3.8",
"next": "14.2.35",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"styled-components": "6.3.11"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18.3.11",
"@types/react-dom": "^18",
"cypress": "^13.15.0",
"eslint": "^8",
"eslint-config-next": "14.2.5",
"prettier": "^3.3.3",
"@types/node": "^25",
"@types/react": "^19.2.14",
"@types/react-dom": "^19",
"cypress": "^15.11.0",
"eslint": "^10",
"eslint-config-next": "16.1.6",
"prettier": "^3.8.1",
"typescript": "^5"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,24 @@ import NexusViewer from "@/components/NexusViewer";
import "../../../../globals.css";
import TextViewer from "@/components/TextViewer";

export default function DataPage({
export default async function DataPage({
params,
}: {
params: { instrument: string; experimentNumber: string; filename: string };
params: Promise<{
instrument: string;
experimentNumber: string;
filename: string;
}>;
}) {
// We expect a route of /instrument_name/experiment_number/filename
// This will result in a slug list of [instrument_name, experiment_number, filename]
const { instrument, experimentNumber, filename } = params;

const { instrument, experimentNumber, filename } = await params;

const fileExtension = filename.split(".").pop() ?? "";
const apiUrl = process.env.API_URL ?? "http://localhost:8000";
// Files to be viewed with the TextViewer, as text files
const textFiles: Array<string> = ["txt", "csv", "gss", "abc", "prm"];

return (
<main className="h5-container">
{textFiles.includes(fileExtension) ? (
Expand Down
187 changes: 95 additions & 92 deletions data-viewer/src/components/NexusViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,106 +1,109 @@
"use client";
import "@h5web/app/dist/styles.css";
import { App, H5GroveProvider } from "@h5web/app";
import { useEffect, useState } from "react";
import { App, H5GroveProvider, createBasicFetcher } from "@h5web/app";
import { useEffect, useState, useMemo } from "react";
import { ErrorBoundary } from "react-error-boundary";
import { CircularProgress, Stack } from "@mui/material";
import { FileQueryUrl } from "@/components/utils/FileQueryUrl";
import { Fallback } from "@/components/utils/FallbackPage";

export default function NexusViewer(props: {
filename: string;
apiUrl: string;
instrument?: string;
experimentNumber?: string;
userNumber?: string;
filename: string;
apiUrl: string;
instrument?: string;
experimentNumber?: string;
userNumber?: string;
}) {
// We need to turn the env var into a full url as the h5provider can not take just the route.
// Typically, we expect API_URL env var to be /plottingapi in staging and production
const [filepath, setFilePath] = useState<string>("");
const [token, setToken] = useState<string>("");
const [loading, setLoading] = useState<boolean>(true);
const [groveApiUrl, setApiUrl] = useState<string>(props.apiUrl);
// We need to turn the env var into a full url as the h5provider can not take just the route.
// Typically, we expect API_URL env var to be /plottingapi in staging and production
const [filepath, setFilePath] = useState<string>("");
const [token, setToken] = useState<string>("");
const [loading, setLoading] = useState<boolean>(true);
const [groveApiUrl, setApiUrl] = useState<string>(props.apiUrl);

useEffect(() => {
setLoading(true);
const loadedToken = localStorage.getItem("scigateway:token") ?? "";
setToken(loadedToken);
setApiUrl(
props.apiUrl.includes("localhost")
? props.apiUrl
: `${window.location.protocol}//${window.location.hostname}/plottingapi`,
);
useEffect(() => {
setLoading(true);
const loadedToken = localStorage.getItem("scigateway:token") ?? "";
setToken(loadedToken);
setApiUrl(
props.apiUrl.includes("localhost")
? props.apiUrl
: `${window.location.protocol}//${window.location.hostname}/plottingapi`,
);

const fileQueryUrl = FileQueryUrl(
props.apiUrl,
props.instrument,
props.experimentNumber,
props.userNumber,
);
if (fileQueryUrl == null) {
throw new Error(
"The API file query URL was not rendered correctly and returned null",
);
}
const fileQueryUrl = FileQueryUrl(
props.apiUrl,
props.instrument,
props.experimentNumber,
props.userNumber,
);
if (fileQueryUrl == null) {
throw new Error(
"The API file query URL was not rendered correctly and returned null",
);
}

const fileQueryParams = `filename=${props.filename}`;
const headers: { [key: string]: string } = {
"Content-Type": "application/json",
};
if (loadedToken != "") {
headers["Authorization"] = `Bearer ${loadedToken}`;
}
const fileQueryParams = `filename=${props.filename}`;
const headers: { [key: string]: string } = {
"Content-Type": "application/json",
};
if (loadedToken != "") {
headers["Authorization"] = `Bearer ${loadedToken}`;
}

fetch(`${fileQueryUrl}?${fileQueryParams}`, { method: "GET", headers })
.then((res) => res.text())
.then((data) => {
const filepath_to_use = data
.split("%20")
.join(" ")
.split("%C")
.join(",")
.replace(/"/g, "");
setFilePath(filepath_to_use);
})
.catch((error) => Error(error))
.finally(() => setLoading(false));
}, [
props.apiUrl,
props.instrument,
props.experimentNumber,
props.userNumber,
props.filename,
]);
fetch(`${fileQueryUrl}?${fileQueryParams}`, { method: "GET", headers })
.then((res) => res.text())
.then((data) => {
const filepath_to_use = data
.split("%20")
.join(" ")
.split("%C")
.join(",")
.replace(/"/g, "");
setFilePath(filepath_to_use);
})
.catch((error) => Error(error))
.finally(() => setLoading(false));
}, [
props.apiUrl,
props.instrument,
props.experimentNumber,
props.userNumber,
props.filename,
]);

return (
<ErrorBoundary FallbackComponent={Fallback}>
{loading ? (
<Stack
spacing={2}
sx={{
justifyContent: "center",
alignItems: "center",
height: "100%",
width: "100%",
}}
>
<p>Finding your file</p>
<CircularProgress />
</Stack>
) : (
<H5GroveProvider
url={groveApiUrl}
filepath={filepath}
axiosConfig={{
params: { file: filepath },
headers: {
Authorization: `Bearer ${token}`,
},
}}
>
<App propagateErrors />
</H5GroveProvider>
)}
</ErrorBoundary>
);
}
const fetcher = useMemo(() => {
const headers: Record<string, string> = {};
if (token) {
headers["Authorization"] = `Bearer ${token}`;
}
return createBasicFetcher({ headers });
}, [token]);

return (
<ErrorBoundary FallbackComponent={Fallback}>
{loading ? (
<Stack
spacing={2}
sx={{
justifyContent: "center",
alignItems: "center",
height: "100%",
width: "100%",
}}
>
<p>Finding your file</p>
<CircularProgress />
</Stack>
) : (
<H5GroveProvider
url={groveApiUrl}
filepath={filepath}
fetcher={fetcher}
>
<App propagateErrors />
</H5GroveProvider>
)}
</ErrorBoundary>
);
}
7 changes: 5 additions & 2 deletions data-viewer/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
"node_modules",
"cypress",
"cypress.config.ts"
]
}
Loading
Loading