@shushed/helpers 0.0.200-runtime-url-fix-20251125155626 → 0.0.200-runtime-url-fix2-20251125155916
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/dist/cjs/dist-dereferenced/messages/order/delivered.js +1 -1
- package/dist/cjs/dist-dereferenced/messages/order/new.js +1 -1
- package/dist/cjs/dist-dereferenced/messages/order/processed.js +1 -1
- package/dist/cjs/dist-dereferenced/messages/order/return-initiated.js +1 -1
- package/dist/cjs/dist-dereferenced/messages/order/returned.js +1 -1
- package/dist/cjs/dist-dereferenced/messages/order/shipped.js +1 -1
- package/dist/cjs/dist-dereferenced/messages/price-change.js +1 -1
- package/dist/cjs/dist-dereferenced/order/orderMain.js +1 -1
- package/dist/cjs/dist-dereferenced/order/payment.js +1 -1
- package/dist/cjs/dist-dereferenced/order/shipment/pos/outbound.js +1 -1
- package/dist/cjs/dist-dereferenced/order/shipment/pos/return.js +1 -1
- package/dist/cjs/dist-dereferenced/price.js +1 -1
- package/dist/cjs/src-public/airtable.js +54 -26
- package/dist/cjs/src-public/dato.js +8 -385
- package/dist/cjs/src-public/runtime.js +2 -2
- package/dist/cjs/src-public/utils.js +119 -1
- package/dist/types/dist-dereferenced/messages/order/delivered.d.ts +5 -23
- package/dist/types/dist-dereferenced/messages/order/new.d.ts +5 -23
- package/dist/types/dist-dereferenced/messages/order/processed.d.ts +5 -23
- package/dist/types/dist-dereferenced/messages/order/return-initiated.d.ts +5 -23
- package/dist/types/dist-dereferenced/messages/order/returned.d.ts +5 -23
- package/dist/types/dist-dereferenced/messages/order/shipped.d.ts +5 -23
- package/dist/types/dist-dereferenced/messages/price-change.d.ts +29 -20
- package/dist/types/dist-dereferenced/order/index.d.ts +5 -23
- package/dist/types/dist-dereferenced/order/orderMain.d.ts +5 -23
- package/dist/types/dist-dereferenced/order/payment.d.ts +0 -16
- package/dist/types/dist-dereferenced/order/shipment/pos/outbound.d.ts +1 -0
- package/dist/types/dist-dereferenced/order/shipment/pos/return.d.ts +1 -0
- package/dist/types/dist-dereferenced/price.d.ts +29 -20
- package/dist/types/dist-types/messages/order/delivered.d.ts +1 -7
- package/dist/types/dist-types/messages/order/new.d.ts +1 -7
- package/dist/types/dist-types/messages/order/processed.d.ts +1 -7
- package/dist/types/dist-types/messages/order/return-initiated.d.ts +1 -7
- package/dist/types/dist-types/messages/order/returned.d.ts +1 -7
- package/dist/types/dist-types/messages/order/shipped.d.ts +1 -7
- package/dist/types/dist-types/messages/price-change.d.ts +6 -5
- package/dist/types/dist-types/order/orderMain.d.ts +1 -7
- package/dist/types/dist-types/order/payment.d.ts +0 -4
- package/dist/types/dist-types/price.d.ts +6 -5
- package/dist/types/src-public/airtable.d.ts +1 -1
- package/dist/types/src-public/dato.d.ts +0 -33
- package/dist/types/src-public/utils.d.ts +34 -0
- package/package.json +1 -1
|
@@ -10,19 +10,16 @@ const runtime_1 = __importDefault(require("./runtime"));
|
|
|
10
10
|
class DatoHelper extends runtime_1.default {
|
|
11
11
|
apiToken;
|
|
12
12
|
baseUrl;
|
|
13
|
-
environment;
|
|
14
13
|
constructor(opts, datoOpts) {
|
|
15
14
|
super(opts);
|
|
16
15
|
this.apiToken = datoOpts.apiToken;
|
|
17
16
|
this.baseUrl = datoOpts.baseUrl || 'https://site-api.datocms.com';
|
|
18
|
-
this.environment = datoOpts.environment || 'main';
|
|
19
17
|
}
|
|
20
18
|
async requestUploadParameters(filename) {
|
|
21
19
|
const response = await fetch(`${this.baseUrl}/upload-requests`, {
|
|
22
20
|
method: 'POST',
|
|
23
21
|
headers: {
|
|
24
22
|
'X-Api-Version': '3',
|
|
25
|
-
'X-Environment': this.environment,
|
|
26
23
|
'Authorization': `Bearer ${this.apiToken}`,
|
|
27
24
|
'Content-Type': "application/vnd.api+json",
|
|
28
25
|
'Accept': 'application/json',
|
|
@@ -72,11 +69,6 @@ class DatoHelper extends runtime_1.default {
|
|
|
72
69
|
return response;
|
|
73
70
|
}
|
|
74
71
|
async createAssetFromUpload(uploadId, opts, uploadCollectionId) {
|
|
75
|
-
const normalizedTags = Array.isArray(opts.tags) && opts.tags.length > 0
|
|
76
|
-
? opts.tags
|
|
77
|
-
: ["integrations"];
|
|
78
|
-
const safeTitle = opts.title && opts.title.trim().length > 0 ? opts.title : "untitled";
|
|
79
|
-
const safeAlt = opts.alt && opts.alt.trim().length > 0 ? opts.alt : safeTitle;
|
|
80
72
|
const body = {
|
|
81
73
|
data: {
|
|
82
74
|
type: 'upload',
|
|
@@ -85,35 +77,13 @@ class DatoHelper extends runtime_1.default {
|
|
|
85
77
|
author: opts.author,
|
|
86
78
|
copyright: opts.copyright,
|
|
87
79
|
notes: opts.notes || null,
|
|
88
|
-
|
|
89
|
-
alt: safeAlt,
|
|
90
|
-
default_field_metadata: {
|
|
91
|
-
"en-GB": {
|
|
92
|
-
title: safeTitle,
|
|
93
|
-
alt: safeAlt,
|
|
94
|
-
custom_data: {
|
|
95
|
-
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
96
|
-
tags: normalizedTags,
|
|
97
|
-
}
|
|
98
|
-
},
|
|
80
|
+
defaultFieldMetadata: {
|
|
99
81
|
en: {
|
|
100
|
-
title:
|
|
101
|
-
alt:
|
|
102
|
-
custom_data: {
|
|
103
|
-
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
104
|
-
tags: normalizedTags,
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
"en-US": {
|
|
108
|
-
title: safeTitle,
|
|
109
|
-
alt: safeAlt,
|
|
110
|
-
custom_data: {
|
|
111
|
-
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
112
|
-
tags: normalizedTags,
|
|
113
|
-
}
|
|
82
|
+
title: opts.title,
|
|
83
|
+
alt: opts.alt,
|
|
114
84
|
}
|
|
115
85
|
},
|
|
116
|
-
tags:
|
|
86
|
+
tags: opts.tags || [],
|
|
117
87
|
},
|
|
118
88
|
},
|
|
119
89
|
};
|
|
@@ -133,7 +103,6 @@ class DatoHelper extends runtime_1.default {
|
|
|
133
103
|
'Authorization': `Bearer ${this.apiToken}`,
|
|
134
104
|
'Accept': 'application/json',
|
|
135
105
|
'X-Api-Version': '3',
|
|
136
|
-
'X-Environment': this.environment,
|
|
137
106
|
'Content-Type': 'application/vnd.api+json',
|
|
138
107
|
},
|
|
139
108
|
body: JSON.stringify(body),
|
|
@@ -152,7 +121,6 @@ class DatoHelper extends runtime_1.default {
|
|
|
152
121
|
'Authorization': `Bearer ${this.apiToken}`,
|
|
153
122
|
'Accept': 'application/json',
|
|
154
123
|
'X-Api-Version': '3',
|
|
155
|
-
'X-Environment': this.environment
|
|
156
124
|
},
|
|
157
125
|
});
|
|
158
126
|
if (!response.ok) {
|
|
@@ -182,119 +150,8 @@ class DatoHelper extends runtime_1.default {
|
|
|
182
150
|
this.logging.error(timeoutMsg);
|
|
183
151
|
throw new Error(timeoutMsg);
|
|
184
152
|
}
|
|
185
|
-
updateUpload = async (options) => {
|
|
186
|
-
try {
|
|
187
|
-
this.logging.log(`Starting update upload - ${JSON.stringify(options)}`);
|
|
188
|
-
if (!options.id)
|
|
189
|
-
throw new Error("updateUpload requires 'id' in options");
|
|
190
|
-
const safeTitle = options.title && options.title.trim().length > 0
|
|
191
|
-
? options.title.trim()
|
|
192
|
-
: options.filename?.trim().split(".")[0] || "untitled";
|
|
193
|
-
const safeAlt = options.alt && options.alt.trim().length > 0
|
|
194
|
-
? options.alt.trim()
|
|
195
|
-
: safeTitle;
|
|
196
|
-
const normalizedTags = Array.isArray(options.tags) && options.tags.length > 0
|
|
197
|
-
? options.tags
|
|
198
|
-
: ["integrations"];
|
|
199
|
-
const body = {
|
|
200
|
-
data: {
|
|
201
|
-
type: "upload",
|
|
202
|
-
id: options.id,
|
|
203
|
-
attributes: {
|
|
204
|
-
author: options.author,
|
|
205
|
-
copyright: options.copyright,
|
|
206
|
-
tags: normalizedTags,
|
|
207
|
-
notes: options.notes || null,
|
|
208
|
-
default_field_metadata: {
|
|
209
|
-
"en-GB": {
|
|
210
|
-
title: safeTitle,
|
|
211
|
-
alt: safeAlt,
|
|
212
|
-
custom_data: {
|
|
213
|
-
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
214
|
-
tags: normalizedTags,
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
en: {
|
|
218
|
-
title: safeTitle,
|
|
219
|
-
alt: safeAlt,
|
|
220
|
-
custom_data: {
|
|
221
|
-
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
222
|
-
tags: normalizedTags,
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
"en-US": {
|
|
226
|
-
title: safeTitle,
|
|
227
|
-
alt: safeAlt,
|
|
228
|
-
custom_data: {
|
|
229
|
-
source: normalizedTags[0] || "uploaded-by-integrations",
|
|
230
|
-
tags: normalizedTags,
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
};
|
|
237
|
-
if (options.uploadCollectionId) {
|
|
238
|
-
body.data.relationships = {
|
|
239
|
-
upload_collection: {
|
|
240
|
-
data: {
|
|
241
|
-
type: "upload_collection",
|
|
242
|
-
id: options.uploadCollectionId,
|
|
243
|
-
},
|
|
244
|
-
},
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
const response = await fetch(`${this.baseUrl}/uploads/${options.id}`, {
|
|
248
|
-
method: "PATCH",
|
|
249
|
-
headers: {
|
|
250
|
-
Authorization: `Bearer ${this.apiToken}`,
|
|
251
|
-
"X-Api-Version": "3",
|
|
252
|
-
"X-Environment": this.environment,
|
|
253
|
-
"Content-Type": "application/vnd.api+json",
|
|
254
|
-
Accept: "application/json",
|
|
255
|
-
},
|
|
256
|
-
body: JSON.stringify(body),
|
|
257
|
-
});
|
|
258
|
-
if (!response.ok) {
|
|
259
|
-
const errorText = await response.text();
|
|
260
|
-
this.logging.error(`Failed to update upload: ${response.statusText} - ${errorText}`);
|
|
261
|
-
throw new Error(`Failed to update upload: ${response.statusText} - ${errorText}`);
|
|
262
|
-
}
|
|
263
|
-
const updated = await response.json();
|
|
264
|
-
if (Array.isArray(updated.data) && updated.data[0]?.type === "api_error") {
|
|
265
|
-
const errorCode = updated.data[0].attributes?.code || "UNKNOWN_ERROR";
|
|
266
|
-
const errorMsg = `DatoCMS API error: ${errorCode}`;
|
|
267
|
-
this.logging.error(errorMsg);
|
|
268
|
-
throw new Error(errorMsg);
|
|
269
|
-
}
|
|
270
|
-
if (updated.data?.type === "job") {
|
|
271
|
-
this.logging.log(`Waiting for job completion: ${updated.data.id}`);
|
|
272
|
-
const jobPayload = await this.waitForJobCompletion(updated.data.id);
|
|
273
|
-
const assetId = jobPayload?.data?.id;
|
|
274
|
-
const assetUrl = jobPayload?.data?.attributes?.url;
|
|
275
|
-
if (!assetId || !assetUrl) {
|
|
276
|
-
throw new Error(`Job completed but payload missing expected fields: ${JSON.stringify(jobPayload)}`);
|
|
277
|
-
}
|
|
278
|
-
this.logging.log(`Job completed for upload: ${assetId}`);
|
|
279
|
-
return { success: true, assetId, upload: assetUrl };
|
|
280
|
-
}
|
|
281
|
-
if (updated.data?.type === "upload") {
|
|
282
|
-
const { id, attributes } = updated.data;
|
|
283
|
-
this.logging.log(`Upload updated successfully: ${id}`);
|
|
284
|
-
return { success: true, assetId: id, upload: attributes.url };
|
|
285
|
-
}
|
|
286
|
-
const errMsg = `Unexpected update response: ${JSON.stringify(updated)}`;
|
|
287
|
-
this.logging.error(errMsg);
|
|
288
|
-
throw new Error(errMsg);
|
|
289
|
-
}
|
|
290
|
-
catch (err) {
|
|
291
|
-
const msg = err?.message ?? JSON.stringify(err);
|
|
292
|
-
this.logging.error(`[updateUpload] failed: ${msg}`);
|
|
293
|
-
throw new Error(msg);
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
153
|
async uploadFromUrl(options) {
|
|
297
|
-
const { url, filename, uploadCollectionId
|
|
154
|
+
const { url, filename, uploadCollectionId } = options;
|
|
298
155
|
this.logging.log(`Starting upload from URL: ${url}`);
|
|
299
156
|
const fileResponse = await fetch(url);
|
|
300
157
|
if (!fileResponse.ok) {
|
|
@@ -302,43 +159,9 @@ class DatoHelper extends runtime_1.default {
|
|
|
302
159
|
this.logging.error(errorMsg);
|
|
303
160
|
throw new Error(errorMsg);
|
|
304
161
|
}
|
|
305
|
-
let finalFilename = filename || path_1.default.basename(new URL(url).pathname) ||
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const urlObj = new URL(url);
|
|
309
|
-
const pathname = urlObj.pathname;
|
|
310
|
-
if (pathname.includes(".")) {
|
|
311
|
-
const urlExtension = path_1.default.extname(pathname);
|
|
312
|
-
if (urlExtension) {
|
|
313
|
-
finalFilename += urlExtension;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (!path_1.default.extname(finalFilename) && fileResponse.headers.has('content-type')) {
|
|
317
|
-
const ext = (0, mime_types_1.extension)(fileResponse.headers.get('content-type'));
|
|
318
|
-
if (typeof ext === "string" && ext.length > 0) {
|
|
319
|
-
finalFilename += `.${ext}`;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
else if (!filename) {
|
|
324
|
-
if (!path_1.default.extname(finalFilename) && fileResponse.headers.has('content-type')) {
|
|
325
|
-
const ext = (0, mime_types_1.extension)(fileResponse.headers.get('content-type'));
|
|
326
|
-
if (typeof ext === "string" && ext.length > 0) {
|
|
327
|
-
finalFilename += `.${ext}`;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
if (skipCreationIfAlreadyExists) {
|
|
332
|
-
const existing = await this.findUploadByFilename(finalFilename);
|
|
333
|
-
if (existing) {
|
|
334
|
-
this.logging.log(`Skipping upload: existing asset found for "${finalFilename}"`);
|
|
335
|
-
return {
|
|
336
|
-
success: true,
|
|
337
|
-
assetId: existing.id,
|
|
338
|
-
upload: existing.attributes.url,
|
|
339
|
-
skipped: true,
|
|
340
|
-
};
|
|
341
|
-
}
|
|
162
|
+
let finalFilename = filename || path_1.default.basename(new URL(url).pathname) || 'bs-' + this.triggerId + '-' + new Date().getTime();
|
|
163
|
+
if (!path_1.default.extname(finalFilename) && fileResponse.headers.has('content-type')) {
|
|
164
|
+
finalFilename += `.${(0, mime_types_1.extension)(fileResponse.headers.get('content-type'))}`;
|
|
342
165
|
}
|
|
343
166
|
const uploadRequest = await this.requestUploadParameters(finalFilename);
|
|
344
167
|
const uploadId = uploadRequest.data.id;
|
|
@@ -355,7 +178,6 @@ class DatoHelper extends runtime_1.default {
|
|
|
355
178
|
success: true,
|
|
356
179
|
upload: assetPayload.data.attributes.url,
|
|
357
180
|
assetId: assetPayload.data.id,
|
|
358
|
-
skipped: false,
|
|
359
181
|
};
|
|
360
182
|
}
|
|
361
183
|
async uploadFromFile(options) {
|
|
@@ -387,7 +209,6 @@ class DatoHelper extends runtime_1.default {
|
|
|
387
209
|
method: 'GET',
|
|
388
210
|
headers: {
|
|
389
211
|
'X-Api-Version': '3',
|
|
390
|
-
'X-Environment': this.environment,
|
|
391
212
|
'Authorization': `Bearer ${this.apiToken}`,
|
|
392
213
|
'Accept': 'application/json',
|
|
393
214
|
},
|
|
@@ -435,203 +256,5 @@ class DatoHelper extends runtime_1.default {
|
|
|
435
256
|
const query = params.toString();
|
|
436
257
|
return query ? `${base}?${query}` : base;
|
|
437
258
|
}
|
|
438
|
-
async findUploadByFilename(filename) {
|
|
439
|
-
try {
|
|
440
|
-
if (!filename || typeof filename !== 'string') {
|
|
441
|
-
this.logging.log(`findUploadByFilename called with invalid filename: ${String(filename)}`);
|
|
442
|
-
return null;
|
|
443
|
-
}
|
|
444
|
-
const normalizedTarget = filename.trim().toLowerCase().replace(/\.[^.]+$/, '');
|
|
445
|
-
const query = encodeURIComponent(normalizedTarget);
|
|
446
|
-
this.logging.log(`Searching uploads with query="${normalizedTarget}" for filename="${filename}"`);
|
|
447
|
-
const response = await fetch(`${this.baseUrl}/uploads?filter[query]=${query}&page[limit]=100`, {
|
|
448
|
-
headers: {
|
|
449
|
-
'Authorization': `Bearer ${this.apiToken}`,
|
|
450
|
-
'X-Api-Version': '3',
|
|
451
|
-
'X-Environment': this.environment,
|
|
452
|
-
'Accept': 'application/json',
|
|
453
|
-
},
|
|
454
|
-
});
|
|
455
|
-
if (!response.ok) {
|
|
456
|
-
const errorText = await response.text();
|
|
457
|
-
this.logging.error(`Failed to search for existing upload: ${response.status} ${response.statusText} - ${errorText}`);
|
|
458
|
-
return null;
|
|
459
|
-
}
|
|
460
|
-
const data = await response.json().catch((err) => {
|
|
461
|
-
this.logging.error(`Failed to parse JSON in findUploadByFilename: ${err.message}`);
|
|
462
|
-
return null;
|
|
463
|
-
});
|
|
464
|
-
const uploads = data?.data || [];
|
|
465
|
-
if (!uploads.length) {
|
|
466
|
-
this.logging.log(`No uploads returned for query="${normalizedTarget}"`);
|
|
467
|
-
return null;
|
|
468
|
-
}
|
|
469
|
-
const targetFull = filename.trim().toLowerCase();
|
|
470
|
-
const match = uploads.find((u) => {
|
|
471
|
-
const storedFull = u?.attributes?.filename?.toLowerCase();
|
|
472
|
-
if (!storedFull)
|
|
473
|
-
return false;
|
|
474
|
-
if (storedFull === targetFull)
|
|
475
|
-
return true;
|
|
476
|
-
const storedNormalized = storedFull.replace(/\.[^.]+$/, '');
|
|
477
|
-
if (storedNormalized === normalizedTarget)
|
|
478
|
-
return true;
|
|
479
|
-
return false;
|
|
480
|
-
});
|
|
481
|
-
if (match) {
|
|
482
|
-
this.logging.log(`Found existing upload for "${filename}" -> asset_id=${match.id}, storedFilename=${match.attributes?.filename}`);
|
|
483
|
-
return match;
|
|
484
|
-
}
|
|
485
|
-
this.logging.log(`No exact filename match found among ${uploads.length} uploads for "${filename}" (query="${normalizedTarget}")`);
|
|
486
|
-
return null;
|
|
487
|
-
}
|
|
488
|
-
catch (err) {
|
|
489
|
-
this.logging.error(`Error in findUploadByFilename: ${err?.message ?? String(err)}`);
|
|
490
|
-
return null;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
async getItemTypeId(apiKey) {
|
|
494
|
-
try {
|
|
495
|
-
const res = await fetch(`${this.baseUrl}/item-types`, {
|
|
496
|
-
method: "GET",
|
|
497
|
-
headers: {
|
|
498
|
-
Authorization: `Bearer ${this.apiToken}`,
|
|
499
|
-
"X-Api-Version": "3",
|
|
500
|
-
"X-Environment": this.environment,
|
|
501
|
-
Accept: "application/json",
|
|
502
|
-
},
|
|
503
|
-
});
|
|
504
|
-
if (!res.ok) {
|
|
505
|
-
const errorText = await res.text();
|
|
506
|
-
throw new Error(`Failed to fetch item types: ${res.status} ${res.statusText} - ${errorText}`);
|
|
507
|
-
}
|
|
508
|
-
const data = await res.json();
|
|
509
|
-
const type = data.data.find((t) => t.attributes.api_key === apiKey);
|
|
510
|
-
if (!type)
|
|
511
|
-
throw new Error(`Item type "${apiKey}" not found in ${this.environment} - ${JSON.stringify(data)}`);
|
|
512
|
-
return type.id;
|
|
513
|
-
}
|
|
514
|
-
catch (err) {
|
|
515
|
-
this.logging.error(`getItemTypeId failed: ${err.message}`);
|
|
516
|
-
throw err;
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
async getProductBySku(sku) {
|
|
520
|
-
try {
|
|
521
|
-
const encodedSku = encodeURIComponent(sku);
|
|
522
|
-
const res = await fetch(`${this.baseUrl}/items?filter[type]=product&filter[fields][sku][eq]=${encodedSku}&version=current`, {
|
|
523
|
-
headers: {
|
|
524
|
-
Authorization: `Bearer ${this.apiToken}`,
|
|
525
|
-
"X-Api-Version": "3",
|
|
526
|
-
"X-Environment": this.environment,
|
|
527
|
-
"Content-Type": "application/vnd.api+json",
|
|
528
|
-
Accept: "application/json",
|
|
529
|
-
},
|
|
530
|
-
});
|
|
531
|
-
if (!res.ok) {
|
|
532
|
-
const errorText = await res.text();
|
|
533
|
-
throw new Error(`Failed to fetch product by SKU: ${res.status} ${res.statusText} - ${errorText}`);
|
|
534
|
-
}
|
|
535
|
-
return await res.json();
|
|
536
|
-
}
|
|
537
|
-
catch (err) {
|
|
538
|
-
this.logging.error(`getProductBySku failed: ${err.message}`);
|
|
539
|
-
throw err;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
async createProduct(sku, productTypeId) {
|
|
543
|
-
try {
|
|
544
|
-
const res = await fetch(`${this.baseUrl}/items`, {
|
|
545
|
-
method: "POST",
|
|
546
|
-
headers: {
|
|
547
|
-
Authorization: `Bearer ${this.apiToken}`,
|
|
548
|
-
"X-Api-Version": "3",
|
|
549
|
-
"X-Environment": this.environment,
|
|
550
|
-
"Content-Type": "application/vnd.api+json",
|
|
551
|
-
Accept: "application/json",
|
|
552
|
-
},
|
|
553
|
-
body: JSON.stringify({
|
|
554
|
-
data: {
|
|
555
|
-
type: "item",
|
|
556
|
-
attributes: { sku },
|
|
557
|
-
relationships: {
|
|
558
|
-
item_type: { data: { type: "item_type", id: productTypeId } },
|
|
559
|
-
},
|
|
560
|
-
},
|
|
561
|
-
}),
|
|
562
|
-
});
|
|
563
|
-
if (!res.ok) {
|
|
564
|
-
const errorText = await res.text();
|
|
565
|
-
throw new Error(`Failed to create product: ${res.status} ${res.statusText} - ${errorText}`);
|
|
566
|
-
}
|
|
567
|
-
return await res.json();
|
|
568
|
-
}
|
|
569
|
-
catch (err) {
|
|
570
|
-
this.logging.error(`createProduct failed: ${err.message}`);
|
|
571
|
-
throw err;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
async updateProductGallery(productId, gallery) {
|
|
575
|
-
try {
|
|
576
|
-
const res = await fetch(`${this.baseUrl}/items/${productId}`, {
|
|
577
|
-
method: "PATCH",
|
|
578
|
-
headers: {
|
|
579
|
-
Authorization: `Bearer ${this.apiToken}`,
|
|
580
|
-
"X-Api-Version": "3",
|
|
581
|
-
"X-Environment": this.environment,
|
|
582
|
-
"Content-Type": "application/vnd.api+json",
|
|
583
|
-
Accept: "application/json",
|
|
584
|
-
},
|
|
585
|
-
body: JSON.stringify({
|
|
586
|
-
data: {
|
|
587
|
-
type: "item",
|
|
588
|
-
id: productId,
|
|
589
|
-
attributes: { gallery },
|
|
590
|
-
},
|
|
591
|
-
}),
|
|
592
|
-
});
|
|
593
|
-
if (!res.ok) {
|
|
594
|
-
const errorText = await res.text();
|
|
595
|
-
throw new Error(`Failed to update product gallery: ${res.status} ${res.statusText} - ${errorText}`);
|
|
596
|
-
}
|
|
597
|
-
return await res.json();
|
|
598
|
-
}
|
|
599
|
-
catch (err) {
|
|
600
|
-
this.logging.error(`updateProductGallery failed: ${err.message}`);
|
|
601
|
-
throw err;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
async updateProductSwatch(productId, uploadId) {
|
|
605
|
-
try {
|
|
606
|
-
const res = await fetch(`${this.baseUrl}/items/${productId}`, {
|
|
607
|
-
method: "PATCH",
|
|
608
|
-
headers: {
|
|
609
|
-
Authorization: `Bearer ${this.apiToken}`,
|
|
610
|
-
"X-Api-Version": "3",
|
|
611
|
-
"X-Environment": this.environment,
|
|
612
|
-
"Content-Type": "application/vnd.api+json",
|
|
613
|
-
Accept: "application/json",
|
|
614
|
-
},
|
|
615
|
-
body: JSON.stringify({
|
|
616
|
-
data: {
|
|
617
|
-
type: "item",
|
|
618
|
-
id: productId,
|
|
619
|
-
attributes: {
|
|
620
|
-
swatch: { upload_id: uploadId },
|
|
621
|
-
},
|
|
622
|
-
},
|
|
623
|
-
}),
|
|
624
|
-
});
|
|
625
|
-
if (!res.ok) {
|
|
626
|
-
const errorText = await res.text();
|
|
627
|
-
throw new Error(`Failed to update product swatch: ${res.status} ${res.statusText} - ${errorText}`);
|
|
628
|
-
}
|
|
629
|
-
return await res.json();
|
|
630
|
-
}
|
|
631
|
-
catch (err) {
|
|
632
|
-
this.logging.error(`updateProductSwatch failed: ${err.message}`);
|
|
633
|
-
throw err;
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
259
|
}
|
|
637
260
|
exports.default = DatoHelper;
|
|
@@ -92,8 +92,8 @@ class Runtime {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
getRuntimeUrl(options) {
|
|
95
|
-
if (
|
|
96
|
-
return
|
|
95
|
+
if (options.runtimeUrl && this.isDeployment) {
|
|
96
|
+
return options.runtimeUrl;
|
|
97
97
|
}
|
|
98
98
|
const systemEnvName = this.getSystemEnvName(options);
|
|
99
99
|
const [_, ...idParts] = (process.env.GCLOUD_PROJECT || '').split('-');
|
|
@@ -16,6 +16,7 @@ exports.setRetryAfterVariable = setRetryAfterVariable;
|
|
|
16
16
|
exports.createFunction = createFunction;
|
|
17
17
|
exports.createOnResponse = createOnResponse;
|
|
18
18
|
exports.onResponse = onResponse;
|
|
19
|
+
exports.parsePrices = parsePrices;
|
|
19
20
|
const crypto_1 = __importDefault(require("crypto"));
|
|
20
21
|
const firestore_1 = require("@google-cloud/firestore");
|
|
21
22
|
const jwks_1 = __importDefault(require("./jwks"));
|
|
@@ -311,7 +312,7 @@ function onResponse(config, options, requiredFlag) {
|
|
|
311
312
|
return Object.assign({}, x, { body: redactedResponse });
|
|
312
313
|
}
|
|
313
314
|
if (x.body instanceof Error) {
|
|
314
|
-
return Object.assign({}, x, { message: x.body.message, error: true });
|
|
315
|
+
return Object.assign({}, x, { body: { message: x.body.message, error: true } });
|
|
315
316
|
}
|
|
316
317
|
return x;
|
|
317
318
|
}
|
|
@@ -334,3 +335,120 @@ function onResponse(config, options, requiredFlag) {
|
|
|
334
335
|
cacheMaxAge: 0,
|
|
335
336
|
};
|
|
336
337
|
}
|
|
338
|
+
function parsePrices(priceData, timeZone = 'UTC') {
|
|
339
|
+
const result = [];
|
|
340
|
+
const defaultStartDate = convertDateToDatetimeTZ(new Date(0).toISOString(), timeZone);
|
|
341
|
+
const defaultEndDate = convertDateToDatetimeTZ(new Date('2099-01-01T00:00:00Z').toISOString(), timeZone);
|
|
342
|
+
for (let i = 0; i < priceData.length; i += 1) {
|
|
343
|
+
const priceChanges = (priceData[i].price_changes || []).map(x => ({
|
|
344
|
+
effective_from: x.effective_from ? (!isNaN(new Date(x.effective_from).getTime()) ? convertDateToDatetimeTZ(x.effective_from, timeZone) : null) : defaultStartDate,
|
|
345
|
+
effective_until: x.effective_until ? (!isNaN(new Date(x.effective_until).getTime()) ? convertDateToDatetimeTZ(x.effective_until, timeZone) : null) : defaultEndDate,
|
|
346
|
+
price: x.price * 100,
|
|
347
|
+
price_type: x.price_type,
|
|
348
|
+
}));
|
|
349
|
+
const rrpPriceTypes = parsePriceType(priceChanges.filter((x) => x.price_type === 'rrp'));
|
|
350
|
+
const permPriceTypes = parsePriceType(priceChanges.filter((x) => x.price_type === 'perm'));
|
|
351
|
+
const posPriceTypes = parsePriceType(priceChanges.filter((x) => x.price_type === 'pos'));
|
|
352
|
+
let currentPrice;
|
|
353
|
+
let currentPriceType;
|
|
354
|
+
if (rrpPriceTypes.current) {
|
|
355
|
+
currentPrice = rrpPriceTypes.current;
|
|
356
|
+
currentPriceType = 'rrp';
|
|
357
|
+
}
|
|
358
|
+
if (permPriceTypes.current) {
|
|
359
|
+
currentPrice = permPriceTypes.current;
|
|
360
|
+
currentPriceType = 'perm';
|
|
361
|
+
}
|
|
362
|
+
if (posPriceTypes.current) {
|
|
363
|
+
currentPrice = posPriceTypes.current;
|
|
364
|
+
currentPriceType = 'pos';
|
|
365
|
+
}
|
|
366
|
+
result.push({
|
|
367
|
+
...priceData[i],
|
|
368
|
+
current_price: currentPrice,
|
|
369
|
+
current_price_type: currentPriceType,
|
|
370
|
+
rrp: rrpPriceTypes,
|
|
371
|
+
perm: permPriceTypes,
|
|
372
|
+
pos: posPriceTypes
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
return result;
|
|
376
|
+
}
|
|
377
|
+
function parsePriceType(priceTypeData, now = new Date()) {
|
|
378
|
+
if (!priceTypeData) {
|
|
379
|
+
return {
|
|
380
|
+
current: undefined,
|
|
381
|
+
historical: [],
|
|
382
|
+
future: []
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
const historicalEntries = [];
|
|
386
|
+
const futureEntries = [];
|
|
387
|
+
const currentEntries = [];
|
|
388
|
+
for (let i = 0; i < priceTypeData.length; i += 1) {
|
|
389
|
+
const price = priceTypeData[i];
|
|
390
|
+
if (price.effective_from !== null && price.effective_until !== null) {
|
|
391
|
+
const priceWithoutNulls = price;
|
|
392
|
+
if (priceWithoutNulls.effective_from.getTime() > priceWithoutNulls.effective_until.getTime()) {
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
else if (priceWithoutNulls.effective_from.getTime() > now.getTime()) {
|
|
396
|
+
futureEntries.push(priceWithoutNulls);
|
|
397
|
+
}
|
|
398
|
+
else if (priceWithoutNulls.effective_until.getTime() < now.getTime()) {
|
|
399
|
+
historicalEntries.push(priceWithoutNulls);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
currentEntries.push(priceWithoutNulls);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const [currentEntry, ...otherFutureEntries] = currentEntries.sort(createSortPriceTypes(false));
|
|
407
|
+
const sortedHistoricalEntries = historicalEntries.concat(otherFutureEntries).sort(createSortPriceTypes(true));
|
|
408
|
+
const sortedFutureEntries = futureEntries.sort(createSortPriceTypes(true));
|
|
409
|
+
return {
|
|
410
|
+
current: currentEntry,
|
|
411
|
+
historical: sortedHistoricalEntries,
|
|
412
|
+
future: sortedFutureEntries
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function createSortPriceTypes(descending) {
|
|
416
|
+
return function (a, b) {
|
|
417
|
+
let effectiveFromSort;
|
|
418
|
+
if (descending) {
|
|
419
|
+
effectiveFromSort = b.effective_from.getTime() - a.effective_from.getTime();
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
effectiveFromSort = a.effective_from.getTime() - b.effective_from.getTime();
|
|
423
|
+
}
|
|
424
|
+
if (effectiveFromSort === 0) {
|
|
425
|
+
if (!descending) {
|
|
426
|
+
return b.effective_until.getTime() - a.effective_until.getTime();
|
|
427
|
+
}
|
|
428
|
+
return a.effective_until.getTime() - b.effective_until.getTime();
|
|
429
|
+
}
|
|
430
|
+
return effectiveFromSort;
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function convertDateToDatetimeTZ(dateString, timeZone) {
|
|
434
|
+
let normDateString = '';
|
|
435
|
+
if (dateString.includes('T')) {
|
|
436
|
+
normDateString = dateString.slice(0, 19);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
normDateString = dateString + 'T00:00:00';
|
|
440
|
+
}
|
|
441
|
+
console.error(dateString, normDateString + getOffsetString(timeZone));
|
|
442
|
+
return new Date(normDateString + getOffsetString(timeZone));
|
|
443
|
+
}
|
|
444
|
+
function getOffsetString(timeZone) {
|
|
445
|
+
const today = new Date(`${new Date().toISOString().slice(0, 10)}T00:00:00Z`);
|
|
446
|
+
const locale = today.toLocaleString("sv-SE", { timeZone });
|
|
447
|
+
const tzDate = new Date(locale);
|
|
448
|
+
const offsetMinutes = Math.ceil(tzDate.getTime() - today.getTime()) / 60000;
|
|
449
|
+
const sign = offsetMinutes >= 0 ? "+" : "-";
|
|
450
|
+
const abs = Math.abs(offsetMinutes);
|
|
451
|
+
const hours = String(Math.floor(abs / 60)).padStart(2, "0");
|
|
452
|
+
const minutes = String(abs % 60).padStart(2, "0");
|
|
453
|
+
return `${sign}${hours}:${minutes}`;
|
|
454
|
+
}
|