@knime/utils 1.1.0 → 1.1.1
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knime/utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Internal utility functions and constants shared across @knime packages.",
|
|
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)",
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
"consola": "3.2.3",
|
|
11
11
|
"date-fns": "2.30.0",
|
|
12
12
|
"date-fns-tz": "2.0.0",
|
|
13
|
-
"@knime/styles": "1.1.
|
|
13
|
+
"@knime/styles": "1.1.1"
|
|
14
14
|
},
|
|
15
15
|
"main": "src/index.ts",
|
|
16
16
|
"module": "src/index.ts",
|
|
17
17
|
"files": [
|
|
18
18
|
"src",
|
|
19
|
-
"!src
|
|
19
|
+
"!src/**/__tests__/*",
|
|
20
20
|
"test-utils",
|
|
21
21
|
"CHANGELOG.md"
|
|
22
22
|
],
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import flushPromises from "flush-promises";
|
|
3
|
-
|
|
4
|
-
import { AbortError, createAbortablePromise } from "../createAbortablePromise";
|
|
5
|
-
|
|
6
|
-
describe("createAbortablePromise", () => {
|
|
7
|
-
it("should abort promise", async () => {
|
|
8
|
-
const mockFetchCall = () =>
|
|
9
|
-
new Promise((r) => setTimeout(() => r("resolved-value"), 300));
|
|
10
|
-
|
|
11
|
-
const { abortController, runAbortablePromise } = createAbortablePromise();
|
|
12
|
-
|
|
13
|
-
const catchSpy = vi.fn((e) => {
|
|
14
|
-
if (e instanceof AbortError) {
|
|
15
|
-
return "aborted";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
throw e;
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const promise = runAbortablePromise(() => mockFetchCall()).catch(catchSpy);
|
|
22
|
-
|
|
23
|
-
const abortError = new AbortError("aborting");
|
|
24
|
-
abortController.abort(abortError);
|
|
25
|
-
|
|
26
|
-
await flushPromises();
|
|
27
|
-
|
|
28
|
-
expect(catchSpy).toHaveBeenCalledWith(abortError);
|
|
29
|
-
expect(promise).resolves.toBe("aborted");
|
|
30
|
-
});
|
|
31
|
-
});
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { retryPromise } from "../retryPromise";
|
|
4
|
-
|
|
5
|
-
describe("retryPromise", () => {
|
|
6
|
-
it("should retry", async () => {
|
|
7
|
-
let callCount = 0;
|
|
8
|
-
const mockFetchCall = vi.fn(
|
|
9
|
-
() =>
|
|
10
|
-
new Promise((resolve, reject) => {
|
|
11
|
-
callCount++;
|
|
12
|
-
|
|
13
|
-
if (callCount < 3) {
|
|
14
|
-
reject(new Error("failure"));
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (callCount >= 3) {
|
|
19
|
-
resolve("success");
|
|
20
|
-
}
|
|
21
|
-
}),
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const doneSpy = vi.fn();
|
|
25
|
-
const catchSpy = vi.fn();
|
|
26
|
-
|
|
27
|
-
const promise = retryPromise(() => mockFetchCall())
|
|
28
|
-
.then(doneSpy)
|
|
29
|
-
.catch(catchSpy);
|
|
30
|
-
|
|
31
|
-
await promise;
|
|
32
|
-
|
|
33
|
-
expect(doneSpy).toHaveBeenCalledOnce();
|
|
34
|
-
expect(catchSpy).not.toHaveBeenCalled();
|
|
35
|
-
expect(mockFetchCall).toHaveBeenCalledTimes(3);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("should throw if retry count is exceeded", async () => {
|
|
39
|
-
const error = new Error("failure");
|
|
40
|
-
const mockFetchCall = vi.fn(() => Promise.reject(error));
|
|
41
|
-
|
|
42
|
-
const doneSpy = vi.fn();
|
|
43
|
-
|
|
44
|
-
const promise = retryPromise(() => mockFetchCall()).then(doneSpy);
|
|
45
|
-
|
|
46
|
-
await promise.catch(() => {}); // ignore errors caught by test
|
|
47
|
-
expect(() => promise).rejects.toThrowError(error);
|
|
48
|
-
|
|
49
|
-
expect(doneSpy).not.toHaveBeenCalled();
|
|
50
|
-
expect(mockFetchCall).toHaveBeenCalledTimes(6);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("should not retry for specific errors", async () => {
|
|
54
|
-
class MyError extends Error {}
|
|
55
|
-
const myError = new MyError("failure");
|
|
56
|
-
const mockFetchCall = vi.fn(() => Promise.reject(myError));
|
|
57
|
-
|
|
58
|
-
const doneSpy = vi.fn();
|
|
59
|
-
|
|
60
|
-
const promise = retryPromise(
|
|
61
|
-
() => mockFetchCall(),
|
|
62
|
-
2,
|
|
63
|
-
0,
|
|
64
|
-
(e) => e instanceof MyError,
|
|
65
|
-
).then(doneSpy);
|
|
66
|
-
|
|
67
|
-
await promise.catch(() => {}); // ignore errors caught by test
|
|
68
|
-
expect(() => promise).rejects.toThrowError(myError);
|
|
69
|
-
|
|
70
|
-
expect(doneSpy).not.toHaveBeenCalled();
|
|
71
|
-
expect(mockFetchCall).toHaveBeenCalledOnce();
|
|
72
|
-
});
|
|
73
|
-
});
|
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
/* eslint-disable vitest/no-disabled-tests */
|
|
2
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import { newServer } from "mock-xmlhttprequest";
|
|
4
|
-
|
|
5
|
-
import { createUploadManager } from "..";
|
|
6
|
-
|
|
7
|
-
describe("uploadManager", () => {
|
|
8
|
-
const fiftyBytes = new Array(50).fill("A");
|
|
9
|
-
|
|
10
|
-
const file = new File(fiftyBytes, "mock-file.txt", {
|
|
11
|
-
type: "text/plain",
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
type MockXHRHandler = NonNullable<Parameters<typeof newServer>[0]>[string][1];
|
|
15
|
-
|
|
16
|
-
const defaultUploadPartResponse: MockXHRHandler = {
|
|
17
|
-
// status: 200 is the default
|
|
18
|
-
headers: { "Content-Type": "application/json", Etag: '"partId"' },
|
|
19
|
-
body: "{}",
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
type Options = {
|
|
23
|
-
uploadPartResponse?: MockXHRHandler;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const setup = ({ uploadPartResponse }: Options = {}) => {
|
|
27
|
-
const server = newServer({
|
|
28
|
-
put: ["/success", uploadPartResponse ?? defaultUploadPartResponse],
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
server.install();
|
|
32
|
-
|
|
33
|
-
return { server };
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const mockUploadURLMetadata = { method: "PUT", url: "/success" };
|
|
37
|
-
const resolveFilePartUploadURL = vi.fn();
|
|
38
|
-
|
|
39
|
-
beforeEach(() => {
|
|
40
|
-
resolveFilePartUploadURL.mockImplementation(() =>
|
|
41
|
-
Promise.resolve(mockUploadURLMetadata),
|
|
42
|
-
);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const createIdGenerator = () => {
|
|
46
|
-
const generator = (function* () {
|
|
47
|
-
let id = 0;
|
|
48
|
-
while (true) {
|
|
49
|
-
yield id++;
|
|
50
|
-
}
|
|
51
|
-
})();
|
|
52
|
-
|
|
53
|
-
return () => `mock-upload--${generator.next().value}`;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const getUploadId = createIdGenerator();
|
|
57
|
-
|
|
58
|
-
describe("success", () => {
|
|
59
|
-
it("should handle `uploadFiles`", async () => {
|
|
60
|
-
setup();
|
|
61
|
-
const { uploadFiles } = createUploadManager({
|
|
62
|
-
resolveFilePartUploadURL,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const uploadId = getUploadId();
|
|
66
|
-
const { completed, failed, cancelled } = await uploadFiles([
|
|
67
|
-
{ uploadId, file },
|
|
68
|
-
]);
|
|
69
|
-
|
|
70
|
-
expect(completed).toEqual([uploadId]);
|
|
71
|
-
expect(failed.length).toBe(0);
|
|
72
|
-
expect(cancelled.length).toBe(0);
|
|
73
|
-
expect(resolveFilePartUploadURL).toHaveBeenCalled();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("should succeed if resolveFilePartUrl is retried successfully", async () => {
|
|
77
|
-
let callCount = 0;
|
|
78
|
-
resolveFilePartUploadURL.mockImplementation(() => {
|
|
79
|
-
callCount++;
|
|
80
|
-
if (callCount < 2) {
|
|
81
|
-
return Promise.reject(new Error("error that will be retried"));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return Promise.resolve(mockUploadURLMetadata);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
setup();
|
|
88
|
-
const { uploadFiles } = createUploadManager({
|
|
89
|
-
resolveFilePartUploadURL,
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const uploadId = getUploadId();
|
|
93
|
-
const { completed, failed, cancelled } = await uploadFiles([
|
|
94
|
-
{ uploadId, file },
|
|
95
|
-
]);
|
|
96
|
-
|
|
97
|
-
expect(completed).toEqual([uploadId]);
|
|
98
|
-
expect(failed.length).toBe(0);
|
|
99
|
-
expect(cancelled.length).toBe(0);
|
|
100
|
-
expect(resolveFilePartUploadURL).toHaveBeenCalled();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("should succeed if uploadPart request is retried successfully", async () => {
|
|
104
|
-
let callCount = 0;
|
|
105
|
-
setup({
|
|
106
|
-
uploadPartResponse: (request) => {
|
|
107
|
-
callCount++;
|
|
108
|
-
if (callCount <= 3) {
|
|
109
|
-
request.respond(400, {});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
request.respond(200, { Etag: '"partId"' });
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
const uploadId = getUploadId();
|
|
117
|
-
const { uploadFiles } = createUploadManager({
|
|
118
|
-
resolveFilePartUploadURL,
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const { completed, failed, cancelled } = await uploadFiles([
|
|
122
|
-
{ uploadId, file },
|
|
123
|
-
]);
|
|
124
|
-
|
|
125
|
-
expect(completed).toEqual([uploadId]);
|
|
126
|
-
expect(failed.length).toBe(0);
|
|
127
|
-
expect(cancelled.length).toBe(0);
|
|
128
|
-
expect(resolveFilePartUploadURL).toHaveBeenCalled();
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
describe("callbacks", () => {
|
|
133
|
-
it("should call onFileUploadComplete", async () => {
|
|
134
|
-
setup();
|
|
135
|
-
const onComplete = vi.fn();
|
|
136
|
-
const onFailed = vi.fn();
|
|
137
|
-
const { uploadFiles } = createUploadManager({
|
|
138
|
-
resolveFilePartUploadURL,
|
|
139
|
-
onFileUploadComplete: onComplete,
|
|
140
|
-
onFileUploadFailed: onFailed,
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
const uploadId1 = getUploadId();
|
|
144
|
-
const uploadId2 = getUploadId();
|
|
145
|
-
|
|
146
|
-
const { completed, failed, cancelled } = await uploadFiles([
|
|
147
|
-
{ uploadId: uploadId1, file },
|
|
148
|
-
{ uploadId: uploadId2, file },
|
|
149
|
-
]);
|
|
150
|
-
|
|
151
|
-
expect(completed).toEqual([uploadId1, uploadId2]);
|
|
152
|
-
expect(failed.length).toBe(0);
|
|
153
|
-
expect(cancelled.length).toBe(0);
|
|
154
|
-
expect(onComplete).toHaveBeenCalledWith({
|
|
155
|
-
uploadId: uploadId1,
|
|
156
|
-
filePartIds: { 1: "partId" },
|
|
157
|
-
});
|
|
158
|
-
expect(onComplete).toHaveBeenCalledWith({
|
|
159
|
-
uploadId: uploadId2,
|
|
160
|
-
filePartIds: { 1: "partId" },
|
|
161
|
-
});
|
|
162
|
-
expect(onFailed).not.toHaveBeenCalled();
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it("should call onFileUploadFailed", async () => {
|
|
166
|
-
setup({
|
|
167
|
-
uploadPartResponse: (request) => {
|
|
168
|
-
request.respond(400, {});
|
|
169
|
-
},
|
|
170
|
-
});
|
|
171
|
-
const onComplete = vi.fn();
|
|
172
|
-
const onFailed = vi.fn();
|
|
173
|
-
const { uploadFiles } = createUploadManager({
|
|
174
|
-
resolveFilePartUploadURL,
|
|
175
|
-
onFileUploadComplete: onComplete,
|
|
176
|
-
onFileUploadFailed: onFailed,
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
const uploadId1 = getUploadId();
|
|
180
|
-
const uploadId2 = getUploadId();
|
|
181
|
-
const { completed, failed, cancelled } = await uploadFiles([
|
|
182
|
-
{ uploadId: uploadId1, file },
|
|
183
|
-
{ uploadId: uploadId2, file },
|
|
184
|
-
]);
|
|
185
|
-
|
|
186
|
-
expect(completed.length).toBe(0);
|
|
187
|
-
expect(failed).toEqual([uploadId1, uploadId2]);
|
|
188
|
-
expect(cancelled.length).toBe(0);
|
|
189
|
-
expect(onFailed).toHaveBeenCalledWith(
|
|
190
|
-
uploadId1,
|
|
191
|
-
new Error("Failed to upload chunk 0"),
|
|
192
|
-
);
|
|
193
|
-
expect(onFailed).toHaveBeenCalledWith(
|
|
194
|
-
uploadId2,
|
|
195
|
-
new Error("Failed to upload chunk 0"),
|
|
196
|
-
);
|
|
197
|
-
expect(onComplete).not.toHaveBeenCalled();
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it("should call onProgress callback", async () => {
|
|
201
|
-
setup();
|
|
202
|
-
const onProgress = vi.fn();
|
|
203
|
-
|
|
204
|
-
const { uploadFiles } = createUploadManager({
|
|
205
|
-
resolveFilePartUploadURL,
|
|
206
|
-
onProgress,
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
await uploadFiles([{ uploadId: getUploadId(), file }]);
|
|
210
|
-
|
|
211
|
-
expect(onProgress).toHaveBeenCalled();
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
describe("cancellation", () => {
|
|
216
|
-
it("should handle cancellation", async () => {
|
|
217
|
-
setup();
|
|
218
|
-
const { uploadFiles, cancel } = createUploadManager({
|
|
219
|
-
resolveFilePartUploadURL,
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
const uploadId = getUploadId();
|
|
223
|
-
const upload = uploadFiles([{ uploadId, file }]);
|
|
224
|
-
|
|
225
|
-
cancel(uploadId);
|
|
226
|
-
|
|
227
|
-
const { completed, cancelled, failed } = await upload;
|
|
228
|
-
expect(completed.length).toBe(0);
|
|
229
|
-
expect(cancelled).toEqual([uploadId]);
|
|
230
|
-
expect(failed.length).toBe(0);
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
describe("failures", () => {
|
|
235
|
-
it("should fail when resolving upload url fails", async () => {
|
|
236
|
-
resolveFilePartUploadURL.mockImplementation(() =>
|
|
237
|
-
Promise.reject(new Error("error")),
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
setup();
|
|
241
|
-
const { uploadFiles } = createUploadManager({
|
|
242
|
-
resolveFilePartUploadURL,
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
const uploadId = getUploadId();
|
|
246
|
-
const { completed, failed, cancelled } = await uploadFiles([
|
|
247
|
-
{ uploadId, file },
|
|
248
|
-
]);
|
|
249
|
-
|
|
250
|
-
expect(completed.length).toBe(0);
|
|
251
|
-
expect(failed).toEqual([uploadId]);
|
|
252
|
-
expect(cancelled.length).toBe(0);
|
|
253
|
-
expect(resolveFilePartUploadURL).toHaveBeenCalled();
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
it("should fail when uploading part request fails", async () => {
|
|
257
|
-
setup({
|
|
258
|
-
uploadPartResponse: (request) => {
|
|
259
|
-
request.respond(400, {});
|
|
260
|
-
},
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
const { uploadFiles } = createUploadManager({
|
|
264
|
-
resolveFilePartUploadURL,
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
const uploadId = getUploadId();
|
|
268
|
-
|
|
269
|
-
const { completed, failed, cancelled } = await uploadFiles([
|
|
270
|
-
{ uploadId, file },
|
|
271
|
-
]);
|
|
272
|
-
|
|
273
|
-
expect(completed.length).toBe(0);
|
|
274
|
-
expect(failed).toEqual([uploadId]);
|
|
275
|
-
expect(cancelled.length).toBe(0);
|
|
276
|
-
expect(resolveFilePartUploadURL).toHaveBeenCalled();
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
it("should fail when etag is missing from upload part response", async () => {
|
|
280
|
-
setup({
|
|
281
|
-
uploadPartResponse: (request) => {
|
|
282
|
-
request.respond(200, {});
|
|
283
|
-
},
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
const { uploadFiles } = createUploadManager({
|
|
287
|
-
resolveFilePartUploadURL,
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
const uploadId = getUploadId();
|
|
291
|
-
|
|
292
|
-
const { completed, failed, cancelled } = await uploadFiles([
|
|
293
|
-
{ uploadId, file },
|
|
294
|
-
]);
|
|
295
|
-
|
|
296
|
-
expect(completed.length).toBe(0);
|
|
297
|
-
expect(failed).toEqual([uploadId]);
|
|
298
|
-
expect(cancelled.length).toBe(0);
|
|
299
|
-
expect(resolveFilePartUploadURL).toHaveBeenCalled();
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
});
|