@shushed/helpers 0.0.200-v2-20251126121340 → 0.0.200-v2-20251127150547
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.
|
@@ -7,19 +7,30 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const mime_types_1 = require("mime-types");
|
|
9
9
|
const runtime_1 = __importDefault(require("./runtime"));
|
|
10
|
+
const utils_1 = require("./utils");
|
|
10
11
|
class DatoHelper extends runtime_1.default {
|
|
11
12
|
apiToken;
|
|
12
13
|
baseUrl;
|
|
14
|
+
environment;
|
|
15
|
+
root;
|
|
13
16
|
constructor(opts, datoOpts) {
|
|
14
17
|
super(opts);
|
|
15
18
|
this.apiToken = datoOpts.apiToken;
|
|
16
19
|
this.baseUrl = datoOpts.baseUrl || 'https://site-api.datocms.com';
|
|
20
|
+
this.environment = datoOpts.environment || 'main';
|
|
21
|
+
this.root = datoOpts.root;
|
|
22
|
+
}
|
|
23
|
+
handle429(response) {
|
|
24
|
+
if (response.status === 429 && this.root) {
|
|
25
|
+
(0, utils_1.setRetryAfterVariable)(response, { root: this.root });
|
|
26
|
+
}
|
|
17
27
|
}
|
|
18
28
|
async requestUploadParameters(filename) {
|
|
19
29
|
const response = await fetch(`${this.baseUrl}/upload-requests`, {
|
|
20
30
|
method: 'POST',
|
|
21
31
|
headers: {
|
|
22
32
|
'X-Api-Version': '3',
|
|
33
|
+
'X-Environment': this.environment,
|
|
23
34
|
'Authorization': `Bearer ${this.apiToken}`,
|
|
24
35
|
'Content-Type': "application/vnd.api+json",
|
|
25
36
|
'Accept': 'application/json',
|
|
@@ -33,6 +44,7 @@ class DatoHelper extends runtime_1.default {
|
|
|
33
44
|
},
|
|
34
45
|
}),
|
|
35
46
|
});
|
|
47
|
+
this.handle429(response);
|
|
36
48
|
if (!response.ok) {
|
|
37
49
|
const errorText = await response.text();
|
|
38
50
|
this.logging.error(`Failed to request upload parameters: ${response.statusText} - ${errorText}`);
|
|
@@ -69,6 +81,11 @@ class DatoHelper extends runtime_1.default {
|
|
|
69
81
|
return response;
|
|
70
82
|
}
|
|
71
83
|
async createAssetFromUpload(uploadId, opts, uploadCollectionId) {
|
|
84
|
+
const normalizedTags = Array.isArray(opts.tags) && opts.tags.length > 0
|
|
85
|
+
? opts.tags
|
|
86
|
+
: ["integrations"];
|
|
87
|
+
const safeTitle = opts.title && opts.title.trim().length > 0 ? opts.title : "untitled";
|
|
88
|
+
const safeAlt = opts.alt && opts.alt.trim().length > 0 ? opts.alt : safeTitle;
|
|
72
89
|
const body = {
|
|
73
90
|
data: {
|
|
74
91
|
type: 'upload',
|
|
@@ -77,13 +94,35 @@ class DatoHelper extends runtime_1.default {
|
|
|
77
94
|
author: opts.author,
|
|
78
95
|
copyright: opts.copyright,
|
|
79
96
|
notes: opts.notes || null,
|
|
80
|
-
|
|
97
|
+
title: safeTitle,
|
|
98
|
+
alt: safeAlt,
|
|
99
|
+
default_field_metadata: {
|
|
100
|
+
"en-GB": {
|
|
101
|
+
title: safeTitle,
|
|
102
|
+
alt: safeAlt,
|
|
103
|
+
custom_data: {
|
|
104
|
+
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
105
|
+
tags: normalizedTags,
|
|
106
|
+
}
|
|
107
|
+
},
|
|
81
108
|
en: {
|
|
82
|
-
title:
|
|
83
|
-
alt:
|
|
109
|
+
title: safeTitle,
|
|
110
|
+
alt: safeAlt,
|
|
111
|
+
custom_data: {
|
|
112
|
+
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
113
|
+
tags: normalizedTags,
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"en-US": {
|
|
117
|
+
title: safeTitle,
|
|
118
|
+
alt: safeAlt,
|
|
119
|
+
custom_data: {
|
|
120
|
+
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
121
|
+
tags: normalizedTags,
|
|
122
|
+
}
|
|
84
123
|
}
|
|
85
124
|
},
|
|
86
|
-
tags:
|
|
125
|
+
tags: normalizedTags || [],
|
|
87
126
|
},
|
|
88
127
|
},
|
|
89
128
|
};
|
|
@@ -103,10 +142,12 @@ class DatoHelper extends runtime_1.default {
|
|
|
103
142
|
'Authorization': `Bearer ${this.apiToken}`,
|
|
104
143
|
'Accept': 'application/json',
|
|
105
144
|
'X-Api-Version': '3',
|
|
145
|
+
'X-Environment': this.environment,
|
|
106
146
|
'Content-Type': 'application/vnd.api+json',
|
|
107
147
|
},
|
|
108
148
|
body: JSON.stringify(body),
|
|
109
149
|
});
|
|
150
|
+
this.handle429(response);
|
|
110
151
|
if (!response.ok) {
|
|
111
152
|
const errorText = await response.text();
|
|
112
153
|
this.logging.error(`Failed to create asset from upload: ${response.statusText} - ${errorText}`);
|
|
@@ -121,8 +162,10 @@ class DatoHelper extends runtime_1.default {
|
|
|
121
162
|
'Authorization': `Bearer ${this.apiToken}`,
|
|
122
163
|
'Accept': 'application/json',
|
|
123
164
|
'X-Api-Version': '3',
|
|
165
|
+
'X-Environment': this.environment
|
|
124
166
|
},
|
|
125
167
|
});
|
|
168
|
+
this.handle429(response);
|
|
126
169
|
if (!response.ok) {
|
|
127
170
|
if (response.status === 404) {
|
|
128
171
|
return null;
|
|
@@ -150,8 +193,120 @@ class DatoHelper extends runtime_1.default {
|
|
|
150
193
|
this.logging.error(timeoutMsg);
|
|
151
194
|
throw new Error(timeoutMsg);
|
|
152
195
|
}
|
|
196
|
+
updateUpload = async (options) => {
|
|
197
|
+
try {
|
|
198
|
+
this.logging.log(`Starting update upload - ${JSON.stringify(options)}`);
|
|
199
|
+
if (!options.id)
|
|
200
|
+
throw new Error("updateUpload requires 'id' in options");
|
|
201
|
+
const safeTitle = options.title && options.title.trim().length > 0
|
|
202
|
+
? options.title.trim()
|
|
203
|
+
: options.filename?.trim().split(".")[0] || "untitled";
|
|
204
|
+
const safeAlt = options.alt && options.alt.trim().length > 0
|
|
205
|
+
? options.alt.trim()
|
|
206
|
+
: safeTitle;
|
|
207
|
+
const normalizedTags = Array.isArray(options.tags) && options.tags.length > 0
|
|
208
|
+
? options.tags
|
|
209
|
+
: ["integrations"];
|
|
210
|
+
const body = {
|
|
211
|
+
data: {
|
|
212
|
+
type: "upload",
|
|
213
|
+
id: options.id,
|
|
214
|
+
attributes: {
|
|
215
|
+
author: options.author,
|
|
216
|
+
copyright: options.copyright,
|
|
217
|
+
tags: normalizedTags,
|
|
218
|
+
notes: options.notes || null,
|
|
219
|
+
default_field_metadata: {
|
|
220
|
+
"en-GB": {
|
|
221
|
+
title: safeTitle,
|
|
222
|
+
alt: safeAlt,
|
|
223
|
+
custom_data: {
|
|
224
|
+
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
225
|
+
tags: normalizedTags,
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
en: {
|
|
229
|
+
title: safeTitle,
|
|
230
|
+
alt: safeAlt,
|
|
231
|
+
custom_data: {
|
|
232
|
+
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
233
|
+
tags: normalizedTags,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
"en-US": {
|
|
237
|
+
title: safeTitle,
|
|
238
|
+
alt: safeAlt,
|
|
239
|
+
custom_data: {
|
|
240
|
+
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
241
|
+
tags: normalizedTags,
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
if (options.uploadCollectionId) {
|
|
249
|
+
body.data.relationships = {
|
|
250
|
+
upload_collection: {
|
|
251
|
+
data: {
|
|
252
|
+
type: "upload_collection",
|
|
253
|
+
id: options.uploadCollectionId,
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
const response = await fetch(`${this.baseUrl}/uploads/${options.id}`, {
|
|
259
|
+
method: "PATCH",
|
|
260
|
+
headers: {
|
|
261
|
+
Authorization: `Bearer ${this.apiToken}`,
|
|
262
|
+
"X-Api-Version": "3",
|
|
263
|
+
"X-Environment": this.environment,
|
|
264
|
+
"Content-Type": "application/vnd.api+json",
|
|
265
|
+
Accept: "application/json",
|
|
266
|
+
},
|
|
267
|
+
body: JSON.stringify(body),
|
|
268
|
+
});
|
|
269
|
+
this.handle429(response);
|
|
270
|
+
if (!response.ok) {
|
|
271
|
+
const errorText = await response.text();
|
|
272
|
+
this.logging.error(`Failed to update upload: ${response.statusText} - ${errorText}`);
|
|
273
|
+
throw new Error(`Failed to update upload: ${response.statusText} - ${errorText}`);
|
|
274
|
+
}
|
|
275
|
+
const updated = await response.json();
|
|
276
|
+
if (Array.isArray(updated.data) && updated.data[0]?.type === "api_error") {
|
|
277
|
+
const errorCode = updated.data[0].attributes?.code || "UNKNOWN_ERROR";
|
|
278
|
+
const errorMsg = `DatoCMS API error: ${errorCode}`;
|
|
279
|
+
this.logging.error(errorMsg);
|
|
280
|
+
throw new Error(errorMsg);
|
|
281
|
+
}
|
|
282
|
+
if (updated.data?.type === "job") {
|
|
283
|
+
this.logging.log(`Waiting for job completion: ${updated.data.id}`);
|
|
284
|
+
const jobPayload = await this.waitForJobCompletion(updated.data.id);
|
|
285
|
+
const assetId = jobPayload?.data?.id;
|
|
286
|
+
const assetUrl = jobPayload?.data?.attributes?.url;
|
|
287
|
+
if (!assetId || !assetUrl) {
|
|
288
|
+
throw new Error(`Job completed but payload missing expected fields: ${JSON.stringify(jobPayload)}`);
|
|
289
|
+
}
|
|
290
|
+
this.logging.log(`Job completed for upload: ${assetId}`);
|
|
291
|
+
return { success: true, assetId, upload: assetUrl };
|
|
292
|
+
}
|
|
293
|
+
if (updated.data?.type === "upload") {
|
|
294
|
+
const { id, attributes } = updated.data;
|
|
295
|
+
this.logging.log(`Upload updated successfully: ${id}`);
|
|
296
|
+
return { success: true, assetId: id, upload: attributes.url };
|
|
297
|
+
}
|
|
298
|
+
const errMsg = `Unexpected update response: ${JSON.stringify(updated)}`;
|
|
299
|
+
this.logging.error(errMsg);
|
|
300
|
+
throw new Error(errMsg);
|
|
301
|
+
}
|
|
302
|
+
catch (err) {
|
|
303
|
+
const msg = err?.message ?? JSON.stringify(err);
|
|
304
|
+
this.logging.error(`[updateUpload] failed: ${msg}`);
|
|
305
|
+
throw new Error(msg);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
153
308
|
async uploadFromUrl(options) {
|
|
154
|
-
const { url, filename, uploadCollectionId } = options;
|
|
309
|
+
const { url, filename, uploadCollectionId, skipCreationIfAlreadyExists } = options;
|
|
155
310
|
this.logging.log(`Starting upload from URL: ${url}`);
|
|
156
311
|
const fileResponse = await fetch(url);
|
|
157
312
|
if (!fileResponse.ok) {
|
|
@@ -159,9 +314,43 @@ class DatoHelper extends runtime_1.default {
|
|
|
159
314
|
this.logging.error(errorMsg);
|
|
160
315
|
throw new Error(errorMsg);
|
|
161
316
|
}
|
|
162
|
-
let finalFilename = filename || path_1.default.basename(new URL(url).pathname) ||
|
|
163
|
-
|
|
164
|
-
|
|
317
|
+
let finalFilename = filename || path_1.default.basename(new URL(url).pathname) ||
|
|
318
|
+
`bs-${this.triggerId}-${Date.now()}`;
|
|
319
|
+
if (filename && !path_1.default.extname(finalFilename)) {
|
|
320
|
+
const urlObj = new URL(url);
|
|
321
|
+
const pathname = urlObj.pathname;
|
|
322
|
+
if (pathname.includes(".")) {
|
|
323
|
+
const urlExtension = path_1.default.extname(pathname);
|
|
324
|
+
if (urlExtension) {
|
|
325
|
+
finalFilename += urlExtension;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (!path_1.default.extname(finalFilename) && fileResponse.headers.has('content-type')) {
|
|
329
|
+
const ext = (0, mime_types_1.extension)(fileResponse.headers.get('content-type'));
|
|
330
|
+
if (typeof ext === "string" && ext.length > 0) {
|
|
331
|
+
finalFilename += `.${ext}`;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
else if (!filename) {
|
|
336
|
+
if (!path_1.default.extname(finalFilename) && fileResponse.headers.has('content-type')) {
|
|
337
|
+
const ext = (0, mime_types_1.extension)(fileResponse.headers.get('content-type'));
|
|
338
|
+
if (typeof ext === "string" && ext.length > 0) {
|
|
339
|
+
finalFilename += `.${ext}`;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (skipCreationIfAlreadyExists) {
|
|
344
|
+
const existing = await this.findUploadByFilename(finalFilename);
|
|
345
|
+
if (existing) {
|
|
346
|
+
this.logging.log(`Skipping upload: existing asset found for "${finalFilename}"`);
|
|
347
|
+
return {
|
|
348
|
+
success: true,
|
|
349
|
+
assetId: existing.id,
|
|
350
|
+
upload: existing.attributes.url,
|
|
351
|
+
skipped: true,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
165
354
|
}
|
|
166
355
|
const uploadRequest = await this.requestUploadParameters(finalFilename);
|
|
167
356
|
const uploadId = uploadRequest.data.id;
|
|
@@ -178,6 +367,7 @@ class DatoHelper extends runtime_1.default {
|
|
|
178
367
|
success: true,
|
|
179
368
|
upload: assetPayload.data.attributes.url,
|
|
180
369
|
assetId: assetPayload.data.id,
|
|
370
|
+
skipped: false,
|
|
181
371
|
};
|
|
182
372
|
}
|
|
183
373
|
async uploadFromFile(options) {
|
|
@@ -209,10 +399,12 @@ class DatoHelper extends runtime_1.default {
|
|
|
209
399
|
method: 'GET',
|
|
210
400
|
headers: {
|
|
211
401
|
'X-Api-Version': '3',
|
|
402
|
+
'X-Environment': this.environment,
|
|
212
403
|
'Authorization': `Bearer ${this.apiToken}`,
|
|
213
404
|
'Accept': 'application/json',
|
|
214
405
|
},
|
|
215
406
|
});
|
|
407
|
+
this.handle429(response);
|
|
216
408
|
if (!response.ok) {
|
|
217
409
|
const errorText = await response.text();
|
|
218
410
|
this.logging.error(`Failed to fetch upload collections: ${response.statusText} - ${errorText}`);
|
|
@@ -256,5 +448,209 @@ class DatoHelper extends runtime_1.default {
|
|
|
256
448
|
const query = params.toString();
|
|
257
449
|
return query ? `${base}?${query}` : base;
|
|
258
450
|
}
|
|
451
|
+
async findUploadByFilename(filename) {
|
|
452
|
+
try {
|
|
453
|
+
if (!filename || typeof filename !== 'string') {
|
|
454
|
+
this.logging.log(`findUploadByFilename called with invalid filename: ${String(filename)}`);
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
const normalizedTarget = filename.trim().toLowerCase().replace(/\.[^.]+$/, '');
|
|
458
|
+
const query = encodeURIComponent(normalizedTarget);
|
|
459
|
+
this.logging.log(`Searching uploads with query="${normalizedTarget}" for filename="${filename}"`);
|
|
460
|
+
const response = await fetch(`${this.baseUrl}/uploads?filter[query]=${query}&page[limit]=100`, {
|
|
461
|
+
headers: {
|
|
462
|
+
'Authorization': `Bearer ${this.apiToken}`,
|
|
463
|
+
'X-Api-Version': '3',
|
|
464
|
+
'X-Environment': this.environment,
|
|
465
|
+
'Accept': 'application/json',
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
this.handle429(response);
|
|
469
|
+
if (!response.ok) {
|
|
470
|
+
const errorText = await response.text();
|
|
471
|
+
this.logging.error(`Failed to search for existing upload: ${response.status} ${response.statusText} - ${errorText}`);
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
const data = await response.json().catch((err) => {
|
|
475
|
+
this.logging.error(`Failed to parse JSON in findUploadByFilename: ${err.message}`);
|
|
476
|
+
return null;
|
|
477
|
+
});
|
|
478
|
+
const uploads = data?.data || [];
|
|
479
|
+
if (!uploads.length) {
|
|
480
|
+
this.logging.log(`No uploads returned for query="${normalizedTarget}"`);
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
const targetFull = filename.trim().toLowerCase();
|
|
484
|
+
const match = uploads.find((u) => {
|
|
485
|
+
const storedFull = u?.attributes?.filename?.toLowerCase();
|
|
486
|
+
if (!storedFull)
|
|
487
|
+
return false;
|
|
488
|
+
if (storedFull === targetFull)
|
|
489
|
+
return true;
|
|
490
|
+
const storedNormalized = storedFull.replace(/\.[^.]+$/, '');
|
|
491
|
+
if (storedNormalized === normalizedTarget)
|
|
492
|
+
return true;
|
|
493
|
+
return false;
|
|
494
|
+
});
|
|
495
|
+
if (match) {
|
|
496
|
+
this.logging.log(`Found existing upload for "${filename}" -> asset_id=${match.id}, storedFilename=${match.attributes?.filename}`);
|
|
497
|
+
return match;
|
|
498
|
+
}
|
|
499
|
+
this.logging.log(`No exact filename match found among ${uploads.length} uploads for "${filename}" (query="${normalizedTarget}")`);
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
catch (err) {
|
|
503
|
+
this.logging.error(`Error in findUploadByFilename: ${err?.message ?? String(err)}`);
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
async getItemTypeId(apiKey) {
|
|
508
|
+
try {
|
|
509
|
+
const res = await fetch(`${this.baseUrl}/item-types`, {
|
|
510
|
+
method: "GET",
|
|
511
|
+
headers: {
|
|
512
|
+
Authorization: `Bearer ${this.apiToken}`,
|
|
513
|
+
"X-Api-Version": "3",
|
|
514
|
+
"X-Environment": this.environment,
|
|
515
|
+
Accept: "application/json",
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
this.handle429(res);
|
|
519
|
+
if (!res.ok) {
|
|
520
|
+
const errorText = await res.text();
|
|
521
|
+
throw new Error(`Failed to fetch item types: ${res.status} ${res.statusText} - ${errorText}`);
|
|
522
|
+
}
|
|
523
|
+
const data = await res.json();
|
|
524
|
+
const type = data.data.find((t) => t.attributes.api_key === apiKey);
|
|
525
|
+
if (!type)
|
|
526
|
+
throw new Error(`Item type "${apiKey}" not found in ${this.environment} - ${JSON.stringify(data)}`);
|
|
527
|
+
return type.id;
|
|
528
|
+
}
|
|
529
|
+
catch (err) {
|
|
530
|
+
this.logging.error(`getItemTypeId failed: ${err.message}`);
|
|
531
|
+
throw err;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async getProductBySku(sku) {
|
|
535
|
+
try {
|
|
536
|
+
const encodedSku = encodeURIComponent(sku);
|
|
537
|
+
const res = await fetch(`${this.baseUrl}/items?filter[type]=product&filter[fields][sku][eq]=${encodedSku}&version=current`, {
|
|
538
|
+
headers: {
|
|
539
|
+
Authorization: `Bearer ${this.apiToken}`,
|
|
540
|
+
"X-Api-Version": "3",
|
|
541
|
+
"X-Environment": this.environment,
|
|
542
|
+
"Content-Type": "application/vnd.api+json",
|
|
543
|
+
Accept: "application/json",
|
|
544
|
+
},
|
|
545
|
+
});
|
|
546
|
+
this.handle429(res);
|
|
547
|
+
if (!res.ok) {
|
|
548
|
+
const errorText = await res.text();
|
|
549
|
+
throw new Error(`Failed to fetch product by SKU: ${res.status} ${res.statusText} - ${errorText}`);
|
|
550
|
+
}
|
|
551
|
+
return await res.json();
|
|
552
|
+
}
|
|
553
|
+
catch (err) {
|
|
554
|
+
this.logging.error(`getProductBySku failed: ${err.message}`);
|
|
555
|
+
throw err;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async createProduct(sku, productTypeId) {
|
|
559
|
+
try {
|
|
560
|
+
const res = await fetch(`${this.baseUrl}/items`, {
|
|
561
|
+
method: "POST",
|
|
562
|
+
headers: {
|
|
563
|
+
Authorization: `Bearer ${this.apiToken}`,
|
|
564
|
+
"X-Api-Version": "3",
|
|
565
|
+
"X-Environment": this.environment,
|
|
566
|
+
"Content-Type": "application/vnd.api+json",
|
|
567
|
+
Accept: "application/json",
|
|
568
|
+
},
|
|
569
|
+
body: JSON.stringify({
|
|
570
|
+
data: {
|
|
571
|
+
type: "item",
|
|
572
|
+
attributes: { sku },
|
|
573
|
+
relationships: {
|
|
574
|
+
item_type: { data: { type: "item_type", id: productTypeId } },
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
}),
|
|
578
|
+
});
|
|
579
|
+
this.handle429(res);
|
|
580
|
+
if (!res.ok) {
|
|
581
|
+
const errorText = await res.text();
|
|
582
|
+
throw new Error(`Failed to create product: ${res.status} ${res.statusText} - ${errorText}`);
|
|
583
|
+
}
|
|
584
|
+
return await res.json();
|
|
585
|
+
}
|
|
586
|
+
catch (err) {
|
|
587
|
+
this.logging.error(`createProduct failed: ${err.message}`);
|
|
588
|
+
throw err;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
async updateProductGallery(productId, gallery) {
|
|
592
|
+
try {
|
|
593
|
+
const res = await fetch(`${this.baseUrl}/items/${productId}`, {
|
|
594
|
+
method: "PATCH",
|
|
595
|
+
headers: {
|
|
596
|
+
Authorization: `Bearer ${this.apiToken}`,
|
|
597
|
+
"X-Api-Version": "3",
|
|
598
|
+
"X-Environment": this.environment,
|
|
599
|
+
"Content-Type": "application/vnd.api+json",
|
|
600
|
+
Accept: "application/json",
|
|
601
|
+
},
|
|
602
|
+
body: JSON.stringify({
|
|
603
|
+
data: {
|
|
604
|
+
type: "item",
|
|
605
|
+
id: productId,
|
|
606
|
+
attributes: { gallery },
|
|
607
|
+
},
|
|
608
|
+
}),
|
|
609
|
+
});
|
|
610
|
+
this.handle429(res);
|
|
611
|
+
if (!res.ok) {
|
|
612
|
+
const errorText = await res.text();
|
|
613
|
+
throw new Error(`Failed to update product gallery: ${res.status} ${res.statusText} - ${errorText}`);
|
|
614
|
+
}
|
|
615
|
+
return await res.json();
|
|
616
|
+
}
|
|
617
|
+
catch (err) {
|
|
618
|
+
this.logging.error(`updateProductGallery failed: ${err.message}`);
|
|
619
|
+
throw err;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
async updateProductSwatch(productId, uploadId) {
|
|
623
|
+
try {
|
|
624
|
+
const res = await fetch(`${this.baseUrl}/items/${productId}`, {
|
|
625
|
+
method: "PATCH",
|
|
626
|
+
headers: {
|
|
627
|
+
Authorization: `Bearer ${this.apiToken}`,
|
|
628
|
+
"X-Api-Version": "3",
|
|
629
|
+
"X-Environment": this.environment,
|
|
630
|
+
"Content-Type": "application/vnd.api+json",
|
|
631
|
+
Accept: "application/json",
|
|
632
|
+
},
|
|
633
|
+
body: JSON.stringify({
|
|
634
|
+
data: {
|
|
635
|
+
type: "item",
|
|
636
|
+
id: productId,
|
|
637
|
+
attributes: {
|
|
638
|
+
swatch: { upload_id: uploadId },
|
|
639
|
+
},
|
|
640
|
+
},
|
|
641
|
+
}),
|
|
642
|
+
});
|
|
643
|
+
this.handle429(res);
|
|
644
|
+
if (!res.ok) {
|
|
645
|
+
const errorText = await res.text();
|
|
646
|
+
throw new Error(`Failed to update product swatch: ${res.status} ${res.statusText} - ${errorText}`);
|
|
647
|
+
}
|
|
648
|
+
return await res.json();
|
|
649
|
+
}
|
|
650
|
+
catch (err) {
|
|
651
|
+
this.logging.error(`updateProductSwatch failed: ${err.message}`);
|
|
652
|
+
throw err;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
259
655
|
}
|
|
260
656
|
exports.default = DatoHelper;
|
|
@@ -92,6 +92,9 @@ class Runtime {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
getRuntimeUrl(options) {
|
|
95
|
+
if (options.runtimeUrl && this.isDeployment) {
|
|
96
|
+
return options.runtimeUrl;
|
|
97
|
+
}
|
|
95
98
|
const systemEnvName = this.getSystemEnvName(options);
|
|
96
99
|
const [_, ...idParts] = (process.env.GCLOUD_PROJECT || '').split('-');
|
|
97
100
|
if (process.env.RUNTIME_URL) {
|
|
@@ -130,6 +133,9 @@ class Runtime {
|
|
|
130
133
|
}
|
|
131
134
|
return systemEnvName || 'test-env';
|
|
132
135
|
}
|
|
136
|
+
get isDeployment() {
|
|
137
|
+
return process.env.K_SERVICE === 'manager';
|
|
138
|
+
}
|
|
133
139
|
}
|
|
134
140
|
exports.default = Runtime;
|
|
135
141
|
;
|
|
@@ -2,21 +2,54 @@ import Runtime, { Opts as RuntimeOpts } from './runtime';
|
|
|
2
2
|
interface DatoHelperOptions {
|
|
3
3
|
apiToken: string;
|
|
4
4
|
baseUrl?: string;
|
|
5
|
+
environment?: string;
|
|
6
|
+
root?: {
|
|
7
|
+
state: Record<string, any>;
|
|
8
|
+
inputs: any;
|
|
9
|
+
};
|
|
5
10
|
}
|
|
6
11
|
interface UploadResult {
|
|
7
12
|
success: boolean;
|
|
8
13
|
upload: string;
|
|
9
14
|
assetId?: string;
|
|
15
|
+
skipped?: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface UploadReturn {
|
|
18
|
+
id: string;
|
|
19
|
+
type: string;
|
|
20
|
+
attributes: {
|
|
21
|
+
url: string;
|
|
22
|
+
filename: string;
|
|
23
|
+
[key: string]: any;
|
|
24
|
+
};
|
|
10
25
|
}
|
|
11
26
|
export default class DatoHelper extends Runtime {
|
|
12
27
|
private apiToken;
|
|
13
28
|
private baseUrl;
|
|
29
|
+
private environment;
|
|
30
|
+
private root?;
|
|
14
31
|
constructor(opts: RuntimeOpts, datoOpts: DatoHelperOptions);
|
|
32
|
+
private handle429;
|
|
15
33
|
private requestUploadParameters;
|
|
16
34
|
private uploadToStorageBucket;
|
|
17
35
|
private createAssetFromUpload;
|
|
18
36
|
private checkJobResult;
|
|
19
37
|
private waitForJobCompletion;
|
|
38
|
+
updateUpload: (options: {
|
|
39
|
+
id: string;
|
|
40
|
+
filename?: string;
|
|
41
|
+
tags: string[];
|
|
42
|
+
notes?: string;
|
|
43
|
+
title: string;
|
|
44
|
+
alt: string;
|
|
45
|
+
author: string;
|
|
46
|
+
copyright: string;
|
|
47
|
+
uploadCollectionId?: string;
|
|
48
|
+
}) => Promise<{
|
|
49
|
+
success: boolean;
|
|
50
|
+
assetId: string;
|
|
51
|
+
upload: string;
|
|
52
|
+
}>;
|
|
20
53
|
uploadFromUrl(options: {
|
|
21
54
|
url: string;
|
|
22
55
|
copyright: string;
|
|
@@ -51,5 +84,11 @@ export default class DatoHelper extends Runtime {
|
|
|
51
84
|
quality?: number;
|
|
52
85
|
format?: 'jpg' | 'pjpg' | 'png' | 'webp' | 'avif' | 'gif' | 'jxl' | 'jp2' | 'jxr' | 'json' | 'blurhash' | (string & {});
|
|
53
86
|
}): string;
|
|
87
|
+
findUploadByFilename(filename: string): Promise<UploadReturn | null>;
|
|
88
|
+
getItemTypeId(apiKey: string): Promise<string>;
|
|
89
|
+
getProductBySku(sku: string): Promise<any>;
|
|
90
|
+
createProduct(sku: string, productTypeId: string): Promise<any>;
|
|
91
|
+
updateProductGallery(productId: string, gallery: any[]): Promise<any>;
|
|
92
|
+
updateProductSwatch(productId: string, uploadId: string): Promise<any>;
|
|
54
93
|
}
|
|
55
94
|
export {};
|