Skip to content

Commit 2810d37

Browse files
committed
Enhance error handling in all package version routes
1 parent de2572f commit 2810d37

File tree

2 files changed

+307
-190
lines changed

2 files changed

+307
-190
lines changed

apps/cyberstorm-remix/app/p/packageVersion.tsx

Lines changed: 147 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ import {
66
} from "@fortawesome/free-solid-svg-icons";
77
import { faArrowUpRight } from "@fortawesome/pro-solid-svg-icons";
88
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
9+
import { getPublicEnvVariables } from "cyberstorm/security/publicEnvVariables";
910
import {
10-
getPublicEnvVariables,
11-
getSessionTools,
12-
} from "cyberstorm/security/publicEnvVariables";
11+
NimbusAwaitErrorElement,
12+
NimbusDefaultRouteErrorBoundary,
13+
} from "cyberstorm/utils/errors/NimbusErrorBoundary";
14+
import { handleLoaderError } from "cyberstorm/utils/errors/handleLoaderError";
15+
import { createNotFoundMapping } from "cyberstorm/utils/errors/loaderMappings";
16+
import { throwUserFacingPayloadResponse } from "cyberstorm/utils/errors/userFacingErrorResponse";
17+
import { getLoaderTools } from "cyberstorm/utils/getLoaderTools";
1318
import { isPromise } from "cyberstorm/utils/typeChecks";
1419
import {
1520
type ReactElement,
@@ -51,13 +56,15 @@ import {
5156
formatInteger,
5257
formatToDisplayName,
5358
} from "@thunderstore/cyberstorm";
54-
import {
55-
DapperTs,
56-
getPackageVersionDetails,
57-
getTeamDetails,
58-
} from "@thunderstore/dapper-ts";
59+
import { getPackageVersionDetails } from "@thunderstore/dapper-ts/src/methods/packageVersion";
60+
import { getTeamDetails } from "@thunderstore/dapper-ts/src/methods/team";
5961

60-
import "./packageListing.css";
62+
const packageVersionNotFoundMappings = [
63+
createNotFoundMapping(
64+
"Package version not found.",
65+
"We could not find the requested package version."
66+
),
67+
];
6168

6269
export async function loader({ params }: LoaderFunctionArgs) {
6370
if (
@@ -66,55 +73,81 @@ export async function loader({ params }: LoaderFunctionArgs) {
6673
params.packageId &&
6774
params.packageVersion
6875
) {
69-
const publicEnvVariables = getPublicEnvVariables(["VITE_API_URL"]);
70-
const dapper = new DapperTs(() => {
76+
const { dapper } = getLoaderTools();
77+
try {
78+
const [community, version, team] = await Promise.all([
79+
dapper.getCommunity(params.communityId),
80+
dapper.getPackageVersionDetails(
81+
params.namespaceId,
82+
params.packageId,
83+
params.packageVersion
84+
),
85+
dapper.getTeamDetails(params.namespaceId),
86+
]);
87+
7188
return {
72-
apiHost: publicEnvVariables.VITE_API_URL,
73-
sessionId: undefined,
89+
communityId: params.communityId,
90+
community,
91+
version,
92+
team,
7493
};
75-
});
76-
77-
return {
78-
communityId: params.communityId,
79-
community: await dapper.getCommunity(params.communityId),
80-
version: await dapper.getPackageVersionDetails(
81-
params.namespaceId,
82-
params.packageId,
83-
params.packageVersion
84-
),
85-
team: await dapper.getTeamDetails(params.namespaceId),
86-
};
94+
} catch (error) {
95+
handleLoaderError(error, { mappings: packageVersionNotFoundMappings });
96+
}
8797
}
88-
throw new Response("Package not found", { status: 404 });
98+
throwUserFacingPayloadResponse({
99+
headline: "Package not found.",
100+
description: "We could not find the requested package.",
101+
category: "not_found",
102+
status: 404,
103+
});
89104
}
90105

91-
export async function clientLoader({ params }: LoaderFunctionArgs) {
106+
export function clientLoader({ params }: LoaderFunctionArgs) {
92107
if (
93108
params.communityId &&
94109
params.namespaceId &&
95110
params.packageId &&
96111
params.packageVersion
97112
) {
98-
const tools = getSessionTools();
99-
const dapper = new DapperTs(() => {
100-
return {
101-
apiHost: tools?.getConfig().apiHost,
102-
sessionId: tools?.getConfig().sessionId,
103-
};
104-
});
105-
106-
return {
107-
communityId: params.communityId,
108-
community: dapper.getCommunity(params.communityId),
109-
version: dapper.getPackageVersionDetails(
113+
const { dapper } = getLoaderTools();
114+
const community = dapper
115+
.getCommunity(params.communityId)
116+
.catch((error) =>
117+
handleLoaderError(error, { mappings: packageVersionNotFoundMappings })
118+
);
119+
const version = dapper
120+
.getPackageVersionDetails(
110121
params.namespaceId,
111122
params.packageId,
112123
params.packageVersion
113-
),
114-
team: dapper.getTeamDetails(params.namespaceId),
124+
)
125+
.catch((error) =>
126+
handleLoaderError(error, { mappings: packageVersionNotFoundMappings })
127+
);
128+
const team = dapper
129+
.getTeamDetails(params.namespaceId)
130+
.catch((error) =>
131+
handleLoaderError(error, { mappings: packageVersionNotFoundMappings })
132+
);
133+
134+
return {
135+
communityId: params.communityId,
136+
community,
137+
version,
138+
team,
115139
};
116140
}
117-
throw new Response("Package not found", { status: 404 });
141+
throwUserFacingPayloadResponse({
142+
headline: "Package not found.",
143+
description: "We could not find the requested package.",
144+
category: "not_found",
145+
status: 404,
146+
});
147+
}
148+
149+
export function ErrorBoundary() {
150+
return <NimbusDefaultRouteErrorBoundary />;
118151
}
119152

120153
export function shouldRevalidate(arg: ShouldRevalidateFunctionArgs) {
@@ -154,39 +187,65 @@ export default function PackageVersion() {
154187
// If strict mode is removed from the entry.client.tsx, this should only run once
155188
useEffect(() => {
156189
if (!startsHydrated.current && isHydrated) return;
157-
if (isPromise(version)) {
158-
version.then((versionData) => {
159-
setFirstUploaded(
160-
<RelativeTime
161-
time={versionData.datetime_created}
162-
suppressHydrationWarning
163-
/>
164-
);
165-
});
166-
} else {
190+
if (!isPromise(version)) {
167191
setFirstUploaded(
168192
<RelativeTime
169193
time={version.datetime_created}
170194
suppressHydrationWarning
171195
/>
172196
);
197+
return;
173198
}
174-
}, []);
199+
200+
let isCancelled = false;
201+
202+
const resolveVersionTimes = async () => {
203+
try {
204+
const versionData = await version;
205+
if (isCancelled) {
206+
return;
207+
}
208+
209+
setFirstUploaded(
210+
<RelativeTime
211+
time={versionData.datetime_created}
212+
suppressHydrationWarning
213+
/>
214+
);
215+
} catch (error) {
216+
if (!isCancelled) {
217+
console.error("Failed to resolve version metadata", error);
218+
}
219+
}
220+
};
221+
222+
resolveVersionTimes();
223+
224+
return () => {
225+
isCancelled = true;
226+
};
227+
}, [isHydrated, version]);
175228
// END: For sidebar meta dates
176229

177230
const currentTab = location.pathname.split("/")[8] || "details";
178231

179232
const versionAndCommunityPromise = useMemo(
180233
() => Promise.all([version, community]),
181-
[]
234+
[version, community]
182235
);
183236

184-
const versionAndTeamPromise = useMemo(() => Promise.all([version, team]), []);
237+
const versionAndTeamPromise = useMemo(
238+
() => Promise.all([version, team]),
239+
[version, team]
240+
);
185241

186242
return (
187243
<>
188244
<Suspense>
189-
<Await resolve={versionAndCommunityPromise}>
245+
<Await
246+
resolve={versionAndCommunityPromise}
247+
errorElement={<NimbusAwaitErrorElement />}
248+
>
190249
{(resolvedValue) => (
191250
<>
192251
<meta
@@ -235,7 +294,10 @@ export default function PackageVersion() {
235294
</NewAlert>
236295
}
237296
>
238-
<Await resolve={version}>
297+
<Await
298+
resolve={version}
299+
errorElement={<NimbusAwaitErrorElement />}
300+
>
239301
{(resolvedValue) => (
240302
<NewAlert csVariant="warning">
241303
You are viewing a potentially older version of this
@@ -259,7 +321,10 @@ export default function PackageVersion() {
259321
<SkeletonBox className="package-listing__page-header-skeleton" />
260322
}
261323
>
262-
<Await resolve={version}>
324+
<Await
325+
resolve={version}
326+
errorElement={<NimbusAwaitErrorElement />}
327+
>
263328
{(resolvedValue) => (
264329
<PageHeader
265330
headingLevel="1"
@@ -325,15 +390,21 @@ export default function PackageVersion() {
325390
rootClasses="package-listing__drawer"
326391
>
327392
<Suspense fallback={<p>Loading...</p>}>
328-
<Await resolve={version}>
393+
<Await
394+
resolve={version}
395+
errorElement={<NimbusAwaitErrorElement />}
396+
>
329397
{(resolvedValue) => (
330398
<>{packageMeta(firstUploaded, resolvedValue)}</>
331399
)}
332400
</Await>
333401
</Suspense>
334402
</Drawer>
335403
<Suspense fallback={<p>Loading...</p>}>
336-
<Await resolve={versionAndTeamPromise}>
404+
<Await
405+
resolve={versionAndTeamPromise}
406+
errorElement={<NimbusAwaitErrorElement />}
407+
>
337408
{(resolvedValue) => (
338409
<Actions
339410
team={resolvedValue[1]}
@@ -348,7 +419,10 @@ export default function PackageVersion() {
348419
<SkeletonBox className="package-listing__nav-skeleton" />
349420
}
350421
>
351-
<Await resolve={version}>
422+
<Await
423+
resolve={version}
424+
errorElement={<NimbusAwaitErrorElement />}
425+
>
352426
{(resolvedValue) => (
353427
<>
354428
<Tabs>
@@ -418,7 +492,10 @@ export default function PackageVersion() {
418492
<SkeletonBox className="package-listing-sidebar__install-skeleton" />
419493
}
420494
>
421-
<Await resolve={version}>
495+
<Await
496+
resolve={version}
497+
errorElement={<NimbusAwaitErrorElement />}
498+
>
422499
{(resolvedValue) => (
423500
<NewButton
424501
csVariant="accent"
@@ -442,7 +519,10 @@ export default function PackageVersion() {
442519
<SkeletonBox className="package-listing-sidebar__actions-skeleton" />
443520
}
444521
>
445-
<Await resolve={versionAndTeamPromise}>
522+
<Await
523+
resolve={versionAndTeamPromise}
524+
errorElement={<NimbusAwaitErrorElement />}
525+
>
446526
{(resolvedValue) => (
447527
<Actions
448528
team={resolvedValue[1]}
@@ -456,7 +536,10 @@ export default function PackageVersion() {
456536
<SkeletonBox className="package-listing-sidebar__skeleton" />
457537
}
458538
>
459-
<Await resolve={version}>
539+
<Await
540+
resolve={version}
541+
errorElement={<NimbusAwaitErrorElement />}
542+
>
460543
{(resolvedValue) => (
461544
<>{packageMeta(firstUploaded, resolvedValue)}</>
462545
)}

0 commit comments

Comments
 (0)