@open-mercato/sync-akeneo 0.9.1-develop.7574.1.837288eec1 → 0.10.1-develop.7576.1.21d5b50435
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/modules/sync_akeneo/lib/catalog-importer.js +40 -89
- package/dist/modules/sync_akeneo/lib/catalog-importer.js.map +2 -2
- package/dist/modules/sync_akeneo/lib/client.js +70 -143
- package/dist/modules/sync_akeneo/lib/client.js.map +2 -2
- package/package.json +6 -6
- package/src/modules/sync_akeneo/i18n/de.json +0 -1
- package/src/modules/sync_akeneo/i18n/en.json +0 -1
- package/src/modules/sync_akeneo/i18n/es.json +0 -1
- package/src/modules/sync_akeneo/i18n/ko.json +0 -1
- package/src/modules/sync_akeneo/i18n/pl.json +0 -1
- package/src/modules/sync_akeneo/lib/catalog-importer.ts +44 -97
- package/src/modules/sync_akeneo/lib/client.ts +98 -174
- package/src/modules/sync_akeneo/__tests__/catalog-importer.media-limits.test.ts +0 -426
- package/src/modules/sync_akeneo/__tests__/client.response-limits.test.ts +0 -339
|
@@ -3,8 +3,6 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
3
3
|
import { emitCrudSideEffects, setCustomFieldsIfAny } from "@open-mercato/shared/lib/commands/helpers";
|
|
4
4
|
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
|
|
5
5
|
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
6
|
-
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
|
-
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
8
6
|
import { SyncExternalIdMapping } from "@open-mercato/core/modules/integrations/data/entities";
|
|
9
7
|
import {
|
|
10
8
|
CatalogOffer,
|
|
@@ -22,9 +20,9 @@ import { normalizeEntityFieldsetConfig } from "@open-mercato/core/modules/entiti
|
|
|
22
20
|
import { invalidateDefinitionsCache } from "@open-mercato/core/modules/entities/api/definitions.cache";
|
|
23
21
|
import { Attachment } from "@open-mercato/core/modules/attachments/data/entities";
|
|
24
22
|
import { buildAttachmentFileUrl, buildAttachmentImageUrl, slugifyAttachmentFileName } from "@open-mercato/core/modules/attachments/lib/imageUrls";
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
} from "@open-mercato/core/modules/attachments/lib/
|
|
23
|
+
import { deletePartitionFile, storePartitionFile } from "@open-mercato/core/modules/attachments/lib/storage";
|
|
24
|
+
import { ensureDefaultPartitions, resolveDefaultPartitionCode } from "@open-mercato/core/modules/attachments/lib/partitions";
|
|
25
|
+
import { mergeAttachmentMetadata } from "@open-mercato/core/modules/attachments/lib/metadata";
|
|
28
26
|
import { attachmentCrudEvents, attachmentCrudIndexer } from "@open-mercato/core/modules/attachments/lib/crud";
|
|
29
27
|
import { emitCatalogQueryIndexEvent } from "@open-mercato/core/modules/catalog/commands/shared";
|
|
30
28
|
import { SalesChannel } from "@open-mercato/core/modules/sales/data/entities";
|
|
@@ -37,14 +35,8 @@ import {
|
|
|
37
35
|
slugifyAkeneoCode,
|
|
38
36
|
buildDefaultAkeneoMapping
|
|
39
37
|
} from "./shared.js";
|
|
40
|
-
import { isAkeneoResponseTooLargeError } from "./client.js";
|
|
41
38
|
import { inferAkeneoProductMapping } from "./inference.js";
|
|
42
39
|
import { normalizeMarkdownText } from "./markdown.js";
|
|
43
|
-
const REJECTED_MEDIA_UPLOAD_CODES = /* @__PURE__ */ new Set([
|
|
44
|
-
"dangerous_executable",
|
|
45
|
-
"max_upload_size",
|
|
46
|
-
"active_content"
|
|
47
|
-
]);
|
|
48
40
|
const PRODUCT_ENTITY_ID = "catalog:catalog_product";
|
|
49
41
|
const VARIANT_ENTITY_ID = "catalog:catalog_product_variant";
|
|
50
42
|
function readPreferredAkeneoValue(values, attributeCode, locale, channel) {
|
|
@@ -1756,61 +1748,40 @@ async function createAkeneoImporter(client, scope) {
|
|
|
1756
1748
|
}
|
|
1757
1749
|
}
|
|
1758
1750
|
}
|
|
1759
|
-
async function recordRejectedMedia(asset, reason) {
|
|
1760
|
-
const integrationLogService = container.resolve("integrationLogService");
|
|
1761
|
-
await integrationLogService.write({
|
|
1762
|
-
integrationId: "sync_akeneo",
|
|
1763
|
-
level: "warn",
|
|
1764
|
-
code: "sync_akeneo.media_rejected",
|
|
1765
|
-
message: `Akeneo media ${asset.remote.codeOrUrl} was not imported: ${reason}`,
|
|
1766
|
-
scopeEntityType: asset.entityId,
|
|
1767
|
-
scopeEntityId: asset.recordId,
|
|
1768
|
-
payload: {
|
|
1769
|
-
externalId: asset.externalId,
|
|
1770
|
-
remoteCode: asset.remote.codeOrUrl,
|
|
1771
|
-
kind: asset.kind,
|
|
1772
|
-
reason
|
|
1773
|
-
}
|
|
1774
|
-
}, scope);
|
|
1775
|
-
}
|
|
1776
1751
|
async function upsertAsset(asset) {
|
|
1777
1752
|
const existingAttachmentId = await externalIdMappingService.lookupLocalId("sync_akeneo", "attachment", asset.externalId, scope);
|
|
1778
1753
|
if (existingAttachmentId) return existingAttachmentId;
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
binary = await client.downloadMediaFile(asset.remote.codeOrUrl);
|
|
1782
|
-
} catch (error) {
|
|
1783
|
-
if (!isAkeneoResponseTooLargeError(error)) throw error;
|
|
1784
|
-
await recordRejectedMedia(asset, "max_upload_size");
|
|
1785
|
-
return null;
|
|
1786
|
-
}
|
|
1754
|
+
await ensureDefaultPartitions(em);
|
|
1755
|
+
const binary = await client.downloadMediaFile(asset.remote.codeOrUrl);
|
|
1787
1756
|
const fileName = asset.remote.fileNameHint ?? binary.fileName ?? `akeneo-${randomUUID().slice(0, 8)}`;
|
|
1788
|
-
const
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1757
|
+
const partitionCode = resolveDefaultPartitionCode(asset.entityId);
|
|
1758
|
+
const stored = await storePartitionFile({
|
|
1759
|
+
partitionCode,
|
|
1760
|
+
orgId: scope.organizationId,
|
|
1761
|
+
tenantId: scope.tenantId,
|
|
1762
|
+
fileName,
|
|
1763
|
+
buffer: binary.buffer
|
|
1764
|
+
});
|
|
1765
|
+
const attachment = em.create(Attachment, {
|
|
1766
|
+
entityId: asset.entityId,
|
|
1767
|
+
recordId: asset.recordId,
|
|
1768
|
+
organizationId: scope.organizationId,
|
|
1769
|
+
tenantId: scope.tenantId,
|
|
1770
|
+
partitionCode,
|
|
1771
|
+
fileName,
|
|
1772
|
+
mimeType: asset.remote.mimeTypeHint ?? binary.contentType ?? "application/octet-stream",
|
|
1773
|
+
fileSize: binary.contentLength ?? binary.buffer.byteLength,
|
|
1774
|
+
storageDriver: "local",
|
|
1775
|
+
storagePath: stored.storagePath,
|
|
1776
|
+
storageMetadata: mergeAttachmentMetadata(null, {
|
|
1777
|
+
assignments: [{ type: asset.entityId, id: asset.recordId }]
|
|
1778
|
+
}),
|
|
1779
|
+
url: buildAttachmentFileUrl("pending"),
|
|
1780
|
+
content: null
|
|
1781
|
+
});
|
|
1782
|
+
em.persist(attachment);
|
|
1783
|
+
await em.flush();
|
|
1784
|
+
attachment.url = asset.kind === "image" ? buildAttachmentImageUrl(attachment.id, { slug: slugifyAttachmentFileName(fileName) }) : buildAttachmentFileUrl(attachment.id);
|
|
1814
1785
|
attachment.storageMetadata = {
|
|
1815
1786
|
...attachment.storageMetadata ?? {},
|
|
1816
1787
|
source: "akeneo",
|
|
@@ -1819,37 +1790,16 @@ async function createAkeneoImporter(client, scope) {
|
|
|
1819
1790
|
remoteCode: asset.remote.codeOrUrl
|
|
1820
1791
|
};
|
|
1821
1792
|
await em.flush();
|
|
1822
|
-
await emitAttachmentCrudChange("
|
|
1793
|
+
await emitAttachmentCrudChange("created", attachment);
|
|
1823
1794
|
await externalIdMappingService.storeExternalIdMapping("sync_akeneo", "attachment", attachment.id, asset.externalId, scope);
|
|
1824
1795
|
return attachment.id;
|
|
1825
1796
|
}
|
|
1826
|
-
async function releaseImportedAttachment(attachment) {
|
|
1827
|
-
const attachmentService = container.resolve("attachmentService");
|
|
1828
|
-
if (!attachmentService.releaseScoped) {
|
|
1829
|
-
throw new Error("[internal] attachmentService.releaseScoped is not available");
|
|
1830
|
-
}
|
|
1831
|
-
try {
|
|
1832
|
-
await attachmentService.releaseScoped({
|
|
1833
|
-
attachmentId: attachment.id,
|
|
1834
|
-
tenantId: scope.tenantId,
|
|
1835
|
-
organizationId: scope.organizationId,
|
|
1836
|
-
expectedOwner: { entityId: attachment.entityId, recordId: attachment.recordId },
|
|
1837
|
-
expectedAssignment: { type: attachment.entityId, id: attachment.recordId },
|
|
1838
|
-
expectedPartitionCode: attachment.partitionCode
|
|
1839
|
-
});
|
|
1840
|
-
return true;
|
|
1841
|
-
} catch (error) {
|
|
1842
|
-
if (isCrudHttpError(error) && error.status === 409) return false;
|
|
1843
|
-
throw error;
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
1797
|
async function reconcileAttachments(params) {
|
|
1847
1798
|
const desiredExternalIds = new Set(params.desiredAssets.map((asset) => asset.externalId));
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
}
|
|
1799
|
+
const desiredImages = params.desiredAssets.filter((asset) => asset.kind === "image");
|
|
1800
|
+
const desiredFiles = params.desiredAssets.filter((asset) => asset.kind === "file");
|
|
1801
|
+
const desiredAttachmentIds = await Promise.all(params.desiredAssets.map((asset) => upsertAsset(asset)));
|
|
1802
|
+
const heroAttachmentId = desiredImages.length > 0 ? desiredAttachmentIds[params.desiredAssets.findIndex((asset) => asset.externalId === desiredImages[0]?.externalId)] : null;
|
|
1853
1803
|
const heroAttachmentUrl = heroAttachmentId ? buildAttachmentImageUrl(heroAttachmentId) : null;
|
|
1854
1804
|
const existing = await findWithDecryption(em, Attachment, {
|
|
1855
1805
|
entityId: params.entityId,
|
|
@@ -1864,7 +1814,8 @@ async function createAkeneoImporter(client, scope) {
|
|
|
1864
1814
|
if (!externalId || !kind) continue;
|
|
1865
1815
|
const shouldDelete = kind === "image" ? params.reconciliation.deleteMissingMedia : params.reconciliation.deleteMissingAttachments;
|
|
1866
1816
|
if (!shouldDelete || desiredExternalIds.has(externalId)) continue;
|
|
1867
|
-
|
|
1817
|
+
await deletePartitionFile(attachment.partitionCode, attachment.storagePath, attachment.storageDriver);
|
|
1818
|
+
await em.remove(attachment).flush();
|
|
1868
1819
|
await emitAttachmentCrudChange("deleted", attachment);
|
|
1869
1820
|
}
|
|
1870
1821
|
return {
|