pim-import 6.11.0 → 6.12.0
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/README.md +62 -0
- package/dist/algolia/products.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -2
- package/dist/libs/contentful.js +5 -4
- package/dist/pim/methods/dictionary.js +4 -3
- package/dist/pim/methods/products.js +56 -42
- package/dist/pim/methods/seo.d.ts +13 -0
- package/dist/pim/methods/seo.js +224 -0
- package/dist/resources/DProductSubLine.d.ts +54 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -442,6 +442,68 @@ await importDownloads("/path/to/downloads.csv");
|
|
|
442
442
|
|
|
443
443
|
---
|
|
444
444
|
|
|
445
|
+
## SEO Import
|
|
446
|
+
|
|
447
|
+
Imports SEO metadata (meta title and meta description) from a CSV file stored on S3 into Contentful `topicSeo` entries.
|
|
448
|
+
|
|
449
|
+
The function parses the `slug` column to extract the locale and the actual slug value. The locale is the first path segment; the slug is the last segment. For example, `de/global/bespoke/` means locale=`de` and slug=`bespoke`, so it searches for a `page` entry with `fields.slug.de = "bespoke"`.
|
|
450
|
+
|
|
451
|
+
### CSV format
|
|
452
|
+
|
|
453
|
+
| Column | Description |
|
|
454
|
+
| ----------------- | ------------------------------------------------------------------------------------- |
|
|
455
|
+
| `slug` | Localized slug path (e.g., `de/global/bespoke/` or `en-US/us/projects/project-name/`) |
|
|
456
|
+
| `metaTitle` | Meta title for the detected locale |
|
|
457
|
+
| `metaDescription` | Meta description for the detected locale |
|
|
458
|
+
|
|
459
|
+
### Example CSV
|
|
460
|
+
|
|
461
|
+
```csv
|
|
462
|
+
slug,metaTitle,metaDescription
|
|
463
|
+
de/global/bespoke/,Bespoke - German,Die Meta-Beschreibung für die Bespoke-Seite
|
|
464
|
+
/en-US/us/projects/projects-bodegas-faustino/,Bodegas Faustino,The meta description for Bodegas Faustino
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
See `src/pim/methods/seo-example.csv` for a complete example.
|
|
468
|
+
|
|
469
|
+
### Usage
|
|
470
|
+
|
|
471
|
+
```ts
|
|
472
|
+
import { importSeoFromCsv } from "pim-import";
|
|
473
|
+
|
|
474
|
+
const result = await importSeoFromCsv(
|
|
475
|
+
"path/to/seo-data.csv", // S3 path (required)
|
|
476
|
+
0, // offset (default 0)
|
|
477
|
+
50, // limit, -1 = all (default 50)
|
|
478
|
+
);
|
|
479
|
+
// result: {
|
|
480
|
+
// offset: number,
|
|
481
|
+
// limit: number,
|
|
482
|
+
// completed: boolean,
|
|
483
|
+
// total: number,
|
|
484
|
+
// processed: number,
|
|
485
|
+
// created: number,
|
|
486
|
+
// updated: number,
|
|
487
|
+
// skipped: number,
|
|
488
|
+
// errors: string[]
|
|
489
|
+
// }
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Behavior
|
|
493
|
+
|
|
494
|
+
- **Slug parsing**: Extracts locale (first segment) and slug (last segment) from the path
|
|
495
|
+
- **Slug fallback**: If a page does not have the slug translated for the requested locale (i.e., `fields.slug.{locale}` does not exist), falls back to searching with `fields.slug.en`
|
|
496
|
+
- **Invalid format**: Logs a warning and skips rows with malformed slug paths
|
|
497
|
+
- **Unknown locale**: Logs a warning and skips rows with unsupported locales
|
|
498
|
+
- **Page not found**: Logs a warning and skips the row
|
|
499
|
+
- **topicSeo exists**: Updates only `metaTitle` and `metaDescription` for the detected locale; other locales and fields remain unchanged
|
|
500
|
+
- **topicSeo not found**: Creates a new `topicSeo` entry, populates `metaTitle`/`metaDescription` for the detected locale, and associates it to the page's `seo` field
|
|
501
|
+
- **Empty CSV value**: Retains the existing value in Contentful (never overwrites with empty)
|
|
502
|
+
- **Published automatically**: New and updated `topicSeo` entries are published immediately
|
|
503
|
+
- **Errors**: Logged but processing continues for other rows
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
445
507
|
## Contentful utilities
|
|
446
508
|
|
|
447
509
|
```ts
|
package/dist/algolia/products.js
CHANGED
|
@@ -322,7 +322,7 @@ const reindexProducts = async (filterKey, filterValue, offset = 0, limit = 50, l
|
|
|
322
322
|
const timeStart = new Date();
|
|
323
323
|
(0, logs_1.log)(`reindexProducts - filterKey: ${filterKey} filterValue: ${filterValue} offset: ${offset} limit: ${limit} lastPimSyncDateGte: ${lastPimSyncDateGte} filters: ${filters
|
|
324
324
|
.map((filter) => `${filter.key}:${filter.value}`)
|
|
325
|
-
.join(", ")}`);
|
|
325
|
+
.join(", ")} lastModifiedGte: ${lastModifiedGte}`);
|
|
326
326
|
const records = await getObjects(filterKey, filterValue, offset, limit, lastPimSyncDateGte, filters, lastModifiedGte);
|
|
327
327
|
const defaultEnvironmentLocaleCode = await (0, contentful_cda_1.getEnvironmentDefaultLocaleCode)();
|
|
328
328
|
const objectsToSave = records.objects.filter((object) => !isObjectToDelete(object, defaultEnvironmentLocaleCode));
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export { importProductByCode, setProductsRelationships, setProductRelationships,
|
|
|
12
12
|
export { publishAllProductDrafts } from "./pim/methods/bulkPublish";
|
|
13
13
|
export { migrateEntryFields } from "./pim/methods/migrateEntryFields";
|
|
14
14
|
export { checkTopicDraftAndPagePublished } from "./pim/methods/checkTopicDraftAndPagePublished";
|
|
15
|
+
export { importSeoFromCsv } from "./pim/methods/seo";
|
|
15
16
|
export { getIndex, cloneIndexSettings } from "./algolia/config";
|
|
16
17
|
export { reindexFamilies, reindexFamily, removeFamilyObject, } from "./algolia/families";
|
|
17
18
|
export { reindexSubFamilies, reindexSubFamily, removeSubFamilyObject, } from "./algolia/subFamilies";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.netlifyBuild = exports.notify = exports.importDesigner = exports.importDesigners = exports.generatePDFByUrl = exports.removeRecordsByStatus = exports.getLogFolder = exports.setLogFilename = exports.setLogPath = exports.setLogId = exports.setServerUtils = exports.log = exports.getLatestProducts = exports.getStaticDailyProducts = exports.getLocalISOTime = exports.importDownloads = exports.removePostObject = exports.reindexPosts = exports.reindexPost = exports.removePressReleaseObject = exports.reindexPressReleases = exports.reindexPressRelease = exports.removePressReviewObject = exports.reindexPressReviews = exports.reindexPressReview = exports.removeStoryObject = exports.reindexStories = exports.reindexStory = exports.removeProjectObject = exports.reindexProjects = exports.reindexProject = exports.removeInspirationObject = exports.reindexInspirations = exports.reindexInspiration = exports.removeDownloadObject = exports.reindexDownloads = exports.reindexDownload = exports.removeModelObject = exports.reindexModels = exports.reindexModel = exports.removeSubModelObject = exports.reindexSubModels = exports.reindexSubModel = exports.triggerPDFGenerator = exports.removeProductObject = exports.reindexProducts = exports.reindexProduct = void 0;
|
|
3
|
+
exports.reindexSubFamily = exports.reindexSubFamilies = exports.removeFamilyObject = exports.reindexFamily = exports.reindexFamilies = exports.cloneIndexSettings = exports.getIndex = exports.importSeoFromCsv = exports.checkTopicDraftAndPagePublished = exports.migrateEntryFields = exports.publishAllProductDrafts = exports.purgeProductThumbCacheByProductCodes = exports.populateDestinations = exports.reimportAuditProducts = exports.getProductPageIdByCode = exports.removeAllProductModelProductRelations = exports.removeProductFromColorVariantsByProductLine = exports.setProductAutodescriptionByTopicId = exports.getProductAutodescription = exports.setProductsAutodescription = exports.generateTechSpecPdf = exports.audit = exports.getAllProductEntriesByCatalog = exports.setProductRelationships = exports.setProductsRelationships = exports.importProductByCode = exports.importFamilies = exports.importSubFamilies = exports.importSubModels = exports.importModels = exports.importLatestProducts = exports.importCategories = exports.importDictionaryProductSubLine = exports.importDictionaryProductLine = exports.importDictionaryData = exports.importDictionaryIcons = exports.importDictionaryFields = exports.savePDFToS3 = exports.getFileFromS3 = exports.saveJsonToS3 = exports.uploadS3 = exports.initS3 = exports.getEntries = exports.getTopicPage = exports.getEntryByID = exports.deleteEntries = exports.deletePages = exports.initBaseEntries = exports.initContentful = exports.initPim = void 0;
|
|
4
|
+
exports.netlifyBuild = exports.notify = exports.importDesigner = exports.importDesigners = exports.generatePDFByUrl = exports.removeRecordsByStatus = exports.getLogFolder = exports.setLogFilename = exports.setLogPath = exports.setLogId = exports.setServerUtils = exports.log = exports.getLatestProducts = exports.getStaticDailyProducts = exports.getLocalISOTime = exports.importDownloads = exports.removePostObject = exports.reindexPosts = exports.reindexPost = exports.removePressReleaseObject = exports.reindexPressReleases = exports.reindexPressRelease = exports.removePressReviewObject = exports.reindexPressReviews = exports.reindexPressReview = exports.removeStoryObject = exports.reindexStories = exports.reindexStory = exports.removeProjectObject = exports.reindexProjects = exports.reindexProject = exports.removeInspirationObject = exports.reindexInspirations = exports.reindexInspiration = exports.removeDownloadObject = exports.reindexDownloads = exports.reindexDownload = exports.removeModelObject = exports.reindexModels = exports.reindexModel = exports.removeSubModelObject = exports.reindexSubModels = exports.reindexSubModel = exports.triggerPDFGenerator = exports.removeProductObject = exports.reindexProducts = exports.reindexProduct = exports.removeSubFamilyObject = void 0;
|
|
5
5
|
var config_1 = require("./pim/config");
|
|
6
6
|
Object.defineProperty(exports, "initPim", { enumerable: true, get: function () { return config_1.init; } });
|
|
7
7
|
var contentful_1 = require("./libs/contentful");
|
|
@@ -58,6 +58,8 @@ var migrateEntryFields_1 = require("./pim/methods/migrateEntryFields");
|
|
|
58
58
|
Object.defineProperty(exports, "migrateEntryFields", { enumerable: true, get: function () { return migrateEntryFields_1.migrateEntryFields; } });
|
|
59
59
|
var checkTopicDraftAndPagePublished_1 = require("./pim/methods/checkTopicDraftAndPagePublished");
|
|
60
60
|
Object.defineProperty(exports, "checkTopicDraftAndPagePublished", { enumerable: true, get: function () { return checkTopicDraftAndPagePublished_1.checkTopicDraftAndPagePublished; } });
|
|
61
|
+
var seo_1 = require("./pim/methods/seo");
|
|
62
|
+
Object.defineProperty(exports, "importSeoFromCsv", { enumerable: true, get: function () { return seo_1.importSeoFromCsv; } });
|
|
61
63
|
var config_2 = require("./algolia/config");
|
|
62
64
|
Object.defineProperty(exports, "getIndex", { enumerable: true, get: function () { return config_2.getIndex; } });
|
|
63
65
|
Object.defineProperty(exports, "cloneIndexSettings", { enumerable: true, get: function () { return config_2.cloneIndexSettings; } });
|
package/dist/libs/contentful.js
CHANGED
|
@@ -178,7 +178,8 @@ const getAllEntriesByCodes = async (codes, contentTypeId, select, filter = "fiel
|
|
|
178
178
|
if (!codes.length) {
|
|
179
179
|
return [];
|
|
180
180
|
}
|
|
181
|
-
|
|
181
|
+
const uniqueCodes = Array.from(new Set(codes));
|
|
182
|
+
(0, logs_1.log)(`getAllEntriesByCodes uniqueCodes: ${uniqueCodes.length} contentTypeId: ${contentTypeId}`);
|
|
182
183
|
const env = await (0, exports.getEnvironment)();
|
|
183
184
|
const defEnvLocaleCode = await (0, exports.getEnvironmentDefaultLocaleCode)();
|
|
184
185
|
let allItems = [];
|
|
@@ -188,11 +189,11 @@ const getAllEntriesByCodes = async (codes, contentTypeId, select, filter = "fiel
|
|
|
188
189
|
const chunkLimit = 20;
|
|
189
190
|
const filterKey = `${filter}[in]`;
|
|
190
191
|
let codesChunks = [];
|
|
191
|
-
if (
|
|
192
|
-
codesChunks = (0, utils_1.doChunk)(
|
|
192
|
+
if (uniqueCodes.length > chunkLimit) {
|
|
193
|
+
codesChunks = (0, utils_1.doChunk)(uniqueCodes, chunkLimit);
|
|
193
194
|
}
|
|
194
195
|
else {
|
|
195
|
-
codesChunks.push(
|
|
196
|
+
codesChunks.push(uniqueCodes);
|
|
196
197
|
}
|
|
197
198
|
let count = 0;
|
|
198
199
|
for (const codesChunk of codesChunks) {
|
|
@@ -363,10 +363,11 @@ const importDictionaryProductSubLine = async () => {
|
|
|
363
363
|
(0, logs_1.log)(`Import productSubLine ${productSubLine.code}`);
|
|
364
364
|
let productSubLineEntry = await (0, contentful_1.getEntryByCode)(productSubLine.code, "topicProductSubLine");
|
|
365
365
|
let productLineEntry = null;
|
|
366
|
-
|
|
367
|
-
|
|
366
|
+
const productLineCode = productSubLine.productLine?.code;
|
|
367
|
+
if (productLineCode) {
|
|
368
|
+
productLineEntry = await (0, contentful_1.getEntryByCode)(productLineCode, "topicProductLine");
|
|
368
369
|
if (!productLineEntry) {
|
|
369
|
-
(0, logs_1.log)(`No topicProductLine with code ${
|
|
370
|
+
(0, logs_1.log)(`No topicProductLine with code ${productLineCode} found for the productSubLine ${productSubLine.code}`);
|
|
370
371
|
}
|
|
371
372
|
}
|
|
372
373
|
else {
|
|
@@ -391,6 +391,11 @@ const getProductFields = async (pimDetails) => {
|
|
|
391
391
|
const getTopicProductIdByCode = (productCode) => {
|
|
392
392
|
return productCode;
|
|
393
393
|
};
|
|
394
|
+
const extractProductCode = (code) => {
|
|
395
|
+
return code?.includes(PIM_DUPLICATE_SEPARATOR)
|
|
396
|
+
? code.split(PIM_DUPLICATE_SEPARATOR)[0]
|
|
397
|
+
: code;
|
|
398
|
+
};
|
|
394
399
|
const getProductPageIdByCode = (productCode) => {
|
|
395
400
|
return `${productCode}_PAGE`;
|
|
396
401
|
};
|
|
@@ -1056,17 +1061,24 @@ const getAllProductEntriesByCatalog = async (catalog, limit = 100, select = "")
|
|
|
1056
1061
|
return allItems;
|
|
1057
1062
|
};
|
|
1058
1063
|
exports.getAllProductEntriesByCatalog = getAllProductEntriesByCatalog;
|
|
1064
|
+
const updateEntryInArray = (entries, updatedEntry) => {
|
|
1065
|
+
const index = entries.findIndex((e) => e.sys.id === updatedEntry.sys.id);
|
|
1066
|
+
if (index !== -1) {
|
|
1067
|
+
entries[index] = updatedEntry;
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1059
1070
|
const productAudit = async (audit, productEntries, catalog, defaultEnvironmentLocaleCode, current, allAudit, productPageEntries) => {
|
|
1060
|
-
|
|
1071
|
+
const productCode = extractProductCode(audit.product);
|
|
1072
|
+
(0, logs_1.log)(`Search product entry with id ${productCode}...`);
|
|
1061
1073
|
let productEntry = productEntries.find((currentEntry) => currentEntry?.fields?.code?.[defaultEnvironmentLocaleCode] ===
|
|
1062
|
-
|
|
1074
|
+
productCode);
|
|
1063
1075
|
if (!productEntry) {
|
|
1064
1076
|
let logMessage = "";
|
|
1065
1077
|
if (catalog) {
|
|
1066
|
-
logMessage = `The ${
|
|
1078
|
+
logMessage = `The ${productCode} product was not found in the CMS with catalog ${catalog}`;
|
|
1067
1079
|
}
|
|
1068
1080
|
else {
|
|
1069
|
-
logMessage = `The ${
|
|
1081
|
+
logMessage = `The ${productCode} product was not found in the CMS`;
|
|
1070
1082
|
}
|
|
1071
1083
|
(0, logs_1.log)(logMessage, "WARN");
|
|
1072
1084
|
if (logs_1.serverUtils) {
|
|
@@ -1077,7 +1089,7 @@ const productAudit = async (audit, productEntries, catalog, defaultEnvironmentLo
|
|
|
1077
1089
|
return { current, continue: true };
|
|
1078
1090
|
}
|
|
1079
1091
|
(0, logs_1.log)(`Founded product with id ${productEntry.sys.id}`);
|
|
1080
|
-
const pageEntryFromId = (0, exports.getProductPageIdByCode)(
|
|
1092
|
+
const pageEntryFromId = (0, exports.getProductPageIdByCode)(productCode);
|
|
1081
1093
|
(0, logs_1.log)(`Search product page entry with id ${pageEntryFromId}...`);
|
|
1082
1094
|
let pageEntryFrom = productPageEntries.find((currentEntry) => currentEntry.sys.id === pageEntryFromId);
|
|
1083
1095
|
if (pageEntryFrom) {
|
|
@@ -1091,31 +1103,35 @@ const productAudit = async (audit, productEntries, catalog, defaultEnvironmentLo
|
|
|
1091
1103
|
return entryCatalog.sys.id;
|
|
1092
1104
|
});
|
|
1093
1105
|
if (catalog && !productCatalogs.includes(catalog)) {
|
|
1094
|
-
(0, logs_1.log)(`Product ${
|
|
1106
|
+
(0, logs_1.log)(`Product ${productCode} does not belong to the ${catalog} catalog`);
|
|
1095
1107
|
}
|
|
1096
1108
|
else if (["PRODUCT_UNPUBLISH", "PRODUCT_WEBOFF"].includes(audit.what)) {
|
|
1097
1109
|
(0, logs_1.log)(`Set the product status as archive...`);
|
|
1098
1110
|
productEntry = await (0, contentful_1.archiveEntry)(productEntry, true);
|
|
1111
|
+
updateEntryInArray(productEntries, productEntry);
|
|
1099
1112
|
if (!pageEntryFrom) {
|
|
1100
1113
|
(0, logs_1.log)(`${pageEntryFromId} page from not found`);
|
|
1101
1114
|
}
|
|
1102
1115
|
else if (pageEntryFrom.isArchived()) {
|
|
1103
|
-
(0, logs_1.log)(`Can't create redirect ${
|
|
1116
|
+
(0, logs_1.log)(`Can't create redirect ${productCode} to ${audit.upgradeProduct} because the page ${pageEntryFrom.sys.id} is archived.`, "WARN");
|
|
1104
1117
|
}
|
|
1105
1118
|
else if (pageEntryFrom?.fields) {
|
|
1106
1119
|
if (audit.upgradeProduct && audit.upgradeProduct !== "0") {
|
|
1107
|
-
(0, logs_1.log)(`Creating redirect from ${
|
|
1120
|
+
(0, logs_1.log)(`Creating redirect from ${productCode} to ${audit.upgradeProduct}`);
|
|
1108
1121
|
const pageEntryToId = (0, exports.getProductPageIdByCode)(audit.upgradeProduct);
|
|
1109
1122
|
const pageEntryTo = await (0, contentful_1.getEntryByID)(pageEntryToId, "page", "sys.id");
|
|
1110
1123
|
if (pageEntryTo) {
|
|
1111
1124
|
pageEntryFrom.fields = await (0, contentful_1.addToRelationFields)(pageEntryFrom, "redirectTo", pageEntryTo.sys.id);
|
|
1112
1125
|
pageEntryFrom = await pageEntryFrom.update();
|
|
1126
|
+
updateEntryInArray(productPageEntries, pageEntryFrom);
|
|
1127
|
+
(0, logs_1.log)(`Created redirect from ${productCode} to ${audit.upgradeProduct}`);
|
|
1113
1128
|
}
|
|
1114
1129
|
else {
|
|
1115
|
-
(0, logs_1.log)(`Can't create redirect ${
|
|
1130
|
+
(0, logs_1.log)(`Can't create redirect ${productCode} to ${audit.upgradeProduct} because the page ${pageEntryToId} not found.`, "WARN");
|
|
1116
1131
|
}
|
|
1117
1132
|
}
|
|
1118
1133
|
pageEntryFrom = await (0, contentful_1.archiveEntry)(pageEntryFrom);
|
|
1134
|
+
updateEntryInArray(productPageEntries, pageEntryFrom);
|
|
1119
1135
|
}
|
|
1120
1136
|
}
|
|
1121
1137
|
else if (audit.what === "REMOVE_PRODUCT_RELATIONS" &&
|
|
@@ -1125,6 +1141,7 @@ const productAudit = async (audit, productEntries, catalog, defaultEnvironmentLo
|
|
|
1125
1141
|
try {
|
|
1126
1142
|
(0, logs_1.log)(`Publish existing changes of entry ${productEntry.sys.id}.`);
|
|
1127
1143
|
productEntry = await productEntry.publish();
|
|
1144
|
+
updateEntryInArray(productEntries, productEntry);
|
|
1128
1145
|
}
|
|
1129
1146
|
catch (err) {
|
|
1130
1147
|
(0, logs_1.log)(`Cannot publish changes of entry ${productEntry.sys.id}.`);
|
|
@@ -1138,33 +1155,28 @@ const productAudit = async (audit, productEntries, catalog, defaultEnvironmentLo
|
|
|
1138
1155
|
}
|
|
1139
1156
|
else {
|
|
1140
1157
|
let edit = false;
|
|
1141
|
-
if (item?.catalogCode) {
|
|
1142
|
-
productEntry.fields = await (0, contentful_1.removeFromRelationFields)(productEntry, "catalogs", item.catalogCode, true);
|
|
1143
|
-
(0, logs_1.log)(`edit catalogs`);
|
|
1144
|
-
edit = true;
|
|
1145
|
-
}
|
|
1146
1158
|
if (item?.categoryCode) {
|
|
1147
1159
|
const categoriesFieldKey = "categories" + (0, utils_1.capitalizeFirstLetter)(item.where);
|
|
1160
|
+
(0, logs_1.log)(`Removing ${item.categoryCode} from ${categoriesFieldKey}...`);
|
|
1148
1161
|
productEntry.fields = await (0, contentful_1.removeFromRelationFields)(productEntry, categoriesFieldKey, item.categoryCode, true);
|
|
1149
|
-
(0, logs_1.log)(`edit ${categoriesFieldKey}`);
|
|
1150
1162
|
edit = true;
|
|
1151
1163
|
}
|
|
1152
1164
|
if (item?.subfamilyCode) {
|
|
1153
1165
|
const subFamiliesFieldKey = "subFamilies" + (0, utils_1.capitalizeFirstLetter)(item.where);
|
|
1166
|
+
(0, logs_1.log)(`Removing ${item.subfamilyCode} from ${subFamiliesFieldKey}...`);
|
|
1154
1167
|
productEntry.fields = await (0, contentful_1.removeFromRelationFields)(productEntry, subFamiliesFieldKey, item.subfamilyCode, true);
|
|
1155
|
-
(0, logs_1.log)(`edit ${subFamiliesFieldKey}`);
|
|
1156
1168
|
edit = true;
|
|
1157
1169
|
}
|
|
1158
1170
|
if (item?.models) {
|
|
1159
1171
|
const modelsFieldKey = "models" + (0, utils_1.capitalizeFirstLetter)(item.where);
|
|
1172
|
+
(0, logs_1.log)(`Removing ${item.models} from ${modelsFieldKey}...`);
|
|
1160
1173
|
productEntry.fields = await (0, contentful_1.removeFromRelationFields)(productEntry, modelsFieldKey, item.models, true);
|
|
1161
|
-
(0, logs_1.log)(`edit ${modelsFieldKey}`);
|
|
1162
1174
|
edit = true;
|
|
1163
1175
|
}
|
|
1164
1176
|
if (item?.subModels) {
|
|
1165
1177
|
const subModelsFieldKey = "subModels" + (0, utils_1.capitalizeFirstLetter)(item.where);
|
|
1178
|
+
(0, logs_1.log)(`Removing ${item.subModels} from ${subModelsFieldKey}...`);
|
|
1166
1179
|
productEntry.fields = await (0, contentful_1.removeFromRelationFields)(productEntry, subModelsFieldKey, item.subModels, true);
|
|
1167
|
-
(0, logs_1.log)(`edit ${subModelsFieldKey}`);
|
|
1168
1180
|
edit = true;
|
|
1169
1181
|
}
|
|
1170
1182
|
const objectFieldsRelations = {
|
|
@@ -1215,9 +1227,11 @@ const productAudit = async (audit, productEntries, catalog, defaultEnvironmentLo
|
|
|
1215
1227
|
try {
|
|
1216
1228
|
(0, logs_1.log)(`update ${productEntry.sys.id}`);
|
|
1217
1229
|
productEntry = await productEntry.update();
|
|
1230
|
+
updateEntryInArray(productEntries, productEntry);
|
|
1218
1231
|
if (productEntry.isPublished()) {
|
|
1219
1232
|
try {
|
|
1220
1233
|
productEntry = await productEntry.publish();
|
|
1234
|
+
updateEntryInArray(productEntries, productEntry);
|
|
1221
1235
|
}
|
|
1222
1236
|
catch (err) {
|
|
1223
1237
|
(0, logs_1.log)(`Cannot publish entry ${productEntry.sys.id}.`);
|
|
@@ -1232,13 +1246,12 @@ const productAudit = async (audit, productEntries, catalog, defaultEnvironmentLo
|
|
|
1232
1246
|
}
|
|
1233
1247
|
else {
|
|
1234
1248
|
(0, logs_1.log)(`No valid editable criteria found`);
|
|
1235
|
-
console.log(productEntry.sys.id, "audit", audit);
|
|
1236
1249
|
}
|
|
1237
1250
|
}
|
|
1238
1251
|
}
|
|
1239
1252
|
}
|
|
1240
1253
|
else {
|
|
1241
|
-
(0, logs_1.log)(`No catalogs field found in the audit ${
|
|
1254
|
+
(0, logs_1.log)(`No catalogs field found in the audit ${productCode} product`, "WARN");
|
|
1242
1255
|
}
|
|
1243
1256
|
}
|
|
1244
1257
|
else {
|
|
@@ -1247,16 +1260,16 @@ const productAudit = async (audit, productEntries, catalog, defaultEnvironmentLo
|
|
|
1247
1260
|
return { current, continue: false };
|
|
1248
1261
|
};
|
|
1249
1262
|
const familyAudit = async (audit, familyEntries, catalog, defaultEnvironmentLocaleCode, current, allAudit) => {
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1263
|
+
const familyCode = extractProductCode(audit.product);
|
|
1264
|
+
(0, logs_1.log)(`Search family entry with id ${familyCode}...`);
|
|
1265
|
+
let familyEntry = familyEntries.find((currentEntry) => currentEntry?.fields?.code?.[defaultEnvironmentLocaleCode] === familyCode);
|
|
1253
1266
|
if (!familyEntry) {
|
|
1254
1267
|
let logMessage = "";
|
|
1255
1268
|
if (catalog) {
|
|
1256
|
-
logMessage = `The ${
|
|
1269
|
+
logMessage = `The ${familyCode} family was not found in the CMS with catalog ${catalog}`;
|
|
1257
1270
|
}
|
|
1258
1271
|
else {
|
|
1259
|
-
logMessage = `The ${
|
|
1272
|
+
logMessage = `The ${familyCode} family was not found in the CMS`;
|
|
1260
1273
|
}
|
|
1261
1274
|
(0, logs_1.log)(logMessage, "WARN");
|
|
1262
1275
|
if (logs_1.serverUtils) {
|
|
@@ -1272,12 +1285,12 @@ const familyAudit = async (audit, familyEntries, catalog, defaultEnvironmentLoca
|
|
|
1272
1285
|
return entryCatalog.sys.id;
|
|
1273
1286
|
});
|
|
1274
1287
|
if (catalog && !familyCatalogs.includes(catalog)) {
|
|
1275
|
-
(0, logs_1.log)(`Family ${
|
|
1288
|
+
(0, logs_1.log)(`Family ${familyCode} does not belong to the ${catalog} catalog`);
|
|
1276
1289
|
}
|
|
1277
1290
|
else if (audit.what === "REMOVE_FAMILY_RELATIONS") {
|
|
1278
1291
|
for (const item of audit?.catalogs) {
|
|
1279
1292
|
if (!item?.where) {
|
|
1280
|
-
(0, logs_1.log)(`catalogs.where field not exists. Family: ${
|
|
1293
|
+
(0, logs_1.log)(`catalogs.where field not exists. Family: ${familyCode} `, "WARN");
|
|
1281
1294
|
}
|
|
1282
1295
|
else {
|
|
1283
1296
|
if (item.where === "DESIGNERS" && item?.codes) {
|
|
@@ -1297,16 +1310,17 @@ const familyAudit = async (audit, familyEntries, catalog, defaultEnvironmentLoca
|
|
|
1297
1310
|
return { current, continue: false };
|
|
1298
1311
|
};
|
|
1299
1312
|
const subFamilyAudit = async (audit, subfamilyEntries, catalog, defaultEnvironmentLocaleCode, current, allAudit) => {
|
|
1300
|
-
|
|
1313
|
+
const subFamilyCode = extractProductCode(audit.product);
|
|
1314
|
+
(0, logs_1.log)(`Search subFamily entry with id ${subFamilyCode}...`);
|
|
1301
1315
|
let subFamilyEntry = subfamilyEntries.find((currentEntry) => currentEntry?.fields?.code?.[defaultEnvironmentLocaleCode] ===
|
|
1302
|
-
|
|
1316
|
+
subFamilyCode);
|
|
1303
1317
|
if (!subFamilyEntry) {
|
|
1304
1318
|
let logMessage = "";
|
|
1305
1319
|
if (catalog) {
|
|
1306
|
-
logMessage = `The ${
|
|
1320
|
+
logMessage = `The ${subFamilyCode} subFamily was not found in the CMS with catalog ${catalog}`;
|
|
1307
1321
|
}
|
|
1308
1322
|
else {
|
|
1309
|
-
logMessage = `The ${
|
|
1323
|
+
logMessage = `The ${subFamilyCode} subFamily was not found in the CMS`;
|
|
1310
1324
|
}
|
|
1311
1325
|
(0, logs_1.log)(logMessage, "WARN");
|
|
1312
1326
|
if (logs_1.serverUtils) {
|
|
@@ -1320,7 +1334,7 @@ const subFamilyAudit = async (audit, subfamilyEntries, catalog, defaultEnvironme
|
|
|
1320
1334
|
(0, logs_1.log)(`Get subFamily catalogs...`);
|
|
1321
1335
|
const subFamilyCatalog = subFamilyEntry?.fields?.catalog?.[defaultEnvironmentLocaleCode].sys.id;
|
|
1322
1336
|
if (catalog && subFamilyCatalog !== catalog) {
|
|
1323
|
-
(0, logs_1.log)(`SubFamily ${
|
|
1337
|
+
(0, logs_1.log)(`SubFamily ${subFamilyCode} does not belong to the ${catalog} catalog`);
|
|
1324
1338
|
}
|
|
1325
1339
|
else if (audit.what === "REMOVE_SUBFAMILY_RELATIONS") {
|
|
1326
1340
|
for (const item of audit?.catalogs) {
|
|
@@ -1365,7 +1379,7 @@ const audit = async (lastModified, catalog, offset = 0, limit = 150, s3FilePath
|
|
|
1365
1379
|
const filename = `${lastModified}-all-audit.json`;
|
|
1366
1380
|
const path = `audit${process.env.FPI_CTF_ENVIRONMENT
|
|
1367
1381
|
? "/" + process.env.FPI_CTF_ENVIRONMENT
|
|
1368
|
-
: ""}`;
|
|
1382
|
+
: ""}/${catalog}`;
|
|
1369
1383
|
await (0, s3_1.saveJsonToS3)(allAudit, filename, path);
|
|
1370
1384
|
const s3Path = `${path}/${filename}`;
|
|
1371
1385
|
if (logs_1.serverUtils) {
|
|
@@ -1395,19 +1409,17 @@ const audit = async (lastModified, catalog, offset = 0, limit = 150, s3FilePath
|
|
|
1395
1409
|
];
|
|
1396
1410
|
const productCodes = allAudit
|
|
1397
1411
|
.filter((item) => productAuditWhat.includes(item.what))
|
|
1398
|
-
.map((audit) => audit.product);
|
|
1412
|
+
.map((audit) => extractProductCode(audit.product));
|
|
1399
1413
|
const familyCodes = allAudit
|
|
1400
1414
|
.filter((item) => item.what === "REMOVE_FAMILY_RELATIONS")
|
|
1401
|
-
.map((audit) => audit.product);
|
|
1415
|
+
.map((audit) => extractProductCode(audit.product));
|
|
1402
1416
|
const subFamilyCodes = allAudit
|
|
1403
1417
|
.filter((item) => item.what === "REMOVE_SUBFAMILY_RELATIONS")
|
|
1404
|
-
.map((audit) => audit.product);
|
|
1418
|
+
.map((audit) => extractProductCode(audit.product));
|
|
1405
1419
|
const otherFilters = [];
|
|
1406
1420
|
if (catalog) {
|
|
1407
1421
|
otherFilters.push({ key: "fields.catalogs.sys.id[in]", value: catalog });
|
|
1408
1422
|
}
|
|
1409
|
-
(0, logs_1.log)(`Get ${productCodes.length} product entry from Contentful`);
|
|
1410
|
-
console.log("productCodes", productCodes);
|
|
1411
1423
|
const productEntries = await (0, contentful_1.getAllEntriesByCodes)(productCodes, "topicProduct", "sys,fields", "fields.code", otherFilters);
|
|
1412
1424
|
(0, logs_1.log)(`Founded ${productEntries.length} topicProduct`);
|
|
1413
1425
|
let familyEntries = [];
|
|
@@ -1425,7 +1437,7 @@ const audit = async (lastModified, catalog, offset = 0, limit = 150, s3FilePath
|
|
|
1425
1437
|
!subFamilyEntries.length) {
|
|
1426
1438
|
(0, logs_1.log)(`No items found between offset: ${offset} and limit: ${limit}. Total: ${total}`);
|
|
1427
1439
|
const nextOffset = offset + limit;
|
|
1428
|
-
const completed = limit === -1 ||
|
|
1440
|
+
const completed = limit === -1 || nextOffset >= total;
|
|
1429
1441
|
if (completed) {
|
|
1430
1442
|
(0, logs_1.log)(`Audit completed`);
|
|
1431
1443
|
}
|
|
@@ -1433,7 +1445,9 @@ const audit = async (lastModified, catalog, offset = 0, limit = 150, s3FilePath
|
|
|
1433
1445
|
const secs = (0, utils_1.secondBetweenTwoDate)(timeStart, tEnd);
|
|
1434
1446
|
(0, logs_1.log)(`Execution time: ${secs} seconds`);
|
|
1435
1447
|
if (logs_1.serverUtils) {
|
|
1436
|
-
|
|
1448
|
+
if (completed) {
|
|
1449
|
+
logs_1.serverUtils.log(`Audit completed`);
|
|
1450
|
+
}
|
|
1437
1451
|
logs_1.serverUtils.updateProgress(100);
|
|
1438
1452
|
}
|
|
1439
1453
|
return {
|
|
@@ -1970,7 +1984,7 @@ const reimportAuditProducts = async (lastModified, catalog, offset = 0, limit =
|
|
|
1970
1984
|
if (!entries.length) {
|
|
1971
1985
|
(0, logs_1.log)(`No products found between offset: ${offset} and limit: ${limit}. Total: ${total}`);
|
|
1972
1986
|
const nextOffset = offset + limit;
|
|
1973
|
-
const completed = limit === -1 ||
|
|
1987
|
+
const completed = limit === -1 || nextOffset >= total;
|
|
1974
1988
|
if (completed) {
|
|
1975
1989
|
(0, logs_1.log)(`Audit completed`);
|
|
1976
1990
|
}
|
|
@@ -2010,7 +2024,7 @@ const reimportAuditProducts = async (lastModified, catalog, offset = 0, limit =
|
|
|
2010
2024
|
const productCatalogs = productEntry?.fields?.catalogs?.[defaultEnvironmentLocaleCode].map((entryCatalog) => {
|
|
2011
2025
|
return entryCatalog.sys.id;
|
|
2012
2026
|
});
|
|
2013
|
-
if (catalog && !productCatalogs
|
|
2027
|
+
if (catalog && !productCatalogs?.includes(catalog)) {
|
|
2014
2028
|
(0, logs_1.log)(`Product ${audit.product} does not belong to the ${catalog} catalog`);
|
|
2015
2029
|
}
|
|
2016
2030
|
else {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface ImportResult {
|
|
2
|
+
offset: number;
|
|
3
|
+
limit: number;
|
|
4
|
+
completed: boolean;
|
|
5
|
+
total: number;
|
|
6
|
+
processed: number;
|
|
7
|
+
created: number;
|
|
8
|
+
updated: number;
|
|
9
|
+
skipped: number;
|
|
10
|
+
errors: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare const importSeoFromCsv: (s3CsvPath: string, offset?: number, limit?: number) => Promise<ImportResult>;
|
|
13
|
+
export default importSeoFromCsv;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.importSeoFromCsv = void 0;
|
|
7
|
+
const contentful_1 = require("../../libs/contentful");
|
|
8
|
+
const s3_1 = require("../../libs/s3");
|
|
9
|
+
const logs_1 = require("../../libs/logs");
|
|
10
|
+
const csv_parser_1 = __importDefault(require("csv-parser"));
|
|
11
|
+
const stream_1 = require("stream");
|
|
12
|
+
const LOCALES = contentful_1.cfLocales;
|
|
13
|
+
const parseCsvFromS3 = async (s3Path) => {
|
|
14
|
+
const csvString = await (0, s3_1.getFileFromS3)(s3Path);
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const results = [];
|
|
17
|
+
stream_1.Readable.from(csvString)
|
|
18
|
+
.pipe((0, csv_parser_1.default)())
|
|
19
|
+
.on("data", (data) => results.push(data))
|
|
20
|
+
.on("end", () => resolve(results))
|
|
21
|
+
.on("error", reject);
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const parseLocalizedSlug = (slugPath) => {
|
|
25
|
+
const cleanPath = slugPath.startsWith("/") ? slugPath.slice(1) : slugPath;
|
|
26
|
+
const segments = cleanPath.split("/").filter(Boolean);
|
|
27
|
+
if (segments.length < 2) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const locale = segments[0];
|
|
31
|
+
const slug = segments[segments.length - 1];
|
|
32
|
+
if (!locale || !slug) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return { locale, slug };
|
|
36
|
+
};
|
|
37
|
+
const isValidLocale = (locale) => {
|
|
38
|
+
return LOCALES.includes(locale);
|
|
39
|
+
};
|
|
40
|
+
const findPageBySlug = async (locale, slug) => {
|
|
41
|
+
const env = await (0, contentful_1.getEnvironment)();
|
|
42
|
+
const slugFieldKey = `fields.slug.${locale}`;
|
|
43
|
+
let opts = {
|
|
44
|
+
content_type: "page",
|
|
45
|
+
[slugFieldKey]: slug,
|
|
46
|
+
limit: 1,
|
|
47
|
+
include: 1,
|
|
48
|
+
};
|
|
49
|
+
let { items } = await env.getEntries(opts);
|
|
50
|
+
if (items.length === 0 && locale !== "en") {
|
|
51
|
+
(0, logs_1.log)(`Slug not found for locale "${locale}", falling back to "en"`);
|
|
52
|
+
opts = {
|
|
53
|
+
content_type: "page",
|
|
54
|
+
"fields.slug.en": slug,
|
|
55
|
+
limit: 1,
|
|
56
|
+
include: 1,
|
|
57
|
+
};
|
|
58
|
+
({ items } = await env.getEntries(opts));
|
|
59
|
+
}
|
|
60
|
+
return items.length > 0 ? items[0] : null;
|
|
61
|
+
};
|
|
62
|
+
const createTopicSeo = async (metaTitle, metaDescription) => {
|
|
63
|
+
const env = await (0, contentful_1.getEnvironment)();
|
|
64
|
+
const hasMetaTitle = Object.keys(metaTitle).length > 0;
|
|
65
|
+
const hasMetaDescription = Object.keys(metaDescription).length > 0;
|
|
66
|
+
const fields = {
|
|
67
|
+
metaTitle: hasMetaTitle ? metaTitle : { en: "" },
|
|
68
|
+
metaDescription: hasMetaDescription ? metaDescription : { en: "" },
|
|
69
|
+
};
|
|
70
|
+
const entryData = { fields };
|
|
71
|
+
const topicSeo = await env.createEntry("topicSeo", entryData);
|
|
72
|
+
await topicSeo.publish();
|
|
73
|
+
return topicSeo;
|
|
74
|
+
};
|
|
75
|
+
const updateTopicSeo = async (topicSeoEntry, metaTitle, metaDescription) => {
|
|
76
|
+
const currentFields = topicSeoEntry.fields;
|
|
77
|
+
const newMetaTitle = {};
|
|
78
|
+
for (const locale of LOCALES) {
|
|
79
|
+
if (metaTitle[locale] && metaTitle[locale].trim() !== "") {
|
|
80
|
+
newMetaTitle[locale] = metaTitle[locale];
|
|
81
|
+
}
|
|
82
|
+
else if (currentFields.metaTitle?.[locale]) {
|
|
83
|
+
newMetaTitle[locale] = currentFields.metaTitle[locale];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const newMetaDescription = {};
|
|
87
|
+
for (const locale of LOCALES) {
|
|
88
|
+
if (metaDescription[locale] && metaDescription[locale].trim() !== "") {
|
|
89
|
+
newMetaDescription[locale] = metaDescription[locale];
|
|
90
|
+
}
|
|
91
|
+
else if (currentFields.metaDescription?.[locale]) {
|
|
92
|
+
newMetaDescription[locale] = currentFields.metaDescription[locale];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const updateData = {
|
|
96
|
+
fields: {
|
|
97
|
+
metaTitle: newMetaTitle,
|
|
98
|
+
metaDescription: newMetaDescription,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
const updatedEntry = await (0, contentful_1.updateEntry)(topicSeoEntry, updateData, true);
|
|
102
|
+
return updatedEntry;
|
|
103
|
+
};
|
|
104
|
+
const associateTopicSeoToPage = async (pageEntry, topicSeoEntry) => {
|
|
105
|
+
pageEntry.fields.seo = {
|
|
106
|
+
en: {
|
|
107
|
+
sys: {
|
|
108
|
+
type: "Link",
|
|
109
|
+
linkType: "Entry",
|
|
110
|
+
id: topicSeoEntry.sys.id,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
const updatedPage = await pageEntry.update();
|
|
115
|
+
await updatedPage.publish();
|
|
116
|
+
return updatedPage;
|
|
117
|
+
};
|
|
118
|
+
const importSeoFromCsv = async (s3CsvPath, offset = 0, limit = 50) => {
|
|
119
|
+
const errors = [];
|
|
120
|
+
let processed = 0;
|
|
121
|
+
let created = 0;
|
|
122
|
+
let updated = 0;
|
|
123
|
+
let skipped = 0;
|
|
124
|
+
(0, logs_1.log)(`Importing SEO from CSV: ${s3CsvPath} (offset: ${offset}, limit: ${limit})`);
|
|
125
|
+
let csvData;
|
|
126
|
+
try {
|
|
127
|
+
csvData = await parseCsvFromS3(s3CsvPath);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
throw new Error(`Failed to parse CSV from S3: ${s3CsvPath}. ${err?.message || err}`);
|
|
131
|
+
}
|
|
132
|
+
const total = csvData.length;
|
|
133
|
+
(0, logs_1.log)(`CSV loaded: ${total} rows`);
|
|
134
|
+
let count = 0;
|
|
135
|
+
for (const row of csvData) {
|
|
136
|
+
if (offset <= count && (limit === -1 || processed < limit)) {
|
|
137
|
+
const slugPath = row.slug?.trim();
|
|
138
|
+
if (!slugPath) {
|
|
139
|
+
errors.push(`Row ${count}: slug is empty, skipping`);
|
|
140
|
+
(0, logs_1.log)(`Row ${count}: slug is empty, skipping`, "WARN");
|
|
141
|
+
count++;
|
|
142
|
+
processed++;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const parsed = parseLocalizedSlug(slugPath);
|
|
146
|
+
if (!parsed) {
|
|
147
|
+
errors.push(`Row ${count}: invalid slug path format "${slugPath}", skipping`);
|
|
148
|
+
(0, logs_1.log)(`Row ${count}: invalid slug path format "${slugPath}", skipping`, "WARN");
|
|
149
|
+
count++;
|
|
150
|
+
processed++;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const { locale, slug } = parsed;
|
|
154
|
+
if (!isValidLocale(locale)) {
|
|
155
|
+
errors.push(`Row ${count}: unknown locale "${locale}" in slug path "${slugPath}", skipping`);
|
|
156
|
+
(0, logs_1.log)(`Row ${count}: unknown locale "${locale}" in slug path "${slugPath}", skipping`, "WARN");
|
|
157
|
+
count++;
|
|
158
|
+
processed++;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
(0, logs_1.log)(`Processing row ${count}: locale="${locale}", slug="${slug}"`);
|
|
162
|
+
try {
|
|
163
|
+
const pageEntry = await findPageBySlug(locale, slug);
|
|
164
|
+
if (!pageEntry) {
|
|
165
|
+
errors.push(`Page not found for slug="${slug}" (locale=${locale})`);
|
|
166
|
+
(0, logs_1.log)(`Page not found for slug="${slug}" (locale=${locale})`, "WARN");
|
|
167
|
+
processed++;
|
|
168
|
+
count++;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const existingTopicSeo = pageEntry.fields?.seo?.en?.sys?.id;
|
|
172
|
+
const metaTitle = {};
|
|
173
|
+
const metaDescription = {};
|
|
174
|
+
if (row.metaTitle && row.metaTitle.trim() !== "") {
|
|
175
|
+
metaTitle[locale] = row.metaTitle.trim();
|
|
176
|
+
}
|
|
177
|
+
if (row.metaDescription && row.metaDescription.trim() !== "") {
|
|
178
|
+
metaDescription[locale] = row.metaDescription.trim();
|
|
179
|
+
}
|
|
180
|
+
if (existingTopicSeo) {
|
|
181
|
+
const env = await (0, contentful_1.getEnvironment)();
|
|
182
|
+
const topicSeoEntry = await env.getEntry(existingTopicSeo);
|
|
183
|
+
if (topicSeoEntry) {
|
|
184
|
+
await updateTopicSeo(topicSeoEntry, metaTitle, metaDescription);
|
|
185
|
+
(0, logs_1.log)(`Updated topicSeo ${existingTopicSeo} for page ${pageEntry.sys.id}`);
|
|
186
|
+
updated++;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
errors.push(`topicSeo ${existingTopicSeo} not found for page slug="${slug}" (locale=${locale})`);
|
|
190
|
+
(0, logs_1.log)(`topicSeo ${existingTopicSeo} not found for page slug="${slug}" (locale=${locale})`, "WARN");
|
|
191
|
+
skipped++;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
const newTopicSeo = await createTopicSeo(metaTitle, metaDescription);
|
|
196
|
+
await associateTopicSeoToPage(pageEntry, newTopicSeo);
|
|
197
|
+
(0, logs_1.log)(`Created and associated topicSeo ${newTopicSeo.sys.id} to page ${pageEntry.sys.id}`);
|
|
198
|
+
created++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
const errorMsg = `Error processing slug="${slug}" (locale=${locale}): ${err?.message || err}`;
|
|
203
|
+
errors.push(errorMsg);
|
|
204
|
+
(0, logs_1.log)(errorMsg, "ERROR");
|
|
205
|
+
}
|
|
206
|
+
processed++;
|
|
207
|
+
}
|
|
208
|
+
count++;
|
|
209
|
+
}
|
|
210
|
+
const nextOffset = offset + processed;
|
|
211
|
+
return {
|
|
212
|
+
offset: nextOffset,
|
|
213
|
+
limit,
|
|
214
|
+
completed: nextOffset >= total,
|
|
215
|
+
total,
|
|
216
|
+
processed,
|
|
217
|
+
created,
|
|
218
|
+
updated,
|
|
219
|
+
skipped,
|
|
220
|
+
errors,
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
exports.importSeoFromCsv = importSeoFromCsv;
|
|
224
|
+
exports.default = exports.importSeoFromCsv;
|
|
@@ -1,34 +1,71 @@
|
|
|
1
1
|
export interface DProductSubLine {
|
|
2
|
+
parentName: string;
|
|
2
3
|
code: string;
|
|
3
|
-
image
|
|
4
|
-
imageAlternative
|
|
4
|
+
image: string | null;
|
|
5
|
+
imageAlternative: string | null;
|
|
5
6
|
value_en: string;
|
|
6
|
-
value_en_US: string;
|
|
7
7
|
value_it: string;
|
|
8
8
|
value_es: string;
|
|
9
9
|
value_de: string;
|
|
10
10
|
value_fr: string;
|
|
11
|
-
value_sv: string;
|
|
12
|
-
value_no: string;
|
|
13
|
-
value_da: string;
|
|
14
|
-
value_ru: string;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
value_sv: string | null;
|
|
12
|
+
value_no: string | null;
|
|
13
|
+
value_da: string | null;
|
|
14
|
+
value_ru: string | null;
|
|
15
|
+
value_en_US: string | null;
|
|
16
|
+
value_ja: string | null;
|
|
17
|
+
value_zh: string | null;
|
|
18
|
+
value_ko: string | null;
|
|
19
|
+
note_en: string | null;
|
|
20
|
+
note_it: string | null;
|
|
21
|
+
note_es: string | null;
|
|
22
|
+
note_de: string | null;
|
|
23
|
+
note_fr: string | null;
|
|
24
|
+
note_sv: string | null;
|
|
25
|
+
note_no: string | null;
|
|
26
|
+
note_da: string | null;
|
|
27
|
+
note_ru: string | null;
|
|
28
|
+
note_en_US: string | null;
|
|
29
|
+
note_zh: string | null;
|
|
30
|
+
note_ja: string | null;
|
|
31
|
+
note_ko: string | null;
|
|
32
|
+
imgRelUrl: string | null;
|
|
33
|
+
imgAltRelUrl: string | null;
|
|
34
|
+
priority: number | null;
|
|
18
35
|
productLine: ProductLine;
|
|
19
36
|
}
|
|
20
37
|
export interface ProductLine {
|
|
38
|
+
parentName: string;
|
|
21
39
|
code: string;
|
|
22
|
-
image
|
|
23
|
-
imageAlternative
|
|
40
|
+
image: string | null;
|
|
41
|
+
imageAlternative: string | null;
|
|
24
42
|
value_en: string;
|
|
25
43
|
value_it: string;
|
|
26
44
|
value_es: string;
|
|
27
45
|
value_de: string;
|
|
28
46
|
value_fr: string;
|
|
29
|
-
value_sv: string;
|
|
30
|
-
value_no: string;
|
|
31
|
-
value_da: string;
|
|
32
|
-
value_ru: string;
|
|
33
|
-
value_en_US
|
|
47
|
+
value_sv: string | null;
|
|
48
|
+
value_no: string | null;
|
|
49
|
+
value_da: string | null;
|
|
50
|
+
value_ru: string | null;
|
|
51
|
+
value_en_US: string | null;
|
|
52
|
+
value_ja: string | null;
|
|
53
|
+
value_zh: string | null;
|
|
54
|
+
value_ko: string | null;
|
|
55
|
+
note_en: string | null;
|
|
56
|
+
note_it: string | null;
|
|
57
|
+
note_es: string | null;
|
|
58
|
+
note_de: string | null;
|
|
59
|
+
note_fr: string | null;
|
|
60
|
+
note_sv: string | null;
|
|
61
|
+
note_no: string | null;
|
|
62
|
+
note_da: string | null;
|
|
63
|
+
note_ru: string | null;
|
|
64
|
+
note_en_US: string | null;
|
|
65
|
+
note_zh: string | null;
|
|
66
|
+
note_ja: string | null;
|
|
67
|
+
note_ko: string | null;
|
|
68
|
+
imgRelUrl: string | null;
|
|
69
|
+
imgAltRelUrl: string | null;
|
|
70
|
+
priority: number | null;
|
|
34
71
|
}
|