fix(releases-widget): error display, decouple database repository object from responses and batch widget queries (#2891)
Co-authored-by: Manuel <30572287+manuel-rw@users.noreply.github.com> Co-authored-by: Andre Silva <asilva01@acuitysso.com> Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
This commit is contained in:
@@ -41,12 +41,8 @@ export const Providers: ProvidersProps = {
|
||||
.transform((resp) => ({
|
||||
projectUrl: `https://hub.docker.com/r/${resp.namespace === "library" ? "_" : resp.namespace}/${resp.name}`,
|
||||
projectDescription: resp.description,
|
||||
isFork: false,
|
||||
isArchived: false,
|
||||
createdAt: resp.date_registered,
|
||||
starsCount: resp.star_count,
|
||||
openIssues: 0,
|
||||
forksCount: 0,
|
||||
}))
|
||||
.safeParse(response);
|
||||
},
|
||||
@@ -67,12 +63,7 @@ export const Providers: ProvidersProps = {
|
||||
),
|
||||
})
|
||||
.transform((resp) => {
|
||||
return resp.results.map((release) => ({
|
||||
...release,
|
||||
releaseUrl: "",
|
||||
releaseDescription: "",
|
||||
isPreRelease: false,
|
||||
}));
|
||||
return resp.results;
|
||||
})
|
||||
.safeParse(response);
|
||||
},
|
||||
@@ -217,7 +208,6 @@ export const Providers: ProvidersProps = {
|
||||
...release,
|
||||
releaseUrl: `https://www.npmjs.com/package/${resp.name}/v/${release.latestRelease}`,
|
||||
releaseDescription: resp.versions[release.latestRelease]?.description ?? "",
|
||||
isPreRelease: false,
|
||||
}));
|
||||
})
|
||||
.safeParse(response);
|
||||
@@ -282,23 +272,31 @@ export const Providers: ProvidersProps = {
|
||||
},
|
||||
};
|
||||
|
||||
const _detailsSchema = z.object({
|
||||
projectUrl: z.string(),
|
||||
projectDescription: z.string(),
|
||||
isFork: z.boolean(),
|
||||
isArchived: z.boolean(),
|
||||
createdAt: z.date(),
|
||||
starsCount: z.number(),
|
||||
openIssues: z.number(),
|
||||
forksCount: z.number(),
|
||||
});
|
||||
const _detailsSchema = z
|
||||
.object({
|
||||
projectUrl: z.string().optional(),
|
||||
projectDescription: z.string().optional(),
|
||||
isFork: z.boolean().optional(),
|
||||
isArchived: z.boolean().optional(),
|
||||
createdAt: z.date().optional(),
|
||||
starsCount: z.number().optional(),
|
||||
openIssues: z.number().optional(),
|
||||
forksCount: z.number().optional(),
|
||||
})
|
||||
.optional();
|
||||
|
||||
const _releasesSchema = z.object({
|
||||
latestRelease: z.string(),
|
||||
latestReleaseAt: z.date(),
|
||||
releaseUrl: z.string(),
|
||||
releaseDescription: z.string(),
|
||||
isPreRelease: z.boolean(),
|
||||
releaseUrl: z.string().optional(),
|
||||
releaseDescription: z.string().optional(),
|
||||
isPreRelease: z.boolean().optional(),
|
||||
error: z
|
||||
.object({
|
||||
code: z.string().optional(),
|
||||
message: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type DetailsResponse = z.infer<typeof _detailsSchema>;
|
||||
|
||||
@@ -8,22 +8,49 @@ import { createCachedWidgetRequestHandler } from "./lib/cached-widget-request-ha
|
||||
import { Providers } from "./releases-providers";
|
||||
import type { DetailsResponse } from "./releases-providers";
|
||||
|
||||
const errorSchema = z.object({
|
||||
code: z.string().optional(),
|
||||
message: z.string().optional(),
|
||||
});
|
||||
|
||||
type ReleasesError = z.infer<typeof errorSchema>;
|
||||
|
||||
const _reponseSchema = z.object({
|
||||
identifier: z.string(),
|
||||
providerKey: z.string(),
|
||||
latestRelease: z.string(),
|
||||
latestReleaseAt: z.date(),
|
||||
releaseUrl: z.string(),
|
||||
releaseDescription: z.string(),
|
||||
isPreRelease: z.boolean(),
|
||||
projectUrl: z.string(),
|
||||
projectDescription: z.string(),
|
||||
isFork: z.boolean(),
|
||||
isArchived: z.boolean(),
|
||||
createdAt: z.date(),
|
||||
starsCount: z.number(),
|
||||
openIssues: z.number(),
|
||||
forksCount: z.number(),
|
||||
latestRelease: z.string().optional(),
|
||||
latestReleaseAt: z.date().optional(),
|
||||
releaseUrl: z.string().optional(),
|
||||
releaseDescription: z.string().optional(),
|
||||
isPreRelease: z.boolean().optional(),
|
||||
projectUrl: z.string().optional(),
|
||||
projectDescription: z.string().optional(),
|
||||
isFork: z.boolean().optional(),
|
||||
isArchived: z.boolean().optional(),
|
||||
createdAt: z.date().optional(),
|
||||
starsCount: z.number().optional(),
|
||||
openIssues: z.number().optional(),
|
||||
forksCount: z.number().optional(),
|
||||
error: errorSchema.optional(),
|
||||
});
|
||||
|
||||
const formatErrorRelease = (identifier: string, providerKey: string, error: ReleasesError) => ({
|
||||
identifier,
|
||||
providerKey,
|
||||
latestRelease: undefined,
|
||||
latestReleaseAt: undefined,
|
||||
releaseUrl: undefined,
|
||||
releaseDescription: undefined,
|
||||
isPreRelease: undefined,
|
||||
projectUrl: undefined,
|
||||
projectDescription: undefined,
|
||||
isFork: undefined,
|
||||
isArchived: undefined,
|
||||
createdAt: undefined,
|
||||
starsCount: undefined,
|
||||
openIssues: undefined,
|
||||
forksCount: undefined,
|
||||
error,
|
||||
});
|
||||
|
||||
export const releasesRequestHandler = createCachedWidgetRequestHandler({
|
||||
@@ -34,17 +61,7 @@ export const releasesRequestHandler = createCachedWidgetRequestHandler({
|
||||
|
||||
if (!provider) return undefined;
|
||||
|
||||
let detailsResult: DetailsResponse = {
|
||||
projectUrl: "",
|
||||
projectDescription: "",
|
||||
isFork: false,
|
||||
isArchived: false,
|
||||
createdAt: new Date(0),
|
||||
starsCount: 0,
|
||||
openIssues: 0,
|
||||
forksCount: 0,
|
||||
};
|
||||
|
||||
let detailsResult: DetailsResponse;
|
||||
const detailsUrl = provider.getDetailsUrl(input.identifier);
|
||||
if (detailsUrl !== undefined) {
|
||||
const detailsResponse = await fetchWithTimeout(detailsUrl);
|
||||
@@ -53,6 +70,7 @@ export const releasesRequestHandler = createCachedWidgetRequestHandler({
|
||||
if (parsedDetails?.success) {
|
||||
detailsResult = parsedDetails.data;
|
||||
} else {
|
||||
detailsResult = undefined;
|
||||
logger.warn("Failed to parse details response", {
|
||||
provider: input.providerKey,
|
||||
identifier: input.identifier,
|
||||
@@ -63,43 +81,42 @@ export const releasesRequestHandler = createCachedWidgetRequestHandler({
|
||||
}
|
||||
|
||||
const releasesResponse = await fetchWithTimeout(provider.getReleasesUrl(input.identifier));
|
||||
const releasesResult = provider.parseReleasesResponse(await releasesResponse.json());
|
||||
const releasesResponseJson: unknown = await releasesResponse.json();
|
||||
const releasesResult = provider.parseReleasesResponse(releasesResponseJson);
|
||||
|
||||
if (!releasesResult.success) return undefined;
|
||||
|
||||
const latest: ResponseResponse = releasesResult.data
|
||||
.filter((result) => (input.versionRegex ? new RegExp(input.versionRegex).test(result.latestRelease) : true))
|
||||
.reduce(
|
||||
(latest, result) => {
|
||||
return {
|
||||
...detailsResult,
|
||||
...(result.latestReleaseAt > latest.latestReleaseAt ? result : latest),
|
||||
identifier: input.identifier,
|
||||
providerKey: input.providerKey,
|
||||
};
|
||||
},
|
||||
{
|
||||
identifier: "",
|
||||
providerKey: "",
|
||||
latestRelease: "",
|
||||
latestReleaseAt: new Date(0),
|
||||
releaseUrl: "",
|
||||
releaseDescription: "",
|
||||
isPreRelease: false,
|
||||
projectUrl: "",
|
||||
projectDescription: "",
|
||||
isFork: false,
|
||||
isArchived: false,
|
||||
createdAt: new Date(0),
|
||||
starsCount: 0,
|
||||
openIssues: 0,
|
||||
forksCount: 0,
|
||||
},
|
||||
if (!releasesResult.success) {
|
||||
return formatErrorRelease(input.identifier, input.providerKey, {
|
||||
message: releasesResponseJson ? JSON.stringify(releasesResponseJson, null, 2) : releasesResult.error.message,
|
||||
});
|
||||
} else {
|
||||
const releases = releasesResult.data.filter((result) =>
|
||||
input.versionRegex && result.latestRelease ? new RegExp(input.versionRegex).test(result.latestRelease) : true,
|
||||
);
|
||||
|
||||
return latest;
|
||||
const latest =
|
||||
releases.length === 0
|
||||
? formatErrorRelease(input.identifier, input.providerKey, { code: "noMatchingVersion" })
|
||||
: releases.reduce(
|
||||
(latest, result) => {
|
||||
return {
|
||||
...detailsResult,
|
||||
...(result.latestReleaseAt > latest.latestReleaseAt ? result : latest),
|
||||
identifier: input.identifier,
|
||||
providerKey: input.providerKey,
|
||||
};
|
||||
},
|
||||
{
|
||||
identifier: "",
|
||||
providerKey: "",
|
||||
latestRelease: "",
|
||||
latestReleaseAt: new Date(0),
|
||||
},
|
||||
);
|
||||
|
||||
return latest;
|
||||
}
|
||||
},
|
||||
cacheDuration: dayjs.duration(5, "minutes"),
|
||||
});
|
||||
|
||||
export type ResponseResponse = z.infer<typeof _reponseSchema>;
|
||||
export type ReleaseResponse = z.infer<typeof _reponseSchema>;
|
||||
|
||||
Reference in New Issue
Block a user