@knime/hub-features 1.10.3 → 1.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @knime/hub-features
2
2
 
3
+ ## 1.10.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 805588d: update async download flow in useDownloadArtifact to reflect api changes
8
+
9
+ ## 1.10.4
10
+
11
+ ### Patch Changes
12
+
13
+ - cd0f5f0: Fix baseURL handling on ofetch client used by the upload, download and versions features.
14
+ Supplying a custom baseURL with trailing slashes now doesn't break these features
15
+
3
16
  ## 1.10.3
4
17
 
5
18
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knime/hub-features",
3
- "version": "1.10.3",
3
+ "version": "1.10.5",
4
4
  "description": "Vue components & composables for shared hub features",
5
5
  "homepage": "https://knime.github.io/webapps-common/",
6
6
  "license": "GPL 3 and Additional Permissions according to Sec. 7 (SEE the file LICENSE)",
@@ -1,6 +1,8 @@
1
1
  import { merge } from "lodash-es";
2
2
  import { type FetchOptions, ofetch } from "ofetch";
3
3
 
4
+ import { DEFAULT_API_BASE_URL } from "./constants";
5
+
4
6
  export const defaultConfig: FetchOptions = {
5
7
  headers: {
6
8
  "Content-Type": "application/json",
@@ -10,5 +12,11 @@ export const defaultConfig: FetchOptions = {
10
12
  },
11
13
  } as const;
12
14
 
13
- export const getFetchClient = (customOptions?: FetchOptions) =>
14
- ofetch.create(merge(customOptions, defaultConfig));
15
+ export const getFetchClient = (customOptions?: FetchOptions) => {
16
+ const { baseURL = DEFAULT_API_BASE_URL, ...otherCustomOptions } =
17
+ customOptions ?? {};
18
+
19
+ return ofetch.create(
20
+ merge({ baseURL, ...otherCustomOptions }, defaultConfig),
21
+ );
22
+ };
@@ -12,7 +12,6 @@ import type {
12
12
  WithLabels,
13
13
  } from "@knime/hub-features/versions";
14
14
 
15
- import { DEFAULT_API_BASE_URL } from "../../common/constants";
16
15
  import { getFetchClient } from "../../common/ofetchClient";
17
16
 
18
17
  type UseVersionsApiOptions = {
@@ -22,10 +21,7 @@ type UseVersionsApiOptions = {
22
21
  export const useVersionsApi = ({
23
22
  customFetchClientOptions,
24
23
  }: UseVersionsApiOptions) => {
25
- const { baseURL = DEFAULT_API_BASE_URL, ...otherCustomOptions } =
26
- customFetchClientOptions ?? {};
27
-
28
- const $ofetch = getFetchClient({ baseURL, ...otherCustomOptions });
24
+ const $ofetch = getFetchClient(customFetchClientOptions);
29
25
 
30
26
  const doHubRequest = (path: string, fetchOptions?: FetchOptions) => {
31
27
  const defaults: FetchOptions = {
@@ -4,7 +4,6 @@ import { FetchError, type FetchOptions } from "ofetch";
4
4
  import type { DownloadItem as DownloadItemExternal } from "@knime/components";
5
5
  import { sleep } from "@knime/utils";
6
6
 
7
- import { DEFAULT_API_BASE_URL } from "../common/constants";
8
7
  import { getFetchClient } from "../common/ofetchClient";
9
8
  import { rfcErrors } from "../rfcErrors";
10
9
 
@@ -19,8 +18,20 @@ type ArtifactStatusResponse = {
19
18
  downloadUrl?: string;
20
19
  };
21
20
 
22
- type RequestDownloadResponse = {
23
- downloadId: string;
21
+ // Initial request can have either download url (if it can be downloaded immediately) or download id (to poll for the status)
22
+ type RequestDownloadResponse =
23
+ | {
24
+ downloadId: string;
25
+ }
26
+ | {
27
+ downloadUrl: string;
28
+ };
29
+
30
+ // type guard
31
+ const hasDownloadUrl = (
32
+ response: RequestDownloadResponse,
33
+ ): response is { downloadUrl: string } => {
34
+ return "downloadUrl" in response;
24
35
  };
25
36
 
26
37
  /**
@@ -80,13 +91,11 @@ const isAbortError = (error: unknown) =>
80
91
  export const useDownloadArtifact = (
81
92
  options: UseDownloadArtifactOptions = {},
82
93
  ) => {
94
+ const $ofetch = getFetchClient(options.customFetchClientOptions);
95
+
83
96
  const downloadItems = ref<Record<string, DownloadItem>>({});
84
97
  const abortControllers: Record<string, AbortController> = {};
85
98
 
86
- const $ofetch = getFetchClient(options.customFetchClientOptions);
87
- const baseUrl =
88
- options.customFetchClientOptions?.baseURL ?? DEFAULT_API_BASE_URL;
89
-
90
99
  // Use fallback values if none are provided
91
100
  const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
92
101
  const pollingInterval = options.pollingInterval ?? DEFAULT_POLLING_INTERVAL;
@@ -111,13 +120,10 @@ export const useDownloadArtifact = (
111
120
  downloadId: string;
112
121
  signal?: AbortSignal;
113
122
  }) => {
114
- return $ofetch<ArtifactStatusResponse>(
115
- `${baseUrl}/downloads/${downloadId}/status`,
116
- {
117
- method: "GET",
118
- signal,
119
- },
120
- );
123
+ return $ofetch<ArtifactStatusResponse>(`/downloads/${downloadId}/status`, {
124
+ method: "GET",
125
+ signal,
126
+ });
121
127
  };
122
128
 
123
129
  const openDownload = (downloadUrl: string) => {
@@ -255,30 +261,26 @@ export const useDownloadArtifact = (
255
261
  }) => {
256
262
  try {
257
263
  const response = await $ofetch<RequestDownloadResponse>(
258
- `${baseUrl}/repository/${itemId}/artifact`,
264
+ `/repository/${itemId}/artifact`,
259
265
  {
260
266
  method: "GET",
261
267
  query: { version },
262
268
  },
263
269
  );
264
- const downloadId = response.downloadId;
265
-
266
- const downloadStatusResponse = await fetchDownloadStatus({ downloadId });
267
-
268
- if (
269
- // download is ready immediately, no polling needed
270
- downloadStatusResponse?.status === "READY" &&
271
- downloadStatusResponse?.downloadUrl
272
- ) {
273
- openDownload(downloadStatusResponse.downloadUrl);
274
- } else {
275
- await pollDownloadItem({
276
- itemId,
277
- version,
278
- downloadId,
279
- name,
280
- });
270
+
271
+ // if download is ready, open url immediately
272
+ if (hasDownloadUrl(response)) {
273
+ openDownload(response.downloadUrl);
274
+ return;
281
275
  }
276
+
277
+ // otherwise start polling download status
278
+ await pollDownloadItem({
279
+ itemId,
280
+ version,
281
+ downloadId: response.downloadId,
282
+ name,
283
+ });
282
284
  } catch (error: unknown) {
283
285
  // here only errors thrown by the initial requests are caught;
284
286
  // errors occurring while polling will be handled in pollDownloadItem
@@ -4,7 +4,6 @@ import { FetchError, type FetchOptions } from "ofetch";
4
4
  import { useUploadManager } from "@knime/components";
5
5
  import { getFileMimeType, knimeFileFormats, promise } from "@knime/utils";
6
6
 
7
- import { DEFAULT_API_BASE_URL } from "../common/constants";
8
7
  import { getFetchClient } from "../common/ofetchClient";
9
8
  import { rfcErrors } from "../rfcErrors";
10
9
 
@@ -108,8 +107,6 @@ type UseFileUploadOptions = {
108
107
  * ```
109
108
  */
110
109
  export const useFileUpload = (options: UseFileUploadOptions = {}) => {
111
- const baseUrl =
112
- options.customFetchClientOptions?.baseURL ?? DEFAULT_API_BASE_URL;
113
110
  const $ofetch = getFetchClient(options.customFetchClientOptions);
114
111
 
115
112
  const prepareUpload = (
@@ -141,10 +138,10 @@ export const useFileUpload = (options: UseFileUploadOptions = {}) => {
141
138
  items: Record<string, { uploadId: string }>;
142
139
  };
143
140
 
144
- return $ofetch<PrepareUploadResponse>(
145
- `${baseUrl}/repository/${parentId}/manifest`,
146
- { method: "POST", body: { items } },
147
- ).then(({ items }) => {
141
+ return $ofetch<PrepareUploadResponse>(`/repository/${parentId}/manifest`, {
142
+ method: "POST",
143
+ body: { items },
144
+ }).then(({ items }) => {
148
145
  return Object.keys(items).map((name) => {
149
146
  const { uploadId } = items[name];
150
147
  const file = fileDictionary[name];
@@ -161,7 +158,7 @@ export const useFileUpload = (options: UseFileUploadOptions = {}) => {
161
158
  };
162
159
 
163
160
  return $ofetch<UploadURLResponse>(
164
- `${baseUrl}/uploads/${uploadId}/parts/?partNumber=${partNumber}`,
161
+ `/uploads/${uploadId}/parts/?partNumber=${partNumber}`,
165
162
  { method: "POST" },
166
163
  );
167
164
  };
@@ -170,14 +167,14 @@ export const useFileUpload = (options: UseFileUploadOptions = {}) => {
170
167
  uploadId: string,
171
168
  partIds: Record<number, string>,
172
169
  ) => {
173
- return $ofetch(`${baseUrl}/uploads/${uploadId}`, {
170
+ return $ofetch(`/uploads/${uploadId}`, {
174
171
  method: "POST",
175
172
  body: partIds,
176
173
  });
177
174
  };
178
175
 
179
176
  const cancelUpload = (uploadId: string) => {
180
- return $ofetch(`${baseUrl}/uploads/${uploadId}`, {
177
+ return $ofetch(`/uploads/${uploadId}`, {
181
178
  method: "DELETE",
182
179
  });
183
180
  };