Skip to content

Commit 90f4827

Browse files
feat(frontend): display message on unauthorized query
1 parent f05b2c5 commit 90f4827

File tree

4 files changed

+84
-5
lines changed

4 files changed

+84
-5
lines changed

frontend/workflows-lib/lib/components/workflow/WorkflowErrorBoundaryWithRetry.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Typography } from "@mui/material";
22
import React, { Component, ReactNode } from "react";
3+
import { formatErrorMessage } from "workflows-lib/lib/utils/commonUtils";
34

45
interface ErrorBoundaryProps {
56
children: (props: { fetchKey: number }) => ReactNode;
@@ -14,6 +15,7 @@ interface ErrorBoundaryState {
1415
error: Error | null;
1516
fetchKey: number;
1617
retryCount: number;
18+
isUnauthorized: boolean;
1719
}
1820

1921
class WorkflowErrorBoundaryWithRetry extends Component<
@@ -29,6 +31,7 @@ class WorkflowErrorBoundaryWithRetry extends Component<
2931
error: null,
3032
fetchKey: 0,
3133
retryCount: 0,
34+
isUnauthorized: false,
3235
};
3336
}
3437

@@ -46,9 +49,16 @@ class WorkflowErrorBoundaryWithRetry extends Component<
4649
errorInfo,
4750
);
4851

52+
if (error.message.includes("Unauthorized")) {
53+
this.setState((prev) => ({
54+
...prev,
55+
isUnauthorized: true,
56+
}));
57+
}
58+
4959
const maxRetries = this.props.maxRetries ?? 2;
5060

51-
if (this.state.retryCount < maxRetries) {
61+
if (!this.state.isUnauthorized && this.state.retryCount < maxRetries) {
5262
this.retryTimeoutId = setTimeout(
5363
() => {
5464
this._retry();
@@ -75,17 +85,18 @@ class WorkflowErrorBoundaryWithRetry extends Component<
7585

7686
render() {
7787
const { children, fallback, maxRetries = 2 } = this.props;
78-
const { hasError, error, fetchKey, retryCount } = this.state;
88+
const { hasError, error, fetchKey, retryCount, isUnauthorized } =
89+
this.state;
7990

8091
if (hasError && error) {
81-
if (retryCount >= maxRetries) {
92+
if (isUnauthorized || retryCount >= maxRetries) {
8293
if (typeof fallback === "function") {
8394
return fallback({ error, retry: this._retry });
8495
}
8596
return (
8697
fallback || (
8798
<Typography variant="h6" fontWeight="bold">
88-
Something went wrong with the GraphQL query.
99+
{formatErrorMessage(error.message)}
89100
</Typography>
90101
)
91102
);

frontend/workflows-lib/lib/utils/commonUtils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,13 @@ export function templateSourceToLink(
5454
}
5555
return repo + "/tree/" + rev + "/" + path;
5656
}
57+
58+
export function formatErrorMessage(errorMessage: string | undefined) {
59+
const defaultMessage = "Something went wrong with the GraphQL query.";
60+
const authWorkflowsMessage = "You are not authorised to access this visit.";
61+
const authTemplateMessage = "You are not authorised to access the templates.";
62+
63+
if (errorMessage?.includes("Template")) return authTemplateMessage;
64+
if (errorMessage?.includes("Workflow")) return authWorkflowsMessage;
65+
return defaultMessage;
66+
}

frontend/workflows-lib/tests/components/WorkflowErrorBoundaryWithRetry.test.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { render, waitFor } from "@testing-library/react";
1+
import { render, screen, waitFor } from "@testing-library/react";
22
import "@testing-library/jest-dom";
33
import { describe, it, expect } from "vitest";
44
import WorkflowErrorBoundaryWithRetry from "../../lib/components/workflow/WorkflowErrorBoundaryWithRetry";
5+
import * as commonUtils from "../../lib/utils/commonUtils";
56

67
describe("WorkflowsErrorBoundaryWithRetry", () => {
78
it("renders with children", () => {
@@ -60,4 +61,25 @@ describe("WorkflowsErrorBoundaryWithRetry", () => {
6061
{ timeout: 4000 },
6162
);
6263
});
64+
65+
it("does not retry for an unauthorised query", () => {
66+
const TestAuthError = () => {
67+
throw new Error(
68+
"No data returned for operation `TemplatesListViewQuery`, got error(s): Unauthorized",
69+
);
70+
};
71+
72+
const formatMessage = vi.spyOn(commonUtils, "formatErrorMessage");
73+
74+
render(
75+
<WorkflowErrorBoundaryWithRetry>
76+
{() => <TestAuthError />}
77+
</WorkflowErrorBoundaryWithRetry>,
78+
);
79+
80+
expect(
81+
screen.queryByText("Trying to Refetch Data..."),
82+
).not.toBeInTheDocument();
83+
expect(formatMessage).toHaveBeenCalled();
84+
});
6385
});

frontend/workflows-lib/tests/functions/commonUtils.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Visit } from "@diamondlightsource/sci-react-ui";
22
import {
33
visitTextToVisit,
44
parseVisitAndTemplate,
5+
formatErrorMessage,
56
} from "../../lib/utils/commonUtils";
67

78
describe("visitTextToVisit", () => {
@@ -86,3 +87,38 @@ describe("parseVisitAndTemplate", () => {
8687
},
8788
);
8889
});
90+
91+
describe("formatErrorMessage", () => {
92+
it("should return an auth error for templates", () => {
93+
const input =
94+
"No data returned for operation `TemplatesListViewQuery`, got error(s): Unauthorized";
95+
expect(formatErrorMessage(input)).toBe(
96+
"You are not authorised to access the templates.",
97+
);
98+
});
99+
100+
it("should return a template auth error for WorkflowsListViewTemplatesQuery", () => {
101+
const input =
102+
"No data returned for operation `WorkflowsListViewTemplatesQuery`, got error(s): Unauthorized";
103+
expect(formatErrorMessage(input)).toBe(
104+
"You are not authorised to access the templates.",
105+
);
106+
});
107+
108+
it("should return an auth error for workflows", () => {
109+
const input =
110+
"No data returned for operation `WorkflowsListViewQuery`, got error(s): Unauthorized";
111+
expect(formatErrorMessage(input)).toBe(
112+
"You are not authorised to access this visit.",
113+
);
114+
});
115+
116+
test.each(["Unknown error", "", undefined])(
117+
"%s should return a default message",
118+
(input) => {
119+
expect(formatErrorMessage(input)).toBe(
120+
"Something went wrong with the GraphQL query.",
121+
);
122+
},
123+
);
124+
});

0 commit comments

Comments
 (0)