@hartl-services/medusa-payload-sync 0.1.0 → 0.2.1
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/.medusa/server/src/lib/payload-connection.js +53 -0
- package/.medusa/server/src/lib/payload-sync-guard.js +51 -0
- package/.medusa/server/src/modules/payload-sync/domain/errors.js +96 -2
- package/.medusa/server/src/modules/payload-sync/domain/options.js +28 -5
- package/.medusa/server/src/modules/payload-sync/domain/settings.js +44 -0
- package/.medusa/server/src/modules/payload-sync/loaders/register-payload-client.js +17 -12
- package/.medusa/server/src/modules/payload-sync/service.js +116 -55
- package/.medusa/server/src/modules/payload-sync/services/payload-rest-client.js +1 -1
- package/.medusa/server/src/plugin-actions.js +10 -0
- package/.medusa/server/src/plugin-features.js +49 -0
- package/.medusa/server/src/scripts/reconcile-payload-catalog.js +93 -13
- package/.medusa/server/src/subscribers/payload-sync-product-deleted.js +7 -4
- package/.medusa/server/src/subscribers/payload-sync-product-upserted.js +7 -4
- package/.medusa/server/src/subscribers/payload-sync-variant-deleted.js +7 -4
- package/.medusa/server/src/subscribers/payload-sync-variant-upserted.js +7 -4
- package/.medusa/server/src/workflows/payload-sync/authoritative-reconciliation.js +19 -14
- package/.medusa/server/src/workflows/payload-sync/external-step-failure.js +18 -1
- package/.medusa/server/src/workflows/payload-sync/reconcile-catalog.js +13 -10
- package/.medusa/server/src/workflows/payload-sync/reconcile-full-catalog.js +30 -0
- package/.medusa/server/src/workflows/payload-sync/sync-product.js +3 -11
- package/.medusa/server/src/workflows/payload-sync/sync-variant.js +4 -2
- package/README.md +197 -55
- package/package.json +7 -4
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reconcilePayloadCatalogWorkflow = void 0;
|
|
4
|
+
const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
|
|
5
|
+
const payload_sync_guard_1 = require("../../lib/payload-sync-guard");
|
|
6
|
+
const plugin_actions_1 = require("../../plugin-actions");
|
|
7
|
+
const reconcile_payload_catalog_1 = require("../../scripts/reconcile-payload-catalog");
|
|
8
|
+
const reconcilePayloadCatalogStep = (0, workflows_sdk_1.createStep)({
|
|
9
|
+
name: "payload-sync-reconcile-catalog-step",
|
|
10
|
+
// No step retry: it would rerun the whole catalog. The nested product and
|
|
11
|
+
// page workflows retry on their own.
|
|
12
|
+
maxRetries: 0,
|
|
13
|
+
noCompensation: true,
|
|
14
|
+
}, async (input, { container }) => {
|
|
15
|
+
// The script returns quietly while not configured; the action fails, so
|
|
16
|
+
// the Admin sees why nothing was synchronized.
|
|
17
|
+
await (0, payload_sync_guard_1.requirePayloadSyncConfigured)(container);
|
|
18
|
+
// Per-product failures are collected and then thrown by the shared
|
|
19
|
+
// function, so a partial failure ends the action as "failed".
|
|
20
|
+
return new workflows_sdk_1.StepResponse(await (0, reconcile_payload_catalog_1.reconcilePayloadCatalog)({ container, runId: input.run_id }));
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Whole-catalog reconciliation as a workflow, run by the base plugin for the
|
|
24
|
+
* Admin action `sync_now`. Same code path as the reconcile-payload-catalog
|
|
25
|
+
* script.
|
|
26
|
+
*/
|
|
27
|
+
exports.reconcilePayloadCatalogWorkflow = (0, workflows_sdk_1.createWorkflow)(plugin_actions_1.PAYLOAD_SYNC_RECONCILE_CATALOG_WORKFLOW_ID, function (input) {
|
|
28
|
+
return new workflows_sdk_1.WorkflowResponse(reconcilePayloadCatalogStep(input));
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVjb25jaWxlLWZ1bGwtY2F0YWxvZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3MvcGF5bG9hZC1zeW5jL3JlY29uY2lsZS1mdWxsLWNhdGFsb2cudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEscUVBSzJDO0FBRTNDLHFFQUE0RTtBQUU1RSx5REFBa0Y7QUFDbEYsdUZBQWtGO0FBYWxGLE1BQU0sMkJBQTJCLEdBQUcsSUFBQSwwQkFBVSxFQUM1QztJQUNFLElBQUksRUFBRSxxQ0FBcUM7SUFDM0MsMEVBQTBFO0lBQzFFLHFDQUFxQztJQUNyQyxVQUFVLEVBQUUsQ0FBQztJQUNiLGNBQWMsRUFBRSxJQUFJO0NBQ3JCLEVBQ0QsS0FBSyxFQUFFLEtBQTJDLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFO0lBQ25FLHdFQUF3RTtJQUN4RSwrQ0FBK0M7SUFDL0MsTUFBTSxJQUFBLGlEQUE0QixFQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQzlDLG1FQUFtRTtJQUNuRSw4REFBOEQ7SUFDOUQsT0FBTyxJQUFJLDRCQUFZLENBQ3JCLE1BQU0sSUFBQSxtREFBdUIsRUFBQyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQ2xFLENBQUM7QUFDSixDQUFDLENBQ0YsQ0FBQztBQUVGOzs7O0dBSUc7QUFDVSxRQUFBLCtCQUErQixHQUFHLElBQUEsOEJBQWMsRUFJM0QsMkRBQTBDLEVBQUUsVUFBVSxLQUFLO0lBQzNELE9BQU8sSUFBSSxnQ0FBZ0IsQ0FBQywyQkFBMkIsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO0FBQ2xFLENBQUMsQ0FBQyxDQUFDIn0=
|
|
@@ -4,8 +4,8 @@ exports.syncProductToPayloadWorkflow = void 0;
|
|
|
4
4
|
const core_flows_1 = require("@medusajs/medusa/core-flows");
|
|
5
5
|
const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
|
|
6
6
|
const utils_1 = require("@medusajs/framework/utils");
|
|
7
|
-
const errors_1 = require("../../modules/payload-sync/domain/errors");
|
|
8
7
|
const authoritative_reconciliation_1 = require("./authoritative-reconciliation");
|
|
8
|
+
const external_step_failure_1 = require("./external-step-failure");
|
|
9
9
|
const syncProductMirrorStep = (0, workflows_sdk_1.createStep)({
|
|
10
10
|
name: "payload-sync-sync-product-mirror",
|
|
11
11
|
maxRetries: 1,
|
|
@@ -20,7 +20,7 @@ const syncProductMirrorStep = (0, workflows_sdk_1.createStep)({
|
|
|
20
20
|
}));
|
|
21
21
|
}
|
|
22
22
|
catch (error) {
|
|
23
|
-
|
|
23
|
+
(0, external_step_failure_1.handlePayloadSyncExternalStepFailure)(error, logger, {
|
|
24
24
|
operation: "sync-product",
|
|
25
25
|
entityType: "product",
|
|
26
26
|
medusaId: input.snapshot.id,
|
|
@@ -30,11 +30,6 @@ const syncProductMirrorStep = (0, workflows_sdk_1.createStep)({
|
|
|
30
30
|
attempt,
|
|
31
31
|
idempotencyKey,
|
|
32
32
|
});
|
|
33
|
-
logger.error(safeLog);
|
|
34
|
-
if (isPermanentPayloadSyncError(error)) {
|
|
35
|
-
return workflows_sdk_1.StepResponse.permanentFailure(safeLog);
|
|
36
|
-
}
|
|
37
|
-
throw error;
|
|
38
33
|
}
|
|
39
34
|
});
|
|
40
35
|
exports.syncProductToPayloadWorkflow = (0, workflows_sdk_1.createWorkflow)("payload-sync-sync-product", function (input) {
|
|
@@ -72,7 +67,4 @@ exports.syncProductToPayloadWorkflow = (0, workflows_sdk_1.createWorkflow)("payl
|
|
|
72
67
|
});
|
|
73
68
|
return new workflows_sdk_1.WorkflowResponse(result);
|
|
74
69
|
});
|
|
75
|
-
|
|
76
|
-
return error instanceof errors_1.PayloadSyncError && !error.retryable;
|
|
77
|
-
}
|
|
78
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy1wcm9kdWN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3dvcmtmbG93cy9wYXlsb2FkLXN5bmMvc3luYy1wcm9kdWN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDREQUFnRTtBQUNoRSxxRUFNMkM7QUFDM0MscURBQXNFO0FBTXRFLHFFQUdrRDtBQUNsRCxpRkFBK0U7QUF1Qi9FLE1BQU0scUJBQXFCLEdBQUcsSUFBQSwwQkFBVSxFQUN0QztJQUNFLElBQUksRUFBRSxrQ0FBa0M7SUFDeEMsVUFBVSxFQUFFLENBQUM7SUFDYixhQUFhLEVBQUUsQ0FBQztJQUNoQixjQUFjLEVBQUUsSUFBSTtDQUNyQixFQUNELEtBQUssRUFDSCxLQUFpQyxFQUNqQyxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxFQUNsRCxFQUFFO0lBQ0YsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUVuRSxJQUFJLENBQUM7UUFDSCxPQUFPLElBQUksNEJBQVksQ0FDckIsTUFBTSxJQUFBLDREQUE2QixFQUFDLFNBQVMsRUFBRTtZQUM3QyxRQUFRLEVBQUUsS0FBSyxDQUFDLFFBQVEsQ0FBQyxFQUFFO1lBQzNCLGdCQUFnQixFQUFFLEtBQUssQ0FBQyxRQUFRO1NBQ2pDLENBQUMsQ0FDSCxDQUFDO0lBQ0osQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixNQUFNLE9BQU8sR0FBRyxJQUFBLDZCQUFvQixFQUFDLEtBQUssRUFBRTtZQUMxQyxTQUFTLEVBQUUsY0FBYztZQUN6QixVQUFVLEVBQUUsU0FBUztZQUNyQixRQUFRLEVBQUUsS0FBSyxDQUFDLFFBQVEsQ0FBQyxFQUFFO1lBQzNCLFNBQVMsRUFBRSxLQUFLLENBQUMsU0FBUztZQUMxQixZQUFZLEVBQUUsMkJBQTJCO1lBQ3pDLFVBQVU7WUFDVixPQUFPO1lBQ1AsY0FBYztTQUNmLENBQUMsQ0FBQztRQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFdEIsSUFBSSwyQkFBMkIsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ3ZDLE9BQU8sNEJBQVksQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNoRCxDQUFDO1FBRUQsTUFBTSxLQUFLLENBQUM7SUFDZCxDQUFDO0FBQ0gsQ0FBQyxDQUNGLENBQUM7QUFFVyxRQUFBLDRCQUE0QixHQUFHLElBQUEsOEJBQWMsRUFJeEQsMkJBQTJCLEVBQUUsVUFBVSxLQUFLO0lBQzVDLE1BQU0sRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEdBQUcsSUFBQSw4QkFBaUIsRUFBQztRQUMzQyxNQUFNLEVBQUUsU0FBUztRQUNqQixNQUFNLEVBQUU7WUFDTixJQUFJO1lBQ0osT0FBTztZQUNQLFFBQVE7WUFDUixRQUFRO1lBQ1IsYUFBYTtZQUNiLGdCQUFnQjtTQUNqQjtRQUNELE9BQU8sRUFBRSxFQUFFLEVBQUUsRUFBRSxLQUFLLENBQUMsRUFBRSxFQUFFO0tBQzFCLENBQUMsQ0FBQyxNQUFNLENBQUMsRUFBRSxJQUFJLEVBQUUsc0NBQXNDLEVBQUUsQ0FBQyxDQUFDO0lBRTVELE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQVMsRUFPeEIsRUFBRSxTQUFTLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRSxRQUFRLEVBQUUsRUFDakMsQ0FBQyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsRUFBeUIsRUFBRTtRQUNqRCxNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFNUIsSUFBSSxPQUFPLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDMUIsT0FBTyw0QkFBWSxDQUFDLGdCQUFnQixDQUNsQywwQ0FBMEMsU0FBUyxHQUFHLENBQ3ZELENBQUM7UUFDSixDQUFDO1FBRUQsT0FBTztZQUNMLEVBQUUsRUFBRSxPQUFPLENBQUMsRUFBRTtZQUNkLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSztZQUNwQixNQUFNLEVBQUUsT0FBTyxDQUFDLE1BQU07WUFDdEIsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNO1lBQ3RCLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQztnQkFDM0MsRUFBRSxFQUFFLE9BQU8sQ0FBQyxFQUFFO2dCQUNkLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSzthQUNyQixDQUFDLENBQUM7U0FDSixDQUFDO0lBQ0osQ0FBQyxDQUNGLENBQUM7SUFFRixNQUFNLE1BQU0sR0FBRyxxQkFBcUIsQ0FBQztRQUNuQyxRQUFRO1FBQ1IsU0FBUyxFQUFFLEtBQUssQ0FBQyxTQUFTO0tBQzNCLENBQUMsQ0FBQztJQUVILE9BQU8sSUFBSSxnQ0FBZ0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUN0QyxDQUFDLENBQUMsQ0FBQztBQUVILFNBQVMsMkJBQTJCLENBQUMsS0FBYztJQUNqRCxPQUFPLEtBQUssWUFBWSx5QkFBZ0IsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUM7QUFDL0QsQ0FBQyJ9
|
|
70
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy1wcm9kdWN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3dvcmtmbG93cy9wYXlsb2FkLXN5bmMvc3luYy1wcm9kdWN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDREQUFnRTtBQUNoRSxxRUFNMkM7QUFDM0MscURBQXNFO0FBTXRFLGlGQUErRTtBQUMvRSxtRUFBK0U7QUF1Qi9FLE1BQU0scUJBQXFCLEdBQUcsSUFBQSwwQkFBVSxFQUN0QztJQUNFLElBQUksRUFBRSxrQ0FBa0M7SUFDeEMsVUFBVSxFQUFFLENBQUM7SUFDYixhQUFhLEVBQUUsQ0FBQztJQUNoQixjQUFjLEVBQUUsSUFBSTtDQUNyQixFQUNELEtBQUssRUFDSCxLQUFpQyxFQUNqQyxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxFQUNsRCxFQUFFO0lBQ0YsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUVuRSxJQUFJLENBQUM7UUFDSCxPQUFPLElBQUksNEJBQVksQ0FDckIsTUFBTSxJQUFBLDREQUE2QixFQUFDLFNBQVMsRUFBRTtZQUM3QyxRQUFRLEVBQUUsS0FBSyxDQUFDLFFBQVEsQ0FBQyxFQUFFO1lBQzNCLGdCQUFnQixFQUFFLEtBQUssQ0FBQyxRQUFRO1NBQ2pDLENBQUMsQ0FDSCxDQUFDO0lBQ0osQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixJQUFBLDREQUFvQyxFQUFDLEtBQUssRUFBRSxNQUFNLEVBQUU7WUFDbEQsU0FBUyxFQUFFLGNBQWM7WUFDekIsVUFBVSxFQUFFLFNBQVM7WUFDckIsUUFBUSxFQUFFLEtBQUssQ0FBQyxRQUFRLENBQUMsRUFBRTtZQUMzQixTQUFTLEVBQUUsS0FBSyxDQUFDLFNBQVM7WUFDMUIsWUFBWSxFQUFFLDJCQUEyQjtZQUN6QyxVQUFVO1lBQ1YsT0FBTztZQUNQLGNBQWM7U0FDZixDQUFDLENBQUM7SUFDTCxDQUFDO0FBQ0gsQ0FBQyxDQUNGLENBQUM7QUFFVyxRQUFBLDRCQUE0QixHQUFHLElBQUEsOEJBQWMsRUFJeEQsMkJBQTJCLEVBQUUsVUFBVSxLQUFLO0lBQzVDLE1BQU0sRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEdBQUcsSUFBQSw4QkFBaUIsRUFBQztRQUMzQyxNQUFNLEVBQUUsU0FBUztRQUNqQixNQUFNLEVBQUU7WUFDTixJQUFJO1lBQ0osT0FBTztZQUNQLFFBQVE7WUFDUixRQUFRO1lBQ1IsYUFBYTtZQUNiLGdCQUFnQjtTQUNqQjtRQUNELE9BQU8sRUFBRSxFQUFFLEVBQUUsRUFBRSxLQUFLLENBQUMsRUFBRSxFQUFFO0tBQzFCLENBQUMsQ0FBQyxNQUFNLENBQUMsRUFBRSxJQUFJLEVBQUUsc0NBQXNDLEVBQUUsQ0FBQyxDQUFDO0lBRTVELE1BQU0sUUFBUSxHQUFHLElBQUEseUJBQVMsRUFPeEIsRUFBRSxTQUFTLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRSxRQUFRLEVBQUUsRUFDakMsQ0FBQyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsRUFBeUIsRUFBRTtRQUNqRCxNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFNUIsSUFBSSxPQUFPLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDMUIsT0FBTyw0QkFBWSxDQUFDLGdCQUFnQixDQUNsQywwQ0FBMEMsU0FBUyxHQUFHLENBQ3ZELENBQUM7UUFDSixDQUFDO1FBRUQsT0FBTztZQUNMLEVBQUUsRUFBRSxPQUFPLENBQUMsRUFBRTtZQUNkLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSztZQUNwQixNQUFNLEVBQUUsT0FBTyxDQUFDLE1BQU07WUFDdEIsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNO1lBQ3RCLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQztnQkFDM0MsRUFBRSxFQUFFLE9BQU8sQ0FBQyxFQUFFO2dCQUNkLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSzthQUNyQixDQUFDLENBQUM7U0FDSixDQUFDO0lBQ0osQ0FBQyxDQUNGLENBQUM7SUFFRixNQUFNLE1BQU0sR0FBRyxxQkFBcUIsQ0FBQztRQUNuQyxRQUFRO1FBQ1IsU0FBUyxFQUFFLEtBQUssQ0FBQyxTQUFTO0tBQzNCLENBQUMsQ0FBQztJQUVILE9BQU8sSUFBSSxnQ0FBZ0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUN0QyxDQUFDLENBQUMsQ0FBQyJ9
|
|
@@ -5,6 +5,7 @@ const core_flows_1 = require("@medusajs/medusa/core-flows");
|
|
|
5
5
|
const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
|
|
6
6
|
const utils_1 = require("@medusajs/framework/utils");
|
|
7
7
|
const errors_1 = require("../../modules/payload-sync/domain/errors");
|
|
8
|
+
const payload_connection_1 = require("../../lib/payload-connection");
|
|
8
9
|
const payload_sync_1 = require("../../modules/payload-sync");
|
|
9
10
|
const authoritative_reconciliation_1 = require("./authoritative-reconciliation");
|
|
10
11
|
const sync_product_1 = require("./sync-product");
|
|
@@ -16,7 +17,8 @@ const findParentMirrorStep = (0, workflows_sdk_1.createStep)({
|
|
|
16
17
|
const service = container.resolve(payload_sync_1.PAYLOAD_SYNC_MODULE);
|
|
17
18
|
const logger = container.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
|
|
18
19
|
try {
|
|
19
|
-
|
|
20
|
+
const connection = await (0, payload_connection_1.resolvePayloadConnection)(container);
|
|
21
|
+
return new workflows_sdk_1.StepResponse(await service.findProductMirror(connection, input.productMedusaId));
|
|
20
22
|
}
|
|
21
23
|
catch (error) {
|
|
22
24
|
const safeLog = (0, errors_1.formatPayloadSyncLog)(error, {
|
|
@@ -126,4 +128,4 @@ exports.syncVariantToPayloadWorkflow = (0, workflows_sdk_1.createWorkflow)("payl
|
|
|
126
128
|
function isPermanentPayloadSyncError(error) {
|
|
127
129
|
return error instanceof errors_1.PayloadSyncError && !error.retryable;
|
|
128
130
|
}
|
|
129
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
131
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy12YXJpYW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3dvcmtmbG93cy9wYXlsb2FkLXN5bmMvc3luYy12YXJpYW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDREQUFnRTtBQUNoRSxxRUFPMkM7QUFDM0MscURBQXNFO0FBT3RFLHFFQUdrRDtBQUNsRCxxRUFBd0U7QUFDeEUsNkRBQWlFO0FBRWpFLGlGQUErRTtBQUMvRSxpREFBOEQ7QUFtQzlELE1BQU0sb0JBQW9CLEdBQUcsSUFBQSwwQkFBVSxFQUNyQztJQUNFLElBQUksRUFBRSxpQ0FBaUM7SUFDdkMsVUFBVSxFQUFFLENBQUM7SUFDYixhQUFhLEVBQUUsQ0FBQztDQUNqQixFQUNELEtBQUssRUFDSCxLQUFnQyxFQUNoQyxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxFQUNsRCxFQUFFO0lBQ0YsTUFBTSxPQUFPLEdBQ1gsU0FBUyxDQUFDLE9BQU8sQ0FBMkIsa0NBQW1CLENBQUMsQ0FBQztJQUNuRSxNQUFNLE1BQU0sR0FBRyxTQUFTLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRW5FLElBQUksQ0FBQztRQUNILE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBQSw2Q0FBd0IsRUFBQyxTQUFTLENBQUMsQ0FBQztRQUU3RCxPQUFPLElBQUksNEJBQVksQ0FDckIsTUFBTSxPQUFPLENBQUMsaUJBQWlCLENBQUMsVUFBVSxFQUFFLEtBQUssQ0FBQyxlQUFlLENBQUMsQ0FDbkUsQ0FBQztJQUNKLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsTUFBTSxPQUFPLEdBQUcsSUFBQSw2QkFBb0IsRUFBQyxLQUFLLEVBQUU7WUFDMUMsU0FBUyxFQUFFLHFCQUFxQjtZQUNoQyxVQUFVLEVBQUUsU0FBUztZQUNyQixRQUFRLEVBQUUsS0FBSyxDQUFDLGVBQWU7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
|
package/README.md
CHANGED
|
@@ -22,40 +22,180 @@ remain pinned to `2.19.0`.
|
|
|
22
22
|
## Installation in a shop
|
|
23
23
|
|
|
24
24
|
```sh
|
|
25
|
-
pnpm add @hartl-services/medusa-payload-sync
|
|
25
|
+
pnpm add @hartl-services/medusa-payload-sync @hartl-services/medusa-base
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
`@hartl-services/medusa-base` (>= 1.0.0) is a required peer: it stores the
|
|
29
|
+
plugin switch (see [Feature toggles](#feature-toggles)), the connection
|
|
30
|
+
settings (see [Settings in the Admin](#settings-in-the-admin)) and runs the
|
|
31
|
+
[`sync_now` action](#admin-action-jetzt-synchronisieren). Register it as a
|
|
32
|
+
plugin as well, before Payload Sync.
|
|
33
|
+
|
|
28
34
|
```ts
|
|
29
35
|
// medusa-config.ts
|
|
30
36
|
const plugins = [
|
|
37
|
+
{ resolve: "@hartl-services/medusa-base", options: {} },
|
|
31
38
|
{
|
|
32
39
|
resolve: "@hartl-services/medusa-payload-sync",
|
|
33
|
-
options:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// optional, defaults to "users"
|
|
37
|
-
payloadAuthCollection: process.env.PAYLOAD_AUTH_COLLECTION,
|
|
38
|
-
},
|
|
40
|
+
// No connection options: URL, API key and auth collection are Admin
|
|
41
|
+
// settings. Only the config-only options below may be passed here.
|
|
42
|
+
options: {},
|
|
39
43
|
},
|
|
40
44
|
// ...other plugins
|
|
41
45
|
];
|
|
42
46
|
```
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
`
|
|
52
|
-
|
|
53
|
-
[Options and exact defaults](#options-and-exact-defaults) below. Keep
|
|
54
|
-
`payloadApiKey` out of any public/browser environment-variable prefix and
|
|
55
|
-
never forward it to a Storefront or Admin client. Also complete
|
|
48
|
+
No option is required. The Payload URL, API key and auth collection are
|
|
49
|
+
runtime settings maintained only in the Admin (see
|
|
50
|
+
[Settings in the Admin](#settings-in-the-admin)); there is no
|
|
51
|
+
`medusa-config.ts` or environment fallback. The former options `payloadUrl`,
|
|
52
|
+
`payloadApiKey` and `payloadAuthCollection` are rejected at boot with an
|
|
53
|
+
error that points to the Admin settings. The config-only options
|
|
54
|
+
(`collections.product`, `collections.variant`, `requestTimeoutMs`,
|
|
55
|
+
`backfillBatchSize`, `backfillBatchDelayMs`) are described in
|
|
56
|
+
[Options and exact defaults](#options-and-exact-defaults) below. Also complete
|
|
56
57
|
[Configure Payload authentication](#configure-payload-authentication) below,
|
|
57
58
|
which the plugin itself does not automate.
|
|
58
59
|
|
|
60
|
+
## Settings in the Admin
|
|
61
|
+
|
|
62
|
+
The plugin declares three settings to `@hartl-services/medusa-base`. An Admin
|
|
63
|
+
maintains them under Settings → "Hartl Services Plugins" → Payload-CMS-Sync
|
|
64
|
+
("Öffnen"):
|
|
65
|
+
|
|
66
|
+
| Key | Label | Type | Required | Default |
|
|
67
|
+
| ------------------------- | --------------- | ------ | -------- | ------- |
|
|
68
|
+
| `payload_url` | Payload-URL | url | yes | none |
|
|
69
|
+
| `payload_api_key` | API-Key | secret | yes | none |
|
|
70
|
+
| `payload_auth_collection` | Auth-Collection | string | no | `users` |
|
|
71
|
+
|
|
72
|
+
Precedence per key: value stored in the Admin > default. The page shows each
|
|
73
|
+
value's source; the API key is stored encrypted by the base plugin and never
|
|
74
|
+
displayed ("gesetzt" only). "Löschen" removes a stored value: the URL or API
|
|
75
|
+
key is then missing (the plugin is "nicht konfiguriert"), the auth collection
|
|
76
|
+
returns to `users`. Saving a changed URL or auth collection also clears the
|
|
77
|
+
stored API key (the settings declare `invalidates: ["payload_api_key"]`), so
|
|
78
|
+
the key is never sent to a host it was not entered for; the plugin is "nicht
|
|
79
|
+
konfiguriert" until the key is entered again. Rotating the backend's `COOKIE_SECRET` makes a stored API key unreadable; it
|
|
80
|
+
then counts as not set until it is saved again (see the base plugin's README).
|
|
81
|
+
|
|
82
|
+
Every sync resolves the settings anew, so a change applies to the next event
|
|
83
|
+
without a restart. The settings are read at app level, never by the module
|
|
84
|
+
service (a module does not reach into other modules):
|
|
85
|
+
|
|
86
|
+
- `resolvePayloadConnection(container)` (`src/lib/payload-connection.ts`)
|
|
87
|
+
reads them through the base plugin with the caller's application container
|
|
88
|
+
(workflow step, subscriber, script), validates them and returns the
|
|
89
|
+
connection; while incomplete it throws the permanent
|
|
90
|
+
`PayloadSyncNotConfiguredError`.
|
|
91
|
+
- `describePayloadSyncConfiguration(container)` answers the same check
|
|
92
|
+
without throwing (subscriber guard, reconciliation script, `sync_now`).
|
|
93
|
+
- Every Payload-facing method of the module service takes that connection
|
|
94
|
+
as its first parameter, for example
|
|
95
|
+
`service.syncProductSnapshot(connection, snapshot)` or
|
|
96
|
+
`service.findProductMirror(connection, medusaId)`. The service builds its
|
|
97
|
+
REST client from it lazily and reuses it while URL, key and auth collection
|
|
98
|
+
stay the same. An injected `payloadSyncRestClient` (test seam) is used
|
|
99
|
+
as-is and the connection is ignored.
|
|
100
|
+
Resolved values are validated like the former options (`http`/`https` URL
|
|
101
|
+
with one trailing slash removed, non-empty key, lowercase auth-collection
|
|
102
|
+
slug); an invalid value counts as missing. The collections, timeout and
|
|
103
|
+
backfill options stay config-only.
|
|
104
|
+
|
|
105
|
+
### Not configured
|
|
106
|
+
|
|
107
|
+
While URL or API key is missing or invalid, the plugin is "nicht
|
|
108
|
+
konfiguriert". That is also the state of every fresh installation until the
|
|
109
|
+
settings are saved:
|
|
110
|
+
|
|
111
|
+
- the backend boots normally and does not contact Payload at startup;
|
|
112
|
+
- the settings page and the plugin hub show "nicht konfiguriert" with the
|
|
113
|
+
missing keys;
|
|
114
|
+
- the subscribers check the plugin switch first, then the settings, and
|
|
115
|
+
return without mirroring; product and variant changes made meanwhile are
|
|
116
|
+
not caught up automatically (run the reconciliation afterwards);
|
|
117
|
+
- the reconciliation script returns an empty summary without touching
|
|
118
|
+
Payload; the `sync_now` action ends `failed` with the not-configured
|
|
119
|
+
message;
|
|
120
|
+
- one warning per process names the missing or invalid keys (never values),
|
|
121
|
+
for example `Payload-CMS-Sync ist nicht konfiguriert (fehlend:
|
|
122
|
+
payload_url).`; after a successful resolution the warning is re-armed for
|
|
123
|
+
the next outage.
|
|
124
|
+
|
|
125
|
+
### Migrating an existing shop
|
|
126
|
+
|
|
127
|
+
Earlier versions read the connection from `medusa-config.ts`
|
|
128
|
+
(`PAYLOAD_URL`, `PAYLOAD_SERVICE_API_KEY`, `PAYLOAD_AUTH_COLLECTION`). The
|
|
129
|
+
settings only exist from this version on, so upgrade in this order:
|
|
130
|
+
|
|
131
|
+
1. Remove the three options from the plugin entry (the boot fails while they
|
|
132
|
+
are still passed) and drop the environment variables.
|
|
133
|
+
2. Deploy. The plugin reports "nicht konfiguriert"; product and variant
|
|
134
|
+
events in the meantime are not mirrored.
|
|
135
|
+
3. Enter URL, API key and auth collection in the Admin (Settings → "Hartl
|
|
136
|
+
Services Plugins" → Payload-CMS-Sync).
|
|
137
|
+
4. Run "Jetzt synchronisieren" to catch up on the changes missed meanwhile.
|
|
138
|
+
|
|
139
|
+
Unlike the Documents module of `@hartl-services/medusa-base`, which
|
|
140
|
+
deliberately keeps its `merchant` option as a config fallback for the shop
|
|
141
|
+
master data, this plugin has no config or environment fallback for its
|
|
142
|
+
connection settings.
|
|
143
|
+
|
|
144
|
+
### Admin action "Jetzt synchronisieren"
|
|
145
|
+
|
|
146
|
+
The descriptor declares the action `sync_now` ("Jetzt synchronisieren", mode
|
|
147
|
+
`background`, with a confirmation prompt). It runs the workflow
|
|
148
|
+
`payload-sync-reconcile-catalog`, which calls the same full
|
|
149
|
+
[reconciliation](#initial-and-repeat-reconciliation) as the script. The base
|
|
150
|
+
answers `202` right after starting it and emits `plugin-action.finished.v1`
|
|
151
|
+
with `completed` or `failed` when it ends; any failed product or a failed
|
|
152
|
+
orphan scan makes the run `failed`. The base records every run and refuses a
|
|
153
|
+
second start while one is running (`409`).
|
|
154
|
+
|
|
155
|
+
A failed run's message names the counts and the classified cause, redacted,
|
|
156
|
+
e.g. `Payload-Abgleich fehlgeschlagen: 4 von 4 Produkt(en) nicht gespiegelt
|
|
157
|
+
(Payload nicht erreichbar, Netzwerkfehler [PAYLOAD_NETWORK_ERROR]); verwaiste
|
|
158
|
+
Spiegel nicht geprüft (…)`. A retryable error (network, timeout, HTTP
|
|
159
|
+
408/429/5xx) makes Medusa schedule the step's retry (`retryInterval`) instead
|
|
160
|
+
of retrying inline; the reconciliation does not wait for it and counts that
|
|
161
|
+
product or orphan page as failed right away. Rerun once Payload is back.
|
|
162
|
+
|
|
163
|
+
The workflow reports its progress to that run through the base's
|
|
164
|
+
`reportPluginActionProgress` after every page, and the Admin shows it while
|
|
165
|
+
the run is active:
|
|
166
|
+
|
|
167
|
+
- product pages: `current` = products scanned, `total` = products in Medusa,
|
|
168
|
+
label e.g. `Produkte: Seite 3, 140 gespiegelt`;
|
|
169
|
+
- orphan scans: `Verwaiste Produkt-Spiegel: Seite 1`,
|
|
170
|
+
`Verwaiste Varianten-Spiegel: Seite 1`;
|
|
171
|
+
- at the end: `Fertig: 140 Produkte und 312 Varianten gespiegelt, 2 verwaiste
|
|
172
|
+
Spiegel auf Entwurf gesetzt` (plus `, N fehlgeschlagen` for failed
|
|
173
|
+
products).
|
|
174
|
+
|
|
175
|
+
The reconciliation script reports nothing (it has no run). While the plugin
|
|
176
|
+
is "nicht konfiguriert" the action ends `failed` with the not-configured
|
|
177
|
+
message (for example `Payload-CMS-Sync ist nicht konfiguriert (fehlend:
|
|
178
|
+
payload_url).`) and no progress.
|
|
179
|
+
|
|
180
|
+
## Feature toggles
|
|
181
|
+
|
|
182
|
+
The plugin declares a plugin switch without features to
|
|
183
|
+
`@hartl-services/medusa-base` under the plugin key `payload_sync`. An Admin
|
|
184
|
+
switches it under Settings → "Hartl Services Plugins"; its effective value is
|
|
185
|
+
readable from `GET /store/plugin-features/payload_sync`. The plugin has no
|
|
186
|
+
HTTP routes.
|
|
187
|
+
|
|
188
|
+
| Key | Label | Default | Gates | Keeps running when off |
|
|
189
|
+
| --------------- | ---------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
190
|
+
| (plugin switch) | Payload-CMS-Sync | on | subscribers `payload-sync-product-upserted`, `payload-sync-product-deleted`, `payload-sync-variant-upserted`, `payload-sync-variant-deleted` | the module, existing Payload mirror documents (unchanged), the manual reconciliation script and the `sync_now` action |
|
|
191
|
+
|
|
192
|
+
While the switch is off, product and variant events are consumed without
|
|
193
|
+
effect: nothing is mirrored and nothing is caught up when the switch is
|
|
194
|
+
turned on again. Run the [reconciliation](#initial-and-repeat-reconciliation)
|
|
195
|
+
afterwards to bring Payload back in line; neither the script nor the
|
|
196
|
+
`sync_now` action reads the switch. A toggle or settings read failure fails
|
|
197
|
+
the subscriber, so the Event Bus retries it.
|
|
198
|
+
|
|
59
199
|
## Configure Medusa
|
|
60
200
|
|
|
61
201
|
This repository's demo/test host, `apps/backend`, configures Payload
|
|
@@ -64,16 +204,13 @@ following the same direct `medusa-config.ts` entry shown above; it does not
|
|
|
64
204
|
ship a Payload runtime package.
|
|
65
205
|
|
|
66
206
|
Feature selection and non-secret defaults belong directly in the plugin's
|
|
67
|
-
options in `apps/backend/medusa-config.ts`. It is
|
|
68
|
-
there
|
|
69
|
-
|
|
70
|
-
`PAYLOAD_AUTH_COLLECTION`.
|
|
207
|
+
options in `apps/backend/medusa-config.ts`. It is installed by being listed
|
|
208
|
+
there; the Admin plugin switch (see [Feature toggles](#feature-toggles)) only
|
|
209
|
+
pauses the mirroring.
|
|
71
210
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
the local plugin. Never use a public/browser environment-variable prefix for
|
|
76
|
-
the API key or forward it to a Storefront or Admin client.
|
|
211
|
+
The demo shop passes no Payload connection: it boots with the plugin "nicht
|
|
212
|
+
konfiguriert" until URL and API key are saved in the Admin. Never forward
|
|
213
|
+
the API key to a Storefront or Admin client.
|
|
77
214
|
|
|
78
215
|
The backend configuration owns these server-only options directly; Payload
|
|
79
216
|
Sync exports no consumer-facing option or HTTP contract.
|
|
@@ -82,19 +219,18 @@ Sync exports no consumer-facing option or HTTP contract.
|
|
|
82
219
|
|
|
83
220
|
| Option | Required | Implemented default | Purpose |
|
|
84
221
|
| ----------------------- | -------- | ------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
85
|
-
| `payloadUrl` | yes | none | Payload origin using `http` or `https`; one trailing slash is removed. The client adds `/api`. |
|
|
86
|
-
| `payloadApiKey` | yes | none | API key of the dedicated Payload service user. |
|
|
87
|
-
| `payloadAuthCollection` | no | `users` | Payload auth collection slug used in the API-key Authorization header. |
|
|
88
222
|
| `collections.product` | no | `medusa-products` | Product mirror collection slug. |
|
|
89
223
|
| `collections.variant` | no | `medusa-product-variants` | Variant mirror collection slug. |
|
|
90
224
|
| `requestTimeoutMs` | no | `10000` | Timeout for one Payload REST request; accepted range `100`–`120000`. |
|
|
91
225
|
| `backfillBatchSize` | no | `50` | Medusa/Payload page size for reconciliation; accepted range `1`–`500`. |
|
|
92
226
|
| `backfillBatchDelayMs` | no | `250` | Delay between non-final Medusa product pages; accepted range `0`–`60000`. |
|
|
93
227
|
|
|
94
|
-
Collection
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
228
|
+
Collection slugs must be lowercase Payload-style slugs. Invalid options and
|
|
229
|
+
the removed connection options fail the boot. The connection settings are
|
|
230
|
+
validated when they are used (an invalid one makes the plugin "nicht
|
|
231
|
+
konfiguriert"). `apps/backend/medusa-config.ts` passes no options; changes to
|
|
232
|
+
non-secret shop defaults must be modeled directly in the plugin's options
|
|
233
|
+
there.
|
|
98
234
|
|
|
99
235
|
The REST client rejects any successful JSON response larger than 8 MiB with
|
|
100
236
|
`PAYLOAD_RESPONSE_TOO_LARGE` and cancels the response stream. This fixed bound
|
|
@@ -134,11 +270,11 @@ After restarting Payload with `useAPIKey: true`:
|
|
|
134
270
|
Keep the resulting values server-side:
|
|
135
271
|
|
|
136
272
|
- its document ID as `PAYLOAD_SYNC_SERVICE_USER_ID` in the Payload process;
|
|
137
|
-
- its API key
|
|
273
|
+
- its API key in the Admin setting "API-Key" (`payload_api_key`).
|
|
138
274
|
|
|
139
|
-
Set
|
|
140
|
-
sends the standard header
|
|
141
|
-
`Authorization: <
|
|
275
|
+
Set the Admin setting "Auth-Collection" (`payload_auth_collection`) to the
|
|
276
|
+
auth collection's actual slug. The plugin sends the standard header
|
|
277
|
+
`Authorization: <auth collection> API-Key <API key>` on every
|
|
142
278
|
Payload request. It never logs this header or the key. Do not put either secret
|
|
143
279
|
in a public environment variable, client bundle, URL, or checked-in `.env`
|
|
144
280
|
file.
|
|
@@ -384,29 +520,29 @@ not satisfy this plugin's process-death recovery contract. Reconciliation must
|
|
|
384
520
|
use the same compatible provider and lock domain as event workers. Otherwise
|
|
385
521
|
the ordering guarantee is only process-local or does not apply.
|
|
386
522
|
|
|
387
|
-
##
|
|
523
|
+
## Capability check
|
|
524
|
+
|
|
525
|
+
The plugin does not contact Payload at startup: the connection is a runtime
|
|
526
|
+
setting, so there is nothing to check at boot. A wrong URL, key or missing
|
|
527
|
+
permission surfaces on the first sync (structured, redacted error log) or as
|
|
528
|
+
a failed `sync_now` run.
|
|
388
529
|
|
|
389
|
-
|
|
390
|
-
`/api/access` endpoint
|
|
530
|
+
`runPayloadPreflight` (used by the Payload REST contract suite against a real
|
|
531
|
+
Payload) calls Payload's standard authenticated `/api/access` endpoint and
|
|
532
|
+
validates:
|
|
391
533
|
|
|
392
534
|
- reachable authentication;
|
|
393
535
|
- both configured collection slugs;
|
|
394
536
|
- collection-level `read`, `create`, and `update` capabilities;
|
|
395
537
|
- required field capabilities only when Payload exposes those field shapes.
|
|
396
538
|
|
|
397
|
-
This is intentionally a reduced
|
|
539
|
+
This is intentionally a reduced check. With no custom Payload code, the
|
|
398
540
|
standard endpoint cannot prove every field type, `unique`/index rule,
|
|
399
541
|
relationship `relationTo`, drafts/version setting, delete ACL, or omitted field
|
|
400
542
|
shape. The copyable schema above is the authoritative configuration contract;
|
|
401
543
|
runtime writes and the real Payload contract test are the authoritative
|
|
402
544
|
protocol gate.
|
|
403
545
|
|
|
404
|
-
Recognizable auth, schema, permission, and other non-retryable contract errors
|
|
405
|
-
fail startup with the affected collection or capability. Temporary network
|
|
406
|
-
failures, timeouts, HTTP 408, 429, and 5xx responses produce a structured,
|
|
407
|
-
redacted warning and allow startup in degraded mode; later workflows retry.
|
|
408
|
-
Remote preflight I/O is skipped in Medusa's data-loader-only/migration context.
|
|
409
|
-
|
|
410
546
|
## Initial and repeat reconciliation
|
|
411
547
|
|
|
412
548
|
Reconciliation is the initial backfill and the repair tool after missed or
|
|
@@ -421,8 +557,11 @@ pnpm exec medusa exec \
|
|
|
421
557
|
node_modules/@hartl-services/medusa-payload-sync/.medusa/server/src/scripts/reconcile-payload-catalog.js
|
|
422
558
|
```
|
|
423
559
|
|
|
424
|
-
The command uses the shop's configured database and
|
|
425
|
-
|
|
560
|
+
The command uses the shop's configured database and resolves the Payload
|
|
561
|
+
connection exactly like the subscribers (Admin settings > default). While the plugin is "nicht konfiguriert" it logs that
|
|
562
|
+
once and exits without touching Payload (the Admin action fails instead). The Admin action
|
|
563
|
+
[`sync_now`](#admin-action-jetzt-synchronisieren) runs the same function as
|
|
564
|
+
the workflow `payload-sync-reconcile-catalog`.
|
|
426
565
|
|
|
427
566
|
Every run:
|
|
428
567
|
|
|
@@ -450,7 +589,8 @@ metrics-only limitation.
|
|
|
450
589
|
The runner is restartable and idempotent: safe repeat runs converge on the
|
|
451
590
|
current Medusa state without duplicate mirror documents. Pagination is not a
|
|
452
591
|
database snapshot, so rerun after concurrent catalog changes. V1 deliberately
|
|
453
|
-
has no automatic schedule; operators decide when to rerun it
|
|
592
|
+
has no automatic schedule; operators decide when to rerun it (script or
|
|
593
|
+
`sync_now`).
|
|
454
594
|
|
|
455
595
|
## Retries, logs, and incident recovery
|
|
456
596
|
|
|
@@ -488,7 +628,8 @@ missed delete events without deleting Payload history.
|
|
|
488
628
|
- Medusa categories, collections, prices, inventory, Sales Channels, or other
|
|
489
629
|
commerce facts
|
|
490
630
|
- Medusa Module Links or `defineLink`
|
|
491
|
-
-
|
|
631
|
+
- a plugin-owned Admin UI or HTTP route (settings and the `sync_now` action
|
|
632
|
+
are served by `@hartl-services/medusa-base`), or an automatic schedule
|
|
492
633
|
- Payload hard delete or a configurable delete mode
|
|
493
634
|
- a plugin-owned dead-letter database/table
|
|
494
635
|
|
|
@@ -498,8 +639,9 @@ needed for their editorial model; those remain outside this plugin's contract.
|
|
|
498
639
|
## Plugin development contract
|
|
499
640
|
|
|
500
641
|
Payload Sync is server-to-server integration code and intentionally exposes no
|
|
501
|
-
Store or Admin HTTP routes
|
|
502
|
-
|
|
642
|
+
Store or Admin HTTP routes of its own; its settings and action go through the
|
|
643
|
+
base plugin's `/admin/plugin-features` routes. It has no public DTO, schema,
|
|
644
|
+
path, Storefront client, or runtime Payload-credential contract.
|
|
503
645
|
|
|
504
646
|
If this component gains a Medusa HTTP route, define its request/query/response
|
|
505
647
|
DTOs with `@medusajs/framework/zod`, derive public types from those schemas,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hartl-services/medusa-payload-sync",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Mirrors Medusa product and variant facts into Payload CMS.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"@medusajs/framework": "^2.19.0",
|
|
30
|
-
"@medusajs/medusa": "^2.19.0"
|
|
30
|
+
"@medusajs/medusa": "^2.19.0",
|
|
31
|
+
"@hartl-services/medusa-base": "^1.0.0"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@medusajs/cli": "2.19.0",
|
|
@@ -38,7 +39,8 @@
|
|
|
38
39
|
"@types/jest": "^29.5.14",
|
|
39
40
|
"@types/node": "^20.12.11",
|
|
40
41
|
"jest": "^29.7.0",
|
|
41
|
-
"typescript": "^5.6.2"
|
|
42
|
+
"typescript": "^5.6.2",
|
|
43
|
+
"@hartl-services/medusa-base": "1.0.0"
|
|
42
44
|
},
|
|
43
45
|
"engines": {
|
|
44
46
|
"node": ">=20"
|
|
@@ -47,6 +49,7 @@
|
|
|
47
49
|
"build": "medusa plugin:build",
|
|
48
50
|
"typecheck": "tsc --noEmit",
|
|
49
51
|
"test:unit": "jest --config jest.config.js",
|
|
50
|
-
"test:contract": "jest --config jest.contract.config.js --runInBand"
|
|
52
|
+
"test:contract": "jest --config jest.contract.config.js --runInBand",
|
|
53
|
+
"pretest:unit": "pnpm --filter @hartl-services/medusa-food-supplements-contracts build && pnpm --filter @hartl-services/medusa-base build"
|
|
51
54
|
}
|
|
52
55
|
}
|