@ogcio/building-blocks-sdk 0.2.85 → 0.2.87
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/.azure/pipeline.yaml +15 -14
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/client/clients/messaging/index.d.ts.map +1 -1
- package/dist/client/clients/messaging/schema.d.ts +167 -0
- package/dist/client/clients/messaging/schema.d.ts.map +1 -1
- package/dist/client/clients/messaging/support.d.ts +122 -0
- package/dist/client/clients/messaging/support.d.ts.map +1 -1
- package/dist/client/clients/messaging/support.js +7 -0
- package/dist/client/clients/messaging/support.js.map +1 -1
- package/dist/client/clients/upload/index.d.ts +15 -0
- package/dist/client/clients/upload/index.d.ts.map +1 -1
- package/dist/client/clients/upload/index.js +19 -35
- package/dist/client/clients/upload/index.js.map +1 -1
- package/dist/client/clients/upload/shared.d.ts +47 -0
- package/dist/client/clients/upload/shared.d.ts.map +1 -0
- package/dist/client/clients/upload/shared.js +72 -0
- package/dist/client/clients/upload/shared.js.map +1 -0
- package/dist/client/clients/upload/support.d.ts +15 -0
- package/dist/client/clients/upload/support.d.ts.map +1 -1
- package/dist/client/clients/upload/support.js +19 -35
- package/dist/client/clients/upload/support.js.map +1 -1
- package/dist/client/utils/client-utils.d.ts +9 -0
- package/dist/client/utils/client-utils.d.ts.map +1 -1
- package/dist/client/utils/client-utils.js +43 -0
- package/dist/client/utils/client-utils.js.map +1 -1
- package/package.json +1 -1
- package/src/client/clients/messaging/open-api-definition.json +337 -0
- package/src/client/clients/messaging/schema.ts +167 -0
- package/src/client/clients/messaging/support.ts +13 -0
- package/src/client/clients/upload/index.ts +35 -36
- package/src/client/clients/upload/shared.ts +122 -0
- package/src/client/clients/upload/support.ts +35 -36
- package/src/client/utils/client-utils.ts +59 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type createClient from "openapi-fetch";
|
|
2
|
+
import { buildStreamingMultipart } from "../../utils/client-utils.js";
|
|
3
|
+
import type { paths } from "./schema.js";
|
|
4
|
+
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 120000; // 120 seconds
|
|
6
|
+
|
|
7
|
+
export async function uploadFile(params: {
|
|
8
|
+
file: File;
|
|
9
|
+
expirationDate?: string;
|
|
10
|
+
options?: { timeoutMs?: number };
|
|
11
|
+
client: ReturnType<typeof createClient<paths>>;
|
|
12
|
+
path: "/api/v1/support/files/" | "/api/v1/files/";
|
|
13
|
+
}): Promise<{
|
|
14
|
+
error?: {
|
|
15
|
+
code: string;
|
|
16
|
+
detail: string;
|
|
17
|
+
requestId: string;
|
|
18
|
+
name: string;
|
|
19
|
+
validation?: unknown;
|
|
20
|
+
validationContext?: string;
|
|
21
|
+
};
|
|
22
|
+
data?: { uploadId?: string };
|
|
23
|
+
}> {
|
|
24
|
+
const { file, expirationDate, options, client, path } = params;
|
|
25
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
26
|
+
try {
|
|
27
|
+
const { error, data } = await client.POST(path, {
|
|
28
|
+
body: {
|
|
29
|
+
file,
|
|
30
|
+
expirationDate,
|
|
31
|
+
},
|
|
32
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
33
|
+
bodySerializer: (body: unknown) => {
|
|
34
|
+
const parsed = body as { file: File; expirationDate?: string };
|
|
35
|
+
const formData = new FormData();
|
|
36
|
+
if (parsed.expirationDate) {
|
|
37
|
+
formData.set("expirationDate", parsed.expirationDate);
|
|
38
|
+
}
|
|
39
|
+
formData.set("file", parsed.file);
|
|
40
|
+
return formData;
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return { error, data: { uploadId: data?.data.id } };
|
|
45
|
+
} catch (e: unknown) {
|
|
46
|
+
const err = e as Error & { name?: string };
|
|
47
|
+
if (
|
|
48
|
+
err?.name === "AbortError" ||
|
|
49
|
+
err?.constructor?.name === "DOMException"
|
|
50
|
+
) {
|
|
51
|
+
return {
|
|
52
|
+
error: {
|
|
53
|
+
name: "TimeoutError",
|
|
54
|
+
detail: "Upload aborted after reaching timeout",
|
|
55
|
+
requestId: "",
|
|
56
|
+
code: "TIMEOUT_ERROR",
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function uploadStreamFile(params: {
|
|
65
|
+
file: ReadableStream<Uint8Array> | Blob;
|
|
66
|
+
filename: string;
|
|
67
|
+
mimeType: string;
|
|
68
|
+
expirationDate?: string;
|
|
69
|
+
options?: { timeoutMs?: number };
|
|
70
|
+
client: ReturnType<typeof createClient<paths>>;
|
|
71
|
+
path: "/api/v1/support/files/" | "/api/v1/files/";
|
|
72
|
+
}): Promise<{
|
|
73
|
+
error?: {
|
|
74
|
+
code: string;
|
|
75
|
+
detail: string;
|
|
76
|
+
requestId: string;
|
|
77
|
+
name: string;
|
|
78
|
+
validation?: unknown;
|
|
79
|
+
validationContext?: string;
|
|
80
|
+
};
|
|
81
|
+
data?: { uploadId?: string };
|
|
82
|
+
}> {
|
|
83
|
+
const { file, filename, mimeType, expirationDate, options, client, path } =
|
|
84
|
+
params;
|
|
85
|
+
|
|
86
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
87
|
+
try {
|
|
88
|
+
const stream = file instanceof Blob ? file.stream() : file;
|
|
89
|
+
const { body, contentType } = buildStreamingMultipart(
|
|
90
|
+
stream,
|
|
91
|
+
filename,
|
|
92
|
+
mimeType,
|
|
93
|
+
expirationDate,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const { error, data } = await client.POST(path, {
|
|
97
|
+
body: {} as never,
|
|
98
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
99
|
+
headers: { "content-type": contentType },
|
|
100
|
+
bodySerializer: () => body,
|
|
101
|
+
duplex: "half",
|
|
102
|
+
} as unknown as Parameters<typeof client.POST>[1]);
|
|
103
|
+
|
|
104
|
+
return { error, data: { uploadId: data?.data.id } };
|
|
105
|
+
} catch (e: unknown) {
|
|
106
|
+
const err = e as Error & { name?: string };
|
|
107
|
+
if (
|
|
108
|
+
err?.name === "AbortError" ||
|
|
109
|
+
err?.constructor?.name === "DOMException"
|
|
110
|
+
) {
|
|
111
|
+
return {
|
|
112
|
+
error: {
|
|
113
|
+
name: "TimeoutError",
|
|
114
|
+
detail: "Upload aborted after reaching timeout",
|
|
115
|
+
requestId: "",
|
|
116
|
+
code: "TIMEOUT_ERROR",
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
throwIfEmpty,
|
|
7
7
|
} from "../../utils/client-utils.js";
|
|
8
8
|
import type { paths } from "./schema.js";
|
|
9
|
+
import { uploadFile, uploadStreamFile } from "./shared.js";
|
|
9
10
|
|
|
10
11
|
export class UploadSupport {
|
|
11
12
|
constructor(
|
|
@@ -74,43 +75,41 @@ export class UploadSupport {
|
|
|
74
75
|
};
|
|
75
76
|
data?: { uploadId?: string };
|
|
76
77
|
}> {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
bodySerializer: (body: unknown) => {
|
|
86
|
-
const parsed = body as { file: File; expirationDate?: string };
|
|
87
|
-
const formData = new FormData();
|
|
88
|
-
if (parsed.expirationDate) {
|
|
89
|
-
formData.set("expirationDate", parsed.expirationDate);
|
|
90
|
-
}
|
|
91
|
-
formData.set("file", parsed.file);
|
|
92
|
-
return formData;
|
|
93
|
-
},
|
|
94
|
-
});
|
|
78
|
+
return uploadFile({
|
|
79
|
+
file,
|
|
80
|
+
expirationDate,
|
|
81
|
+
options,
|
|
82
|
+
client: this.client,
|
|
83
|
+
path: "/api/v1/support/files/",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
95
86
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
87
|
+
async uploadStreamFile(
|
|
88
|
+
file: ReadableStream<Uint8Array> | Blob,
|
|
89
|
+
filename: string,
|
|
90
|
+
mimeType: string,
|
|
91
|
+
expirationDate?: string,
|
|
92
|
+
options?: { timeoutMs?: number },
|
|
93
|
+
): Promise<{
|
|
94
|
+
error?: {
|
|
95
|
+
code: string;
|
|
96
|
+
detail: string;
|
|
97
|
+
requestId: string;
|
|
98
|
+
name: string;
|
|
99
|
+
validation?: unknown;
|
|
100
|
+
validationContext?: string;
|
|
101
|
+
};
|
|
102
|
+
data?: { uploadId?: string };
|
|
103
|
+
}> {
|
|
104
|
+
return uploadStreamFile({
|
|
105
|
+
file,
|
|
106
|
+
filename,
|
|
107
|
+
mimeType,
|
|
108
|
+
expirationDate,
|
|
109
|
+
options,
|
|
110
|
+
client: this.client,
|
|
111
|
+
path: "/api/v1/support/files/",
|
|
112
|
+
});
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
shareFile(fileId: string, ...userIds: string[]) {
|
|
@@ -163,3 +163,62 @@ export function throwIfEmpty(input: string | string[]): void {
|
|
|
163
163
|
throw Error("Parameter cannot be an empty string or array!");
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Builds a streaming multipart/form-data body from a ReadableStream without
|
|
169
|
+
* buffering the file content in memory. The returned ReadableStream pipes the
|
|
170
|
+
* file chunks directly into the multipart envelope as they are read.
|
|
171
|
+
*/
|
|
172
|
+
export function buildStreamingMultipart(
|
|
173
|
+
stream: ReadableStream<Uint8Array>,
|
|
174
|
+
filename: string,
|
|
175
|
+
mimeType: string,
|
|
176
|
+
expirationDate?: string,
|
|
177
|
+
): { body: ReadableStream<Uint8Array>; contentType: string } {
|
|
178
|
+
const boundary = `----FormBoundary${crypto.randomUUID().replace(/-/g, "")}`;
|
|
179
|
+
const encoder = new TextEncoder();
|
|
180
|
+
// Sanitize filename to prevent Content-Disposition header injection
|
|
181
|
+
const safeFilename = filename.replace(/["\\\r\n]/g, "_");
|
|
182
|
+
|
|
183
|
+
const parts: (Uint8Array | ReadableStream<Uint8Array>)[] = [];
|
|
184
|
+
|
|
185
|
+
if (expirationDate) {
|
|
186
|
+
parts.push(
|
|
187
|
+
encoder.encode(
|
|
188
|
+
`--${boundary}\r\nContent-Disposition: form-data; name="expirationDate"\r\n\r\n${expirationDate}\r\n`,
|
|
189
|
+
),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
parts.push(
|
|
194
|
+
encoder.encode(
|
|
195
|
+
`--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${safeFilename}"\r\nContent-Type: ${mimeType}\r\n\r\n`,
|
|
196
|
+
),
|
|
197
|
+
);
|
|
198
|
+
parts.push(stream);
|
|
199
|
+
parts.push(encoder.encode(`\r\n--${boundary}--\r\n`));
|
|
200
|
+
|
|
201
|
+
const body = new ReadableStream<Uint8Array>({
|
|
202
|
+
async start(controller) {
|
|
203
|
+
for (const part of parts) {
|
|
204
|
+
if (part instanceof Uint8Array) {
|
|
205
|
+
controller.enqueue(part);
|
|
206
|
+
} else {
|
|
207
|
+
const reader = part.getReader();
|
|
208
|
+
try {
|
|
209
|
+
while (true) {
|
|
210
|
+
const { done, value } = await reader.read();
|
|
211
|
+
if (done) break;
|
|
212
|
+
controller.enqueue(value);
|
|
213
|
+
}
|
|
214
|
+
} finally {
|
|
215
|
+
reader.releaseLock();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
controller.close();
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return { body, contentType: `multipart/form-data; boundary=${boundary}` };
|
|
224
|
+
}
|