pim-import 2.12.0 → 2.16.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/dist/algolia/config.js +13 -1
- package/dist/algolia/config.js.map +1 -1
- package/dist/algolia/downloads.js +1 -1
- package/dist/algolia/projects.js +129 -0
- package/dist/algolia/projects.js.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/libs/contentful-cda.js.map +1 -1
- package/dist/libs/contentful.js +30 -1
- package/dist/libs/contentful.js.map +1 -1
- package/dist/libs/imgix.js +77 -0
- package/dist/libs/imgix.js.map +1 -0
- package/dist/pim/endpoints.js +6 -1
- package/dist/pim/endpoints.js.map +1 -1
- package/dist/pim/methods/dictionary.js +28 -31
- package/dist/pim/methods/dictionary.js.map +1 -1
- package/dist/pim/methods/products.js +28 -0
- package/dist/pim/methods/products.js.map +1 -1
- package/package.json +4 -3
- package/src/algolia/config.ts +15 -2
- package/src/algolia/downloads.ts +1 -1
- package/src/algolia/projects.ts +230 -0
- package/src/index.ts +5 -0
- package/src/libs/contentful-cda.ts +7 -0
- package/src/libs/contentful.ts +44 -0
- package/src/libs/imgix.ts +106 -0
- package/src/pim/endpoints.ts +13 -0
- package/src/pim/methods/dictionary.ts +35 -34
- package/src/pim/methods/products.ts +45 -0
- package/src/types.ts +58 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { log } from "../libs/logs";
|
|
2
|
+
import {
|
|
3
|
+
getClient,
|
|
4
|
+
getEnvironmentDefaultLocaleCode,
|
|
5
|
+
getEntryByID,
|
|
6
|
+
getTopicPage,
|
|
7
|
+
getEntryImageDetails,
|
|
8
|
+
} from "../libs/contentful-cda";
|
|
9
|
+
import { getIndex, AvailableIndicesKey, removeIndexObject } from "./config";
|
|
10
|
+
import { getLocalISOTime, secondBetweenTwoDate } from "../utils";
|
|
11
|
+
import type { Entry } from "contentful-management/dist/typings/entities/entry";
|
|
12
|
+
import { CfLocalizedEntryField, AlgoliaPaginateRecords } from "../types";
|
|
13
|
+
const indexKey: AvailableIndicesKey = "projects";
|
|
14
|
+
|
|
15
|
+
export type AlgoliaProjectRecord = {
|
|
16
|
+
objectID: string;
|
|
17
|
+
name?: CfLocalizedEntryField;
|
|
18
|
+
slugs?: {};
|
|
19
|
+
isBespoke?: boolean;
|
|
20
|
+
categories?: string[];
|
|
21
|
+
applications?: string[];
|
|
22
|
+
locations?: string[];
|
|
23
|
+
thumbnail?: string;
|
|
24
|
+
fullPageImage?: string;
|
|
25
|
+
locationLabel?: string;
|
|
26
|
+
casambiUsed?: boolean;
|
|
27
|
+
lastSyncDate?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const getObject = async (
|
|
31
|
+
topicProject: Entry
|
|
32
|
+
): Promise<AlgoliaProjectRecord> => {
|
|
33
|
+
const defaultEnvironmentLocaleCode = await getEnvironmentDefaultLocaleCode();
|
|
34
|
+
log(`Sync the ${topicProject.sys.id} topicProject...`);
|
|
35
|
+
let topicProjectWithFields: Entry = topicProject;
|
|
36
|
+
|
|
37
|
+
if (!topicProjectWithFields?.fields) {
|
|
38
|
+
log(`Get the ${topicProject.sys.id} topicProject details...`);
|
|
39
|
+
topicProjectWithFields = await getEntryByID(
|
|
40
|
+
topicProject.sys.id,
|
|
41
|
+
"topicProject"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!topicProjectWithFields) {
|
|
46
|
+
log(`The topicProject ${topicProject.sys.id} not found`, "WARN");
|
|
47
|
+
|
|
48
|
+
const recordFail: AlgoliaProjectRecord = {
|
|
49
|
+
objectID: topicProject?.sys?.id,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return recordFail; // return objectID to delete the record if it exists
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
log(`Get page details...`);
|
|
56
|
+
const page = await getTopicPage(topicProject.sys.id, "fields.slug");
|
|
57
|
+
const slugs = page?.fields.slug || {};
|
|
58
|
+
|
|
59
|
+
log(`Get thumbnail details...`);
|
|
60
|
+
const thumbnail = await getEntryImageDetails(
|
|
61
|
+
topicProjectWithFields,
|
|
62
|
+
"thumbnail"
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
log(`Get fullPageImage details...`);
|
|
66
|
+
const fullPageImage = await getEntryImageDetails(
|
|
67
|
+
topicProjectWithFields,
|
|
68
|
+
"fullPageImage"
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// Single record
|
|
72
|
+
const record: AlgoliaProjectRecord = {
|
|
73
|
+
objectID: topicProjectWithFields.sys.id,
|
|
74
|
+
name:
|
|
75
|
+
topicProjectWithFields?.fields?.title?.[defaultEnvironmentLocaleCode] ||
|
|
76
|
+
"",
|
|
77
|
+
slugs,
|
|
78
|
+
isBespoke:
|
|
79
|
+
!!topicProjectWithFields?.fields?.isBespokeProject?.[
|
|
80
|
+
defaultEnvironmentLocaleCode
|
|
81
|
+
],
|
|
82
|
+
categories:
|
|
83
|
+
topicProjectWithFields?.fields?.categories?.[
|
|
84
|
+
defaultEnvironmentLocaleCode
|
|
85
|
+
] || [],
|
|
86
|
+
applications:
|
|
87
|
+
topicProjectWithFields?.fields?.applications?.[
|
|
88
|
+
defaultEnvironmentLocaleCode
|
|
89
|
+
] || [],
|
|
90
|
+
locations:
|
|
91
|
+
topicProjectWithFields?.fields?.locations?.[
|
|
92
|
+
defaultEnvironmentLocaleCode
|
|
93
|
+
] || [],
|
|
94
|
+
locationLabel:
|
|
95
|
+
topicProjectWithFields?.fields?.locationLabel?.[
|
|
96
|
+
defaultEnvironmentLocaleCode
|
|
97
|
+
] || "",
|
|
98
|
+
casambiUsed:
|
|
99
|
+
topicProjectWithFields?.fields?.casambiUsed?.[
|
|
100
|
+
defaultEnvironmentLocaleCode
|
|
101
|
+
] || false,
|
|
102
|
+
thumbnail,
|
|
103
|
+
fullPageImage,
|
|
104
|
+
lastSyncDate: getLocalISOTime(),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return record;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const reindexProject = async (topicProjectId: string) => {
|
|
111
|
+
const defaultEnvironmentLocaleCode = await getEnvironmentDefaultLocaleCode();
|
|
112
|
+
const timeStart = new Date();
|
|
113
|
+
|
|
114
|
+
log(`reindexProject - entryId: ${topicProjectId} `);
|
|
115
|
+
|
|
116
|
+
const topicProject = await getEntryByID(topicProjectId, "topicProject");
|
|
117
|
+
|
|
118
|
+
if (!topicProject) {
|
|
119
|
+
log(`No topicProjectId found with id ${topicProjectId}`, "WARN");
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const object = await getObject(topicProject);
|
|
124
|
+
|
|
125
|
+
// Save record to Algolia
|
|
126
|
+
const index = getIndex(indexKey);
|
|
127
|
+
let record = null;
|
|
128
|
+
const slugs: any = object?.slugs;
|
|
129
|
+
if (slugs?.[defaultEnvironmentLocaleCode]) {
|
|
130
|
+
log(`Save object`);
|
|
131
|
+
record = await index.saveObject(object);
|
|
132
|
+
} else {
|
|
133
|
+
log(`Delete object`);
|
|
134
|
+
record = await index.deleteObject(object.objectID);
|
|
135
|
+
}
|
|
136
|
+
const timeEnd = new Date();
|
|
137
|
+
const seconds = secondBetweenTwoDate(timeStart, timeEnd);
|
|
138
|
+
log(`Execution time: ${seconds} seconds`);
|
|
139
|
+
|
|
140
|
+
return { ...record, ...object };
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get Objects
|
|
145
|
+
*
|
|
146
|
+
* @param offset
|
|
147
|
+
* @param limit
|
|
148
|
+
* @param filterKey
|
|
149
|
+
* @param filterValue
|
|
150
|
+
* @returns
|
|
151
|
+
*/
|
|
152
|
+
const getObjects = async (
|
|
153
|
+
offset: number,
|
|
154
|
+
limit: number,
|
|
155
|
+
filterKey?: string,
|
|
156
|
+
filterValue?: string
|
|
157
|
+
): Promise<AlgoliaPaginateRecords> => {
|
|
158
|
+
const client = await getClient();
|
|
159
|
+
|
|
160
|
+
const opts: any = {
|
|
161
|
+
content_type: "topicProject",
|
|
162
|
+
limit,
|
|
163
|
+
skip: offset,
|
|
164
|
+
locale: "*",
|
|
165
|
+
// select: "",
|
|
166
|
+
};
|
|
167
|
+
if (filterKey && filterValue) {
|
|
168
|
+
opts[filterKey] = filterValue;
|
|
169
|
+
}
|
|
170
|
+
const { items, total } = await client.getEntries(opts);
|
|
171
|
+
|
|
172
|
+
const objects: AlgoliaProjectRecord[] = [];
|
|
173
|
+
let count: number = Number(offset);
|
|
174
|
+
for (const topicProduct of items) {
|
|
175
|
+
log(`${++count} of ${total}`, "INFO");
|
|
176
|
+
const timeStart = new Date();
|
|
177
|
+
const record = await getObject(topicProduct);
|
|
178
|
+
const timeEnd = new Date();
|
|
179
|
+
const seconds = secondBetweenTwoDate(timeStart, timeEnd);
|
|
180
|
+
log(`Execution time: ${seconds} seconds`);
|
|
181
|
+
objects.push(record);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
objects,
|
|
186
|
+
offset: Number(offset) + Number(limit),
|
|
187
|
+
limit: Number(limit),
|
|
188
|
+
completed: count >= total, // if is true the import is completed
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export const reindexProjects = async (
|
|
193
|
+
offset: number = 0,
|
|
194
|
+
limit: number = 50,
|
|
195
|
+
filterKey: string = "",
|
|
196
|
+
filterValue: string = ""
|
|
197
|
+
) => {
|
|
198
|
+
const defaultEnvironmentLocaleCode = await getEnvironmentDefaultLocaleCode();
|
|
199
|
+
const timeStart = new Date();
|
|
200
|
+
log(
|
|
201
|
+
`reindexProjects - filterKey: ${filterKey} filterValue: ${filterValue} offset: ${offset} limit: ${limit} `
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const records = await getObjects(offset, limit, filterKey, filterValue);
|
|
205
|
+
const objectsToSave = records.objects.filter(
|
|
206
|
+
(object) => object?.slugs?.[defaultEnvironmentLocaleCode]
|
|
207
|
+
);
|
|
208
|
+
const objectIdsToDelete = records.objects
|
|
209
|
+
.filter((object) => !object?.slugs?.[defaultEnvironmentLocaleCode])
|
|
210
|
+
.map((object) => object.objectID);
|
|
211
|
+
|
|
212
|
+
// Save records to Algolia
|
|
213
|
+
const index = getIndex(indexKey);
|
|
214
|
+
log(`Save ${objectsToSave.length} objects to ${index.indexName} index`);
|
|
215
|
+
const savedObjectIDs = await index.saveObjects(objectsToSave);
|
|
216
|
+
log(
|
|
217
|
+
`Delete ${objectIdsToDelete.length} objects from ${index.indexName} index`
|
|
218
|
+
);
|
|
219
|
+
const deletedObjectIDs = await index.deleteObjects(objectIdsToDelete);
|
|
220
|
+
|
|
221
|
+
const timeEnd = new Date();
|
|
222
|
+
const seconds = secondBetweenTwoDate(timeStart, timeEnd);
|
|
223
|
+
log(`Execution time: ${seconds} seconds`);
|
|
224
|
+
|
|
225
|
+
return { ...records, ...savedObjectIDs, ...deletedObjectIDs };
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export const removeProjectObject = async (objectId: string) => {
|
|
229
|
+
return removeIndexObject(objectId, indexKey);
|
|
230
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -71,6 +71,11 @@ export {
|
|
|
71
71
|
reindexDownloads,
|
|
72
72
|
removeDownloadObject,
|
|
73
73
|
} from "./algolia/downloads";
|
|
74
|
+
export {
|
|
75
|
+
reindexProject,
|
|
76
|
+
reindexProjects,
|
|
77
|
+
removeProjectObject,
|
|
78
|
+
} from "./algolia/projects";
|
|
74
79
|
// export { log } from "./libs/logs";
|
|
75
80
|
// export { initSentry } from "./libs/sentry";
|
|
76
81
|
|
|
@@ -455,6 +455,13 @@ export const getDictionaryLocaleValue = async (
|
|
|
455
455
|
}
|
|
456
456
|
};
|
|
457
457
|
|
|
458
|
+
/**
|
|
459
|
+
* Get topic page
|
|
460
|
+
*
|
|
461
|
+
* @param topicId
|
|
462
|
+
* @param select Default: "sys,fields"
|
|
463
|
+
* @returns
|
|
464
|
+
*/
|
|
458
465
|
export const getTopicPage = async (
|
|
459
466
|
topicId: string,
|
|
460
467
|
select: string = "sys,fields"
|
package/src/libs/contentful.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
TopicDetailsResponse,
|
|
21
21
|
AssetPropFields,
|
|
22
22
|
OtherFilters,
|
|
23
|
+
WrapperImageFields,
|
|
23
24
|
} from "../types";
|
|
24
25
|
import {
|
|
25
26
|
sleep,
|
|
@@ -1233,3 +1234,46 @@ export const getTopicPage = async (
|
|
|
1233
1234
|
|
|
1234
1235
|
return items?.[0];
|
|
1235
1236
|
};
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Create wrapperImgix if not exists or get it if already exists
|
|
1240
|
+
*
|
|
1241
|
+
* @param id The wrapperImgix ID
|
|
1242
|
+
* @param data See WrapperImageFields
|
|
1243
|
+
* @returns
|
|
1244
|
+
*/
|
|
1245
|
+
export const createWrapperImgix = async (
|
|
1246
|
+
id: string,
|
|
1247
|
+
data: WrapperImageFields
|
|
1248
|
+
) => {
|
|
1249
|
+
let wrapperImgix = await getEntryByID(id, "wrapperImgix");
|
|
1250
|
+
if (wrapperImgix) {
|
|
1251
|
+
log(`wrapperImgix with id ${id} already exists`);
|
|
1252
|
+
return wrapperImgix;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
log(`wrapperImgix with id ${id} not found, create it...`);
|
|
1256
|
+
|
|
1257
|
+
const defaultEnvCode = await getEnvironmentDefaultLocaleCode();
|
|
1258
|
+
const entryData: any = {
|
|
1259
|
+
fields: {},
|
|
1260
|
+
};
|
|
1261
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1262
|
+
const fieldValue: any = value;
|
|
1263
|
+
if (!entryData.fields?.[key]) {
|
|
1264
|
+
entryData.fields[key] = {};
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
if (fieldValue?.[defaultEnvCode] || fieldValue?.[defaultEnvCode] === null) {
|
|
1268
|
+
fieldValue[defaultEnvCode] =
|
|
1269
|
+
fieldValue[defaultEnvCode] === null ? "" : fieldValue[defaultEnvCode];
|
|
1270
|
+
entryData.fields[key] = fieldValue;
|
|
1271
|
+
} else {
|
|
1272
|
+
entryData.fields[key][defaultEnvCode] = fieldValue;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
wrapperImgix = await createEntryWithId("wrapperImgix", id, entryData, true);
|
|
1277
|
+
|
|
1278
|
+
return wrapperImgix;
|
|
1279
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import ImgixAPI from "imgix-management-js";
|
|
2
|
+
import { getPimDomain } from "../pim/endpoints";
|
|
3
|
+
import { log } from "./logs";
|
|
4
|
+
import { ImgixAttributes, ImgixData } from "../types";
|
|
5
|
+
|
|
6
|
+
let imgix: ImgixAPI;
|
|
7
|
+
export const getImgix = () => {
|
|
8
|
+
if (imgix) {
|
|
9
|
+
return imgix;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
imgix = new ImgixAPI({
|
|
13
|
+
apiKey: process.env.FPI_IMGIX_API_KEY || "",
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return imgix;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const getSources = async () => {
|
|
20
|
+
const imgix = getImgix();
|
|
21
|
+
return await imgix.request("sources");
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get origin path by pim url
|
|
26
|
+
*
|
|
27
|
+
* @axample
|
|
28
|
+
* from: https://dam.flos.net/damflos/products//ARCHITECTURAL/ARCHITECTURAL_SYSTEMS_LED-CURTAIN/03.3901.30C/FLOS-LED-CURTAIN-WHITE-1950X1950.JPG
|
|
29
|
+
* to: damflos/products//ARCHITECTURAL/ARCHITECTURAL_SYSTEMS_LED-CURTAIN/03.3901.30C/FLOS-LED-CURTAIN-WHITE-1950X1950.JPG
|
|
30
|
+
*
|
|
31
|
+
* @param pimImgUrl
|
|
32
|
+
* @returns
|
|
33
|
+
*/
|
|
34
|
+
export const getOriginPathByPimUrl = (pimImgUrl: string) => {
|
|
35
|
+
const pimDomain = getPimDomain();
|
|
36
|
+
return pimImgUrl.substring(pimImgUrl.indexOf(pimDomain) + pimDomain.length);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const getImageAttributesByOriginPath = async (
|
|
40
|
+
originPath: string,
|
|
41
|
+
sourceId: string
|
|
42
|
+
) => {
|
|
43
|
+
const url = `assets/${sourceId}${originPath}`;
|
|
44
|
+
log(`getImageAttributesByOriginPath: ${url}`);
|
|
45
|
+
const imgix = getImgix();
|
|
46
|
+
return await imgix
|
|
47
|
+
.request(url)
|
|
48
|
+
.then((response) => {
|
|
49
|
+
const imgDetails: any = response;
|
|
50
|
+
return imgDetails?.data?.attributes as ImgixAttributes;
|
|
51
|
+
})
|
|
52
|
+
.catch((err) => {
|
|
53
|
+
console.log(err.response.errors);
|
|
54
|
+
log(
|
|
55
|
+
`${err.response.errors[0].detail} sourceId: ${sourceId} originPath: ${originPath}`,
|
|
56
|
+
"WARN"
|
|
57
|
+
);
|
|
58
|
+
return {};
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const getImageAttributesByPimUrl = async (pimUrl: string) => {
|
|
63
|
+
const originPath = getOriginPathByPimUrl(pimUrl);
|
|
64
|
+
const sourceId = process.env.FPI_IMGIX_PIM_SOURCE_ID || "";
|
|
65
|
+
|
|
66
|
+
return (await getImageAttributesByOriginPath(
|
|
67
|
+
originPath,
|
|
68
|
+
sourceId
|
|
69
|
+
)) as ImgixAttributes;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get imgix URL by origin path
|
|
74
|
+
*
|
|
75
|
+
* @axample
|
|
76
|
+
* from: /damflos/products//ARCHITECTURAL/ARCHITECTURAL_SYSTEMS_LED-CURTAIN/03.3901.30C/FLOS-LED-CURTAIN-WHITE-1950X1950.JPG
|
|
77
|
+
* to: https://flos-pim.imgix.net/damflos/products//ARCHITECTURAL/ARCHITECTURAL_SYSTEMS_LED-CURTAIN/03.3901.30C/FLOS-LED-CURTAIN-WHITE-1950X1950.JPG
|
|
78
|
+
*
|
|
79
|
+
* @param originPath
|
|
80
|
+
* @returns
|
|
81
|
+
*/
|
|
82
|
+
const getImgixUrlByOriginPath = (originPath: string) => {
|
|
83
|
+
return `https://${process.env.FPI_IMGIX_PIM_IMAGE_DOMAIN}${originPath}`;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const getWrapperImgixAttributesByPimUrl = async (pimUrl: string) => {
|
|
87
|
+
const allImgAttributes = await getImageAttributesByPimUrl(pimUrl);
|
|
88
|
+
let imgixData: ImgixData | null = null;
|
|
89
|
+
|
|
90
|
+
if (Object.entries(allImgAttributes).length) {
|
|
91
|
+
imgixData = {
|
|
92
|
+
url: getImgixUrlByOriginPath(allImgAttributes.origin_path),
|
|
93
|
+
details: {
|
|
94
|
+
size: allImgAttributes.file_size,
|
|
95
|
+
image: {
|
|
96
|
+
width: allImgAttributes.media_width,
|
|
97
|
+
height: allImgAttributes.media_height,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
fileName: allImgAttributes?.name || "",
|
|
101
|
+
contentType: allImgAttributes.content_type,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return imgixData;
|
|
106
|
+
};
|
package/src/pim/endpoints.ts
CHANGED
|
@@ -15,6 +15,19 @@ import { checkConfig, config } from "./config";
|
|
|
15
15
|
|
|
16
16
|
type AvailableOtherCatalogDataEndpoint = "models" | "submodels" | "subfamilies";
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Get pim domain by pim base url
|
|
20
|
+
*
|
|
21
|
+
* @returns
|
|
22
|
+
*/
|
|
23
|
+
export const getPimDomain = () => {
|
|
24
|
+
const pimBaseUrl = process.env.FPI_PIM_BASE_URL || "";
|
|
25
|
+
return pimBaseUrl.substring(
|
|
26
|
+
pimBaseUrl.indexOf("://") + 3,
|
|
27
|
+
pimBaseUrl.indexOf("/.rest")
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
18
31
|
/**
|
|
19
32
|
* Get the catalog taxonomy hierarchy
|
|
20
33
|
*
|
|
@@ -240,36 +240,30 @@ export const importDictionaryFields = async (
|
|
|
240
240
|
|
|
241
241
|
// Get dictionary data
|
|
242
242
|
log("Get dictionary data");
|
|
243
|
-
const pimDictionaryData: DictionaryRecord[] =
|
|
243
|
+
const pimDictionaryData: Partial<Record<string, DictionaryRecord[]>> =
|
|
244
|
+
await getDictionary();
|
|
244
245
|
|
|
245
246
|
if (pimDictionaryData) {
|
|
246
247
|
// Filter required fields
|
|
247
248
|
log("Filter required fields");
|
|
248
|
-
const dictionaryData = Object.entries(pimDictionaryData);
|
|
249
249
|
let count: number = 0;
|
|
250
250
|
const data: ObjectPartial = {};
|
|
251
|
-
for (const
|
|
251
|
+
for (const productFieldRequiredData of productFieldsRequiredData) {
|
|
252
252
|
if (offset <= count || limit === -1) {
|
|
253
|
-
if (
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const dictionaryRecord: DictionaryRecord = value;
|
|
260
|
-
if (currentRequiredField.parent) {
|
|
261
|
-
if (!data[currentRequiredField.parent]) {
|
|
262
|
-
data[currentRequiredField.parent] = {};
|
|
263
|
-
}
|
|
264
|
-
data[currentRequiredField.parent][productFieldKey] =
|
|
265
|
-
dictionaryRecord;
|
|
266
|
-
} else {
|
|
267
|
-
data[productFieldKey] = dictionaryRecord;
|
|
268
|
-
}
|
|
253
|
+
if (pimDictionaryData?.[productFieldRequiredData.dictionary]) {
|
|
254
|
+
const dictionaryRecord =
|
|
255
|
+
pimDictionaryData?.[productFieldRequiredData.dictionary];
|
|
256
|
+
if (productFieldRequiredData.parent) {
|
|
257
|
+
if (!data[productFieldRequiredData.parent]) {
|
|
258
|
+
data[productFieldRequiredData.parent] = {};
|
|
269
259
|
}
|
|
260
|
+
data[productFieldRequiredData.parent][
|
|
261
|
+
productFieldRequiredData.key
|
|
262
|
+
] = dictionaryRecord;
|
|
263
|
+
} else {
|
|
264
|
+
data[productFieldRequiredData.key] = dictionaryRecord;
|
|
270
265
|
}
|
|
271
266
|
}
|
|
272
|
-
|
|
273
267
|
if (limit !== -1 && count + 1 - offset >= limit) {
|
|
274
268
|
break;
|
|
275
269
|
}
|
|
@@ -277,20 +271,27 @@ export const importDictionaryFields = async (
|
|
|
277
271
|
count++;
|
|
278
272
|
}
|
|
279
273
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
274
|
+
const completed = count >= productFieldsRequiredData.length;
|
|
275
|
+
|
|
276
|
+
if (completed) {
|
|
277
|
+
log("Create certifications");
|
|
278
|
+
let certifications: DictionaryRecord[] = [];
|
|
279
|
+
const pimCertifications: { string: DictionaryRecord[] } =
|
|
280
|
+
data.certification;
|
|
281
|
+
for (const [key, values] of Object.entries(pimCertifications)) {
|
|
282
|
+
log(`Add ${key} values to certifications`);
|
|
283
|
+
certifications = certifications.concat(values);
|
|
284
|
+
}
|
|
285
|
+
log(`Remove unwanted values to certifications`);
|
|
286
|
+
certifications = certifications.filter((value) => {
|
|
287
|
+
return sanitizeValue(value?.code);
|
|
288
|
+
});
|
|
289
|
+
log(`Add certifications to data`);
|
|
290
|
+
data.certifications = certifications;
|
|
291
|
+
|
|
292
|
+
log("Create catalogs");
|
|
293
|
+
data.catalogs = pimDictionaryData.Catalog;
|
|
287
294
|
}
|
|
288
|
-
log(`Remove unwanted values to certifications`);
|
|
289
|
-
certifications = certifications.filter((value) => {
|
|
290
|
-
return sanitizeValue(value?.code);
|
|
291
|
-
});
|
|
292
|
-
log(`Add certifications to data`);
|
|
293
|
-
data.certifications = certifications;
|
|
294
295
|
|
|
295
296
|
if (Object.entries(data).length) {
|
|
296
297
|
// Create/Update Contentful entry
|
|
@@ -303,7 +304,7 @@ export const importDictionaryFields = async (
|
|
|
303
304
|
return {
|
|
304
305
|
offset: Number(offset) + Number(limit),
|
|
305
306
|
limit: Number(limit),
|
|
306
|
-
completed
|
|
307
|
+
completed, // if is true the import is completed
|
|
307
308
|
};
|
|
308
309
|
} else {
|
|
309
310
|
log("no data found in the dictionary", "ERROR");
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
CfSys,
|
|
14
14
|
ObjectPartial,
|
|
15
15
|
ContentfulAssetFile,
|
|
16
|
+
WrapperImageFields,
|
|
16
17
|
} from "../../types";
|
|
17
18
|
import { log } from "../../libs/logs";
|
|
18
19
|
import {
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
getEnvironment,
|
|
27
28
|
addFieldValue,
|
|
28
29
|
addToRelationFields,
|
|
30
|
+
createWrapperImgix,
|
|
29
31
|
} from "../../libs/contentful";
|
|
30
32
|
import type {
|
|
31
33
|
Entry,
|
|
@@ -52,6 +54,7 @@ import { getAudit, getProductDetails } from "../endpoints";
|
|
|
52
54
|
import { getCategoryTopicCode } from "./catalogs";
|
|
53
55
|
import productFieldsRequiredData from "../data/productFields.json";
|
|
54
56
|
import mime from "mime-types";
|
|
57
|
+
import { getWrapperImgixAttributesByPimUrl } from "../../libs/imgix";
|
|
55
58
|
|
|
56
59
|
export type AvailableProductStatus =
|
|
57
60
|
| "To be review"
|
|
@@ -873,6 +876,48 @@ const getProductData = async (
|
|
|
873
876
|
"Asset"
|
|
874
877
|
);
|
|
875
878
|
}
|
|
879
|
+
|
|
880
|
+
// THUMB WRAPPER IMGIX
|
|
881
|
+
const wrapperImgixID = pimAssetThumb.md5;
|
|
882
|
+
let wrapperImgix = await getEntryByID(wrapperImgixID, "wrapperImgix");
|
|
883
|
+
if (wrapperImgix) {
|
|
884
|
+
log(`wrapperImgix with id ${wrapperImgixID} already exists`);
|
|
885
|
+
} else {
|
|
886
|
+
log(
|
|
887
|
+
`Add thumbnail imgix wrapper with id ${wrapperImgixID} to Contentful`
|
|
888
|
+
);
|
|
889
|
+
const altText: any = getPimTranslations(pimAssetThumb, "title");
|
|
890
|
+
if (altText[defaultEnvironmentLocaleCode] === null) {
|
|
891
|
+
altText[defaultEnvironmentLocaleCode] = "Thumbnail";
|
|
892
|
+
}
|
|
893
|
+
const wrapperImgixAttributes = await getWrapperImgixAttributesByPimUrl(
|
|
894
|
+
pimAssetThumb.url
|
|
895
|
+
);
|
|
896
|
+
if (wrapperImgixAttributes) {
|
|
897
|
+
const wrapperImgixFields: WrapperImageFields = {
|
|
898
|
+
internalName: `${pimAssetThumb?.assetType?.code}_${pimAssetThumb.md5}`,
|
|
899
|
+
imgixData: wrapperImgixAttributes,
|
|
900
|
+
altText,
|
|
901
|
+
};
|
|
902
|
+
wrapperImgix = await createWrapperImgix(
|
|
903
|
+
wrapperImgixID,
|
|
904
|
+
wrapperImgixFields
|
|
905
|
+
);
|
|
906
|
+
} else {
|
|
907
|
+
log(
|
|
908
|
+
`Unable to create wrapperimagix because the image was not found on imgix. Image URL: ${pimAssetThumb.url}`,
|
|
909
|
+
"WARN"
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
if (wrapperImgix?.sys?.id) {
|
|
915
|
+
data.fields = await addToRelationFields(
|
|
916
|
+
data,
|
|
917
|
+
"thumbnailImgix",
|
|
918
|
+
wrapperImgix.sys.id
|
|
919
|
+
);
|
|
920
|
+
}
|
|
876
921
|
} else {
|
|
877
922
|
log(`No thumbnail found`, "WARN");
|
|
878
923
|
}
|
package/src/types.ts
CHANGED
|
@@ -194,3 +194,61 @@ export interface OtherFilters {
|
|
|
194
194
|
key: string;
|
|
195
195
|
value: string;
|
|
196
196
|
}
|
|
197
|
+
|
|
198
|
+
export interface ImgixData {
|
|
199
|
+
url: string;
|
|
200
|
+
details: ImgixDataDetails;
|
|
201
|
+
fileName: string;
|
|
202
|
+
contentType: string;
|
|
203
|
+
}
|
|
204
|
+
export interface ImgixDataDetails {
|
|
205
|
+
size: number;
|
|
206
|
+
image: ImgixDataImageDetails;
|
|
207
|
+
}
|
|
208
|
+
interface ImgixDataImageDetails {
|
|
209
|
+
width: number;
|
|
210
|
+
height: number;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export type WrapperImageFields = {
|
|
214
|
+
internalName: string;
|
|
215
|
+
imgixData: ImgixData;
|
|
216
|
+
altText: CfLocalizedEntryField;
|
|
217
|
+
seoTitleTag?: "h1" | "h2" | "h3" | "h4";
|
|
218
|
+
caption?: CfLocalizedEntryField;
|
|
219
|
+
blurhash?: string;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
export interface ImgixAttributes {
|
|
223
|
+
analyzed_content_warnings: boolean;
|
|
224
|
+
analyzed_faces: boolean;
|
|
225
|
+
analyzed_tags: boolean;
|
|
226
|
+
categories?: null[] | null;
|
|
227
|
+
color_model: string;
|
|
228
|
+
color_profile: string;
|
|
229
|
+
colors: string;
|
|
230
|
+
content_type: string;
|
|
231
|
+
custom_fields?: null;
|
|
232
|
+
date_created: number;
|
|
233
|
+
date_modified: number;
|
|
234
|
+
description?: null;
|
|
235
|
+
dpi_height: number;
|
|
236
|
+
dpi_width: number;
|
|
237
|
+
face_count?: null;
|
|
238
|
+
file_size: number;
|
|
239
|
+
has_frames: boolean;
|
|
240
|
+
media_height: number;
|
|
241
|
+
media_kind: string;
|
|
242
|
+
media_width: number;
|
|
243
|
+
name?: null;
|
|
244
|
+
origin_path: string;
|
|
245
|
+
source_id: string;
|
|
246
|
+
tags: Record<string, any>;
|
|
247
|
+
uploaded_by: string;
|
|
248
|
+
uploaded_by_api: boolean;
|
|
249
|
+
warning_adult: number;
|
|
250
|
+
warning_medical: number;
|
|
251
|
+
warning_racy: number;
|
|
252
|
+
warning_spoof: number;
|
|
253
|
+
warning_violence: number;
|
|
254
|
+
}
|