diff --git a/.changeset/dirty-ends-pull.md b/.changeset/dirty-ends-pull.md new file mode 100644 index 0000000000..2fa96c9cb9 --- /dev/null +++ b/.changeset/dirty-ends-pull.md @@ -0,0 +1,6 @@ +--- +"@knime/components": patch +--- + +FileExplorer: fix types of OnClickOutside ignores +FileExplorer: disable options menu when multiple items are selected diff --git a/.changeset/old-rocks-occur.md b/.changeset/old-rocks-occur.md new file mode 100644 index 0000000000..e801639217 --- /dev/null +++ b/.changeset/old-rocks-occur.md @@ -0,0 +1,5 @@ +--- +"@knime/hub-features": patch +--- + +fix rfcErrors parser and return undefined on error diff --git a/packages/components/src/components/FileExplorer/components/FileExplorer.vue b/packages/components/src/components/FileExplorer/components/FileExplorer.vue index f5712473a3..cbd73474df 100644 --- a/packages/components/src/components/FileExplorer/components/FileExplorer.vue +++ b/packages/components/src/components/FileExplorer/components/FileExplorer.vue @@ -689,6 +689,7 @@ useResizeObserver(containerProps.ref, containerProps.onScroll); contextMenuAnchor?.openedBy === 'optionsMenu' && contextMenuAnchor?.item.id === item.id " + :disabled="selectedItems.length > 1" compact @dblclick.stop @pointerdown.stop diff --git a/packages/hub-features/src/components/versions/composables/useVersionsApi.ts b/packages/hub-features/src/components/versions/composables/useVersionsApi.ts index 7612aa942c..2f5eb6ba6c 100644 --- a/packages/hub-features/src/components/versions/composables/useVersionsApi.ts +++ b/packages/hub-features/src/components/versions/composables/useVersionsApi.ts @@ -1,4 +1,4 @@ -import { FetchError, type FetchOptions } from "ofetch"; +import { type FetchOptions } from "ofetch"; import { type HubAvatarData, rfcErrors } from "@knime/hub-features"; import { VERSION_DEFAULT_LIMIT } from "@knime/hub-features/versions"; @@ -37,11 +37,7 @@ export const useVersionsApi = ({ return await $ofetch(path, { ...defaults, ...fetchOptions }); } catch (error) { - if (error instanceof FetchError) { - throw rfcErrors.tryParse(error); - } - - throw error; + throw rfcErrors.tryParse(error) ?? error; } }; diff --git a/packages/hub-features/src/rfcErrors/__tests__/index.test.ts b/packages/hub-features/src/rfcErrors/__tests__/index.test.ts index ae262ee503..302c54b715 100644 --- a/packages/hub-features/src/rfcErrors/__tests__/index.test.ts +++ b/packages/hub-features/src/rfcErrors/__tests__/index.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import type { FetchError } from "ofetch"; +import { FetchError } from "ofetch"; import { rfcErrors } from ".."; import { RFCError } from "../types"; @@ -46,16 +46,15 @@ describe("rfcErrors", () => { response.headers.set("x-request-id", "request-id"); response.headers.set("x-error-id", "error-id"); - const fetchError: FetchError = { - data: { - title: "The title", - details: ["detail 1"], - }, - statusCode: 400, - response, - name: "", - message: "", + const fetchError = new FetchError(""); + fetchError.data = { + title: "The title", + details: ["detail 1"], }; + fetchError.statusCode = 400; + fetchError.response = response; + fetchError.name = ""; + fetchError.message = ""; const expectedError = new RFCError({ title: "The title", @@ -71,21 +70,27 @@ describe("rfcErrors", () => { expect(parsedError).toEqual(expectedError); }); - it("should NOT parse into RFCError", () => { - const fetchError: FetchError = { - data: { - title: "The title", - details: ["detail 1"], - }, - statusCode: 400, - name: "", - message: "", + it("should NOT parse into RFCError if response is missing", () => { + const fetchError = new FetchError(""); + fetchError.data = { + title: "The title", + details: ["detail 1"], }; + fetchError.statusCode = 400; + fetchError.name = ""; + fetchError.message = ""; const parsedError = rfcErrors.tryParse(fetchError); - expect(parsedError instanceof rfcErrors.RFCError).toBe(false); - expect(parsedError).toBe(fetchError); + expect(parsedError).toBeUndefined(); + }); + + it("should NOT parse into RFCError if no FetchError is given", () => { + const error = new Error(""); + + const parsedError = rfcErrors.tryParse(error); + + expect(parsedError).toBeUndefined(); }); }); }); diff --git a/packages/hub-features/src/rfcErrors/index.ts b/packages/hub-features/src/rfcErrors/index.ts index b46f1e0cd2..3b0ff3eaa5 100644 --- a/packages/hub-features/src/rfcErrors/index.ts +++ b/packages/hub-features/src/rfcErrors/index.ts @@ -35,21 +35,28 @@ const toToast = ({ }; /** - * Try to parse an ofetch's `FetchError` to an `RFCError`. When parsing is not possible - * it returns the same `FetchError` given unchanged. + * Parse an ofetch's `FetchError` to an `RFCError`. + * When parsing is not possible it returns undefined. */ -const tryParse = (error: FetchError): RFCError | FetchError => { +const tryParse = (error: unknown): RFCError | undefined => { + if (!(error instanceof FetchError)) { + return undefined; + } + if (!error.response) { - return error; + return undefined; } const responseDate = error.response.headers.get("date") ? new Date(error.response.headers.get("date")!) : undefined; + const title = + typeof error.data === "string" ? error.data : error.data?.title ?? ""; + const rfcErrorData: RFCErrorData = { - title: error.data.title as string, - details: error.data.details as string[] | undefined, + title: title as string, + details: error.data?.details as string[] | undefined, status: error.statusCode as number, date: responseDate, requestId: error.response.headers.get("x-request-id") ?? undefined, diff --git a/packages/hub-features/src/useDownloadArtifact/useDownloadArtifact.ts b/packages/hub-features/src/useDownloadArtifact/useDownloadArtifact.ts index 6962371c46..b6ba86f483 100644 --- a/packages/hub-features/src/useDownloadArtifact/useDownloadArtifact.ts +++ b/packages/hub-features/src/useDownloadArtifact/useDownloadArtifact.ts @@ -284,10 +284,7 @@ export const useDownloadArtifact = ( } catch (error: unknown) { // here only errors thrown by the initial requests are caught; // errors occurring while polling will be handled in pollDownloadItem - if (error instanceof FetchError) { - throw rfcErrors.tryParse(error); - } - throw error; + throw rfcErrors.tryParse(error) ?? error; } }; diff --git a/packages/hub-features/src/useFileUpload/useFileUpload.ts b/packages/hub-features/src/useFileUpload/useFileUpload.ts index 9a8831f4a5..a5312740bd 100644 --- a/packages/hub-features/src/useFileUpload/useFileUpload.ts +++ b/packages/hub-features/src/useFileUpload/useFileUpload.ts @@ -287,11 +287,7 @@ export const useFileUpload = (options: UseFileUploadOptions = {}) => { useUploadManagerResult.start(parentId, uploadPayload); } catch (error) { - if (error instanceof FetchError) { - throw rfcErrors.tryParse(error); - } - - throw error; + throw rfcErrors.tryParse(error) ?? error; } finally { // errors can only be thrown in the prepareUpload call, useUploadManagerResult.start does its own error handling prepareQueueSize.value -= enqueableFiles.length;