@projectdochelp/s3te 3.3.1 → 3.3.2
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/package.json
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
BatchWriteCommand,
|
|
18
18
|
DeleteCommand,
|
|
19
19
|
DynamoDBDocumentClient,
|
|
20
|
+
GetCommand,
|
|
20
21
|
PutCommand,
|
|
21
22
|
QueryCommand,
|
|
22
23
|
ScanCommand
|
|
@@ -143,6 +144,7 @@ export function createAwsClients(region = process.env.AWS_REGION) {
|
|
|
143
144
|
invoke: InvokeCommand
|
|
144
145
|
}),
|
|
145
146
|
dynamo: wrapCommandClient(dynamoDocumentClient, {
|
|
147
|
+
get: GetCommand,
|
|
146
148
|
query: QueryCommand,
|
|
147
149
|
scan: ScanCommand,
|
|
148
150
|
batchWrite: BatchWriteCommand,
|
|
@@ -247,7 +249,7 @@ export function buildCoreConfigFromEnvironment(manifest, environmentName) {
|
|
|
247
249
|
debounceSeconds: 60
|
|
248
250
|
},
|
|
249
251
|
lambda: {
|
|
250
|
-
runtime: "
|
|
252
|
+
runtime: "nodejs24.x",
|
|
251
253
|
architecture: "arm64"
|
|
252
254
|
}
|
|
253
255
|
},
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { gunzipSync } from "node:zlib";
|
|
2
|
+
|
|
1
3
|
import { createAwsClients, invokeLambdaEvent } from "./common.mjs";
|
|
2
4
|
|
|
3
5
|
function escapeHtml(value) {
|
|
@@ -134,10 +136,118 @@ function toSimpleValue(value) {
|
|
|
134
136
|
return String(value);
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
function
|
|
138
|
-
|
|
139
|
-
? item.
|
|
140
|
-
:
|
|
139
|
+
function getItemRoot(item) {
|
|
140
|
+
return hasNestedEntryEnvelope(item)
|
|
141
|
+
? item.data
|
|
142
|
+
: item;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function hasNestedEntryEnvelope(item) {
|
|
146
|
+
if (!item?.data || typeof item.data !== "object" || Array.isArray(item.data)) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return [
|
|
151
|
+
"id",
|
|
152
|
+
"entryId",
|
|
153
|
+
"model",
|
|
154
|
+
"modelId",
|
|
155
|
+
"tenant",
|
|
156
|
+
"tenantId",
|
|
157
|
+
"status",
|
|
158
|
+
"values",
|
|
159
|
+
"createdOn",
|
|
160
|
+
"savedOn",
|
|
161
|
+
"publishedOn",
|
|
162
|
+
"lastPublishedOn"
|
|
163
|
+
].some((key) => Object.prototype.hasOwnProperty.call(item.data, key));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function decodeCompressedValue(value) {
|
|
167
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (typeof value.compression !== "string" || typeof value.value !== "string") {
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
const compressed = Buffer.from(value.value, "base64");
|
|
177
|
+
if (value.compression === "gzip") {
|
|
178
|
+
const inflated = gunzipSync(compressed).toString("utf8");
|
|
179
|
+
if (value.isArray) {
|
|
180
|
+
return JSON.parse(inflated);
|
|
181
|
+
}
|
|
182
|
+
return inflated;
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
return "";
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return value.value;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function getFieldIdentifiers(field) {
|
|
192
|
+
return [field?.storageId, field?.fieldId].filter(Boolean);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function findFieldValue(rawValues, field) {
|
|
196
|
+
for (const identifier of getFieldIdentifiers(field)) {
|
|
197
|
+
if (Object.prototype.hasOwnProperty.call(rawValues, identifier)) {
|
|
198
|
+
return rawValues[identifier];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function normalizeFieldValue(rawValue, field) {
|
|
205
|
+
const decodedValue = decodeCompressedValue(rawValue);
|
|
206
|
+
|
|
207
|
+
if (field?.type === "object") {
|
|
208
|
+
const nestedFields = Array.isArray(field.settings?.fields) ? field.settings.fields : [];
|
|
209
|
+
if (Array.isArray(decodedValue)) {
|
|
210
|
+
return decodedValue.map((entry) => normalizeFieldValue(entry, {
|
|
211
|
+
...field,
|
|
212
|
+
list: false
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
if (decodedValue && typeof decodedValue === "object") {
|
|
216
|
+
return normalizeMappedValues(decodedValue, nestedFields);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (Array.isArray(decodedValue)) {
|
|
221
|
+
return decodedValue.map((entry) => toSimpleValue(entry));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return toSimpleValue(decodedValue);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function normalizeMappedValues(rawValues, fields = []) {
|
|
228
|
+
const values = {};
|
|
229
|
+
for (const field of fields) {
|
|
230
|
+
const rawValue = findFieldValue(rawValues, field);
|
|
231
|
+
if (rawValue === undefined) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
values[field.fieldId] = normalizeFieldValue(rawValue, field);
|
|
235
|
+
}
|
|
236
|
+
return values;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function normalizeValues(item, modelFields = []) {
|
|
240
|
+
const root = getItemRoot(item);
|
|
241
|
+
const valueSource = root?.values && typeof root.values === "object"
|
|
242
|
+
? root.values
|
|
243
|
+
: (!hasNestedEntryEnvelope(item) && item?.data && typeof item.data === "object" && !Array.isArray(item.data) ? item.data : null);
|
|
244
|
+
|
|
245
|
+
if (valueSource && Array.isArray(modelFields) && modelFields.length > 0) {
|
|
246
|
+
const mappedValues = normalizeMappedValues(valueSource, modelFields);
|
|
247
|
+
if (Object.keys(mappedValues).length > 0) {
|
|
248
|
+
return mappedValues;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
141
251
|
|
|
142
252
|
if (valueSource) {
|
|
143
253
|
return Object.fromEntries(Object.entries(valueSource).map(([key, value]) => [key, toSimpleValue(value)]));
|
|
@@ -178,44 +288,51 @@ function normalizeValues(item) {
|
|
|
178
288
|
}
|
|
179
289
|
|
|
180
290
|
function isPublished(item) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
||
|
|
184
|
-
||
|
|
291
|
+
const root = getItemRoot(item);
|
|
292
|
+
return root.status === "published"
|
|
293
|
+
|| root.published === true
|
|
294
|
+
|| root.isPublished === true
|
|
295
|
+
|| root.publishedOn != null
|
|
296
|
+
|| root.firstPublishedOn != null
|
|
297
|
+
|| root.lastPublishedOn != null;
|
|
185
298
|
}
|
|
186
299
|
|
|
187
300
|
function extractWebinyLocale(item) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
??
|
|
191
|
-
??
|
|
301
|
+
const root = getItemRoot(item);
|
|
302
|
+
return root.locale
|
|
303
|
+
?? root.localeCode
|
|
304
|
+
?? root.i18n?.locale?.code
|
|
305
|
+
?? root.i18n?.localeCode
|
|
192
306
|
?? null;
|
|
193
307
|
}
|
|
194
308
|
|
|
195
309
|
function extractWebinyTenant(item) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
??
|
|
310
|
+
const root = getItemRoot(item);
|
|
311
|
+
return root.tenant
|
|
312
|
+
?? root.tenantId
|
|
313
|
+
?? root.createdBy?.tenant
|
|
199
314
|
?? null;
|
|
200
315
|
}
|
|
201
316
|
|
|
202
|
-
export function normalizeContentItem(item) {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
??
|
|
317
|
+
export function normalizeContentItem(item, options = {}) {
|
|
318
|
+
const root = getItemRoot(item);
|
|
319
|
+
const values = normalizeValues(item, options.modelFields);
|
|
320
|
+
const model = root.model
|
|
321
|
+
?? root.modelId
|
|
322
|
+
?? root.__typename
|
|
323
|
+
?? root.contentModel?.modelId
|
|
207
324
|
?? null;
|
|
208
325
|
return {
|
|
209
|
-
id: item.id,
|
|
210
|
-
contentId:
|
|
326
|
+
id: root.id ?? item.id,
|
|
327
|
+
contentId: values.contentId ?? values.contentid ?? root.contentId ?? root.contentid ?? root.entryId ?? root.id ?? item.id,
|
|
211
328
|
model,
|
|
212
329
|
locale: extractWebinyLocale(item) ?? undefined,
|
|
213
330
|
tenant: extractWebinyTenant(item) ?? undefined,
|
|
214
|
-
values
|
|
215
|
-
createdAt:
|
|
216
|
-
updatedAt:
|
|
217
|
-
version:
|
|
218
|
-
lastChangedAt:
|
|
331
|
+
values,
|
|
332
|
+
createdAt: root.createdAt ?? root.createdOn,
|
|
333
|
+
updatedAt: root.updatedAt ?? root.savedOn ?? root.publishedOn ?? root.lastPublishedOn,
|
|
334
|
+
version: root._version ?? root.version,
|
|
335
|
+
lastChangedAt: root._lastChangedAt ?? root.lastChangedAt
|
|
219
336
|
};
|
|
220
337
|
}
|
|
221
338
|
|
|
@@ -224,20 +341,46 @@ export function matchesConfiguredTenant(item, configuredTenant) {
|
|
|
224
341
|
return true;
|
|
225
342
|
}
|
|
226
343
|
|
|
227
|
-
const
|
|
344
|
+
const root = getItemRoot(item);
|
|
345
|
+
const tenant = root.tenant ?? root.tenantId ?? root.createdBy?.tenant ?? null;
|
|
228
346
|
return tenant != null && String(tenant) === String(configuredTenant);
|
|
229
347
|
}
|
|
230
348
|
|
|
349
|
+
async function loadModelFields(clients, sourceTableName, tenant, modelId, cache) {
|
|
350
|
+
if (!sourceTableName || !tenant || !modelId) {
|
|
351
|
+
return [];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const cacheKey = `${tenant}#${modelId}`;
|
|
355
|
+
if (cache.has(cacheKey)) {
|
|
356
|
+
return cache.get(cacheKey);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const response = await clients.dynamo.get({
|
|
360
|
+
TableName: sourceTableName,
|
|
361
|
+
Key: {
|
|
362
|
+
PK: `T#${tenant}#CMS#CM`,
|
|
363
|
+
SK: modelId
|
|
364
|
+
}
|
|
365
|
+
}).promise();
|
|
366
|
+
const model = response.Item?.data ?? response.Item ?? null;
|
|
367
|
+
const fields = Array.isArray(model?.fields) ? model.fields : [];
|
|
368
|
+
cache.set(cacheKey, fields);
|
|
369
|
+
return fields;
|
|
370
|
+
}
|
|
371
|
+
|
|
231
372
|
export async function handler(event) {
|
|
232
373
|
const clients = createAwsClients();
|
|
233
374
|
const tableName = process.env.S3TE_CONTENT_TABLE;
|
|
234
375
|
const renderWorkerName = process.env.S3TE_RENDER_WORKER_NAME;
|
|
235
376
|
const environmentName = process.env.S3TE_ENVIRONMENT;
|
|
377
|
+
const sourceTableName = process.env.S3TE_WEBINY_SOURCE_TABLE;
|
|
236
378
|
const configuredTenant = String(process.env.S3TE_WEBINY_TENANT ?? "").trim();
|
|
237
379
|
const relevantModels = new Set(String(process.env.S3TE_RELEVANT_MODELS ?? "")
|
|
238
380
|
.split(",")
|
|
239
381
|
.map((entry) => entry.trim())
|
|
240
382
|
.filter(Boolean));
|
|
383
|
+
const modelFieldCache = new Map();
|
|
241
384
|
|
|
242
385
|
let mirrored = 0;
|
|
243
386
|
let deleted = 0;
|
|
@@ -253,7 +396,15 @@ export async function handler(event) {
|
|
|
253
396
|
continue;
|
|
254
397
|
}
|
|
255
398
|
|
|
256
|
-
const
|
|
399
|
+
const itemRoot = getItemRoot(item);
|
|
400
|
+
const modelFields = await loadModelFields(
|
|
401
|
+
clients,
|
|
402
|
+
sourceTableName,
|
|
403
|
+
extractWebinyTenant(item),
|
|
404
|
+
itemRoot?.modelId ?? itemRoot?.model,
|
|
405
|
+
modelFieldCache
|
|
406
|
+
);
|
|
407
|
+
const contentItem = normalizeContentItem(item, { modelFields });
|
|
257
408
|
if (!contentItem.id || !contentItem.model || (relevantModels.size > 0 && !relevantModels.has(contentItem.model))) {
|
|
258
409
|
continue;
|
|
259
410
|
}
|
|
@@ -632,6 +632,7 @@ export function buildWebinyCloudFormationTemplate({ config, environment }) {
|
|
|
632
632
|
S3TE_ENVIRONMENT: environment,
|
|
633
633
|
S3TE_CONTENT_TABLE: runtimeConfig.tables.content,
|
|
634
634
|
S3TE_RELEVANT_MODELS: runtimeConfig.integrations.webiny.relevantModels.join(","),
|
|
635
|
+
S3TE_WEBINY_SOURCE_TABLE: runtimeConfig.integrations.webiny.sourceTableName,
|
|
635
636
|
S3TE_WEBINY_TENANT: runtimeConfig.integrations.webiny.tenant ?? "",
|
|
636
637
|
S3TE_RENDER_WORKER_NAME: functionNames.renderWorker
|
|
637
638
|
}
|
|
@@ -277,7 +277,7 @@ export function resolveProjectConfig(projectConfig) {
|
|
|
277
277
|
debounceSeconds: projectConfig.aws?.invalidationStore?.debounceSeconds ?? 60
|
|
278
278
|
},
|
|
279
279
|
lambda: {
|
|
280
|
-
runtime: projectConfig.aws?.lambda?.runtime ?? "
|
|
280
|
+
runtime: projectConfig.aws?.lambda?.runtime ?? "nodejs24.x",
|
|
281
281
|
architecture: projectConfig.aws?.lambda?.architecture ?? "arm64"
|
|
282
282
|
}
|
|
283
283
|
};
|