@deenruv/merchant-plugin 1.0.17-dev.18 → 1.0.17-dev.23
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 +14 -0
- package/dist/plugin-server/api/platform-integration-admin-resolver.d.ts +20 -1
- package/dist/plugin-server/api/platform-integration-admin-resolver.js +44 -19
- package/dist/plugin-server/entities/merchant-sync-item.entity.d.ts +14 -0
- package/dist/plugin-server/entities/merchant-sync-item.entity.js +56 -0
- package/dist/plugin-server/entities/merchant-sync-run.entity.d.ts +19 -0
- package/dist/plugin-server/entities/merchant-sync-run.entity.js +72 -0
- package/dist/plugin-server/extensions/api-extensions.js +53 -2
- package/dist/plugin-server/index.js +10 -1
- package/dist/plugin-server/services/google-merchant-api.d.ts +48 -6
- package/dist/plugin-server/services/google-merchant-api.js +247 -32
- package/dist/plugin-server/services/google-platform-integration.service.d.ts +8 -7
- package/dist/plugin-server/services/google-platform-integration.service.js +79 -6
- package/dist/plugin-server/services/merchant-sync-history.service.d.ts +25 -0
- package/dist/plugin-server/services/merchant-sync-history.service.js +97 -0
- package/dist/plugin-server/services/platform-integration.service.d.ts +6 -2
- package/dist/plugin-server/services/platform-integration.service.js +53 -29
- package/dist/plugin-server/services/subscriber.service.d.ts +32 -4
- package/dist/plugin-server/services/subscriber.service.js +141 -23
- package/dist/plugin-server/ui/graphql/mutations.ts +7 -0
- package/dist/plugin-server/ui/graphql/queries.ts +99 -0
- package/dist/plugin-server/ui/pages/GooglePage.tsx +158 -16
- package/dist/plugin-server/ui/zeus/const.ts +106 -1
- package/dist/plugin-server/ui/zeus/index.ts +482 -10
- package/dist/plugin-server/zeus/const.js +104 -1
- package/dist/plugin-server/zeus/index.d.ts +492 -6
- package/dist/plugin-ui/graphql/mutations.d.ts +9 -4
- package/dist/plugin-ui/graphql/mutations.js +6 -0
- package/dist/plugin-ui/graphql/queries.d.ts +55 -4
- package/dist/plugin-ui/graphql/queries.js +49 -0
- package/dist/plugin-ui/pages/GooglePage.js +51 -13
- package/dist/plugin-ui/zeus/const.js +104 -1
- package/dist/plugin-ui/zeus/index.d.ts +492 -6
- package/package.json +8 -7
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MerchantSyncHistoryService = exports.summarizeGoogleSyncRun = void 0;
|
|
13
|
+
const core_1 = require("@deenruv/core");
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const merchant_sync_item_entity_js_1 = require("../entities/merchant-sync-item.entity.js");
|
|
16
|
+
const merchant_sync_run_entity_js_1 = require("../entities/merchant-sync-run.entity.js");
|
|
17
|
+
const google_merchant_api_js_1 = require("./google-merchant-api.js");
|
|
18
|
+
function summarizeGoogleSyncRun(input) {
|
|
19
|
+
const resultFailures = input.results.filter((result) => result.status === "error").length;
|
|
20
|
+
const total = input.results.length || input.requestedTotal;
|
|
21
|
+
const failed = input.results.length === 0 && input.fallbackError
|
|
22
|
+
? input.requestedTotal
|
|
23
|
+
: resultFailures;
|
|
24
|
+
const succeeded = Math.max(0, total - failed);
|
|
25
|
+
const status = failed === 0 && !input.fallbackError
|
|
26
|
+
? "SUCCESS"
|
|
27
|
+
: succeeded > 0
|
|
28
|
+
? "PARTIAL"
|
|
29
|
+
: "FAILED";
|
|
30
|
+
return { failed, status, succeeded, total };
|
|
31
|
+
}
|
|
32
|
+
exports.summarizeGoogleSyncRun = summarizeGoogleSyncRun;
|
|
33
|
+
let MerchantSyncHistoryService = class MerchantSyncHistoryService {
|
|
34
|
+
constructor(connection) {
|
|
35
|
+
this.connection = connection;
|
|
36
|
+
}
|
|
37
|
+
async start(ctx, input) {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
return this.connection.getRepository(ctx, merchant_sync_run_entity_js_1.MerchantSyncRun).save(new merchant_sync_run_entity_js_1.MerchantSyncRun({
|
|
40
|
+
errorSummary: null,
|
|
41
|
+
failed: 0,
|
|
42
|
+
finishedAt: null,
|
|
43
|
+
items: [],
|
|
44
|
+
jobId: (_a = input.jobId) !== null && _a !== void 0 ? _a : null,
|
|
45
|
+
platform: input.platform,
|
|
46
|
+
startedAt: new Date(),
|
|
47
|
+
status: "RUNNING",
|
|
48
|
+
succeeded: 0,
|
|
49
|
+
total: (_b = input.total) !== null && _b !== void 0 ? _b : 0,
|
|
50
|
+
trigger: input.trigger,
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
async finishGoogleRun(ctx, run, results, fallbackError) {
|
|
54
|
+
var _a, _b;
|
|
55
|
+
const itemRepository = this.connection.getRepository(ctx, merchant_sync_item_entity_js_1.MerchantSyncItem);
|
|
56
|
+
const items = results.map((result) => {
|
|
57
|
+
var _a, _b;
|
|
58
|
+
const normalized = result.status === "error"
|
|
59
|
+
? (0, google_merchant_api_js_1.normalizeGoogleMerchantError)(result.error)
|
|
60
|
+
: undefined;
|
|
61
|
+
return new merchant_sync_item_entity_js_1.MerchantSyncItem({
|
|
62
|
+
attempts: result.attempts,
|
|
63
|
+
errorCode: (_a = normalized === null || normalized === void 0 ? void 0 : normalized.code) !== null && _a !== void 0 ? _a : null,
|
|
64
|
+
errorMessage: (_b = normalized === null || normalized === void 0 ? void 0 : normalized.message) !== null && _b !== void 0 ? _b : null,
|
|
65
|
+
offerId: result.item.communicateID,
|
|
66
|
+
operation: result.item.method,
|
|
67
|
+
run,
|
|
68
|
+
status: result.status === "success" ? "SUCCESS" : "FAILED",
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
if (items.length > 0)
|
|
72
|
+
await itemRepository.save(items, { chunk: 100 });
|
|
73
|
+
const summary = summarizeGoogleSyncRun({
|
|
74
|
+
fallbackError,
|
|
75
|
+
requestedTotal: run.total,
|
|
76
|
+
results,
|
|
77
|
+
});
|
|
78
|
+
const fallback = fallbackError
|
|
79
|
+
? (0, google_merchant_api_js_1.normalizeGoogleMerchantError)(fallbackError)
|
|
80
|
+
: undefined;
|
|
81
|
+
const firstItemError = items.find((item) => item.errorMessage);
|
|
82
|
+
return this.connection.getRepository(ctx, merchant_sync_run_entity_js_1.MerchantSyncRun).save(Object.assign(Object.assign({}, run), { errorSummary: (_b = (_a = fallback === null || fallback === void 0 ? void 0 : fallback.message) !== null && _a !== void 0 ? _a : firstItemError === null || firstItemError === void 0 ? void 0 : firstItemError.errorMessage) !== null && _b !== void 0 ? _b : null, failed: summary.failed, finishedAt: new Date(), status: summary.status, succeeded: summary.succeeded, total: summary.total }));
|
|
83
|
+
}
|
|
84
|
+
async findLatest(ctx, platform, take = 20) {
|
|
85
|
+
return this.connection.getRepository(ctx, merchant_sync_run_entity_js_1.MerchantSyncRun).find({
|
|
86
|
+
order: { createdAt: "DESC" },
|
|
87
|
+
relations: ["items"],
|
|
88
|
+
take: Math.min(Math.max(take, 1), 100),
|
|
89
|
+
where: { platform },
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
exports.MerchantSyncHistoryService = MerchantSyncHistoryService;
|
|
94
|
+
exports.MerchantSyncHistoryService = MerchantSyncHistoryService = __decorate([
|
|
95
|
+
(0, common_1.Injectable)(),
|
|
96
|
+
__metadata("design:paramtypes", [core_1.TransactionalConnection])
|
|
97
|
+
], MerchantSyncHistoryService);
|
|
@@ -4,6 +4,7 @@ import { MerchantPlatformSettingsEntity } from "../entities/platform-integration
|
|
|
4
4
|
import { FacebookPlatformIntegrationService } from "./facebook-platform-integration.service.js";
|
|
5
5
|
import { GooglePlatformIntegrationService } from "./google-platform-integration.service.js";
|
|
6
6
|
import { MerchantStrategyService } from "./merchant-strategy.service.js";
|
|
7
|
+
import { MerchantSyncHistoryService } from "./merchant-sync-history.service.js";
|
|
7
8
|
type JOB_PAYLOAD = {
|
|
8
9
|
platform: string;
|
|
9
10
|
action: "SEND_ALL_PRODUCTS";
|
|
@@ -15,6 +16,7 @@ export declare class PlatformIntegrationService implements OnModuleInit {
|
|
|
15
16
|
private readonly facebookService;
|
|
16
17
|
private readonly productService;
|
|
17
18
|
private readonly strategy;
|
|
19
|
+
private readonly syncHistory;
|
|
18
20
|
MerchantPlatformQueue: JobQueue<{
|
|
19
21
|
payload: JOB_PAYLOAD;
|
|
20
22
|
worker: number;
|
|
@@ -24,7 +26,7 @@ export declare class PlatformIntegrationService implements OnModuleInit {
|
|
|
24
26
|
}>;
|
|
25
27
|
private readonly logger;
|
|
26
28
|
private log;
|
|
27
|
-
constructor(connection: TransactionalConnection, jobQueueService: JobQueueService, googleService: GooglePlatformIntegrationService, facebookService: FacebookPlatformIntegrationService, productService: ProductService, strategy: MerchantStrategyService);
|
|
29
|
+
constructor(connection: TransactionalConnection, jobQueueService: JobQueueService, googleService: GooglePlatformIntegrationService, facebookService: FacebookPlatformIntegrationService, productService: ProductService, strategy: MerchantStrategyService, syncHistory: MerchantSyncHistoryService);
|
|
28
30
|
removeOrphanItems(ctx: RequestContext, platform: string): Promise<boolean>;
|
|
29
31
|
fetchProducts(input: {
|
|
30
32
|
ctx: RequestContext;
|
|
@@ -32,8 +34,10 @@ export declare class PlatformIntegrationService implements OnModuleInit {
|
|
|
32
34
|
}, callback?: (progress: number) => void): AsyncGenerator<import("../types.js").BaseProductData<import("../types.js").BaseData>, void, unknown>;
|
|
33
35
|
onModuleInit(): Promise<void>;
|
|
34
36
|
createContext(): Promise<RequestContext>;
|
|
35
|
-
savePlatformIntegrationSettings(ctx: RequestContext, settings: MerchantPlatformSettingsEntity): Promise<MerchantPlatformSettingsEntity
|
|
37
|
+
savePlatformIntegrationSettings(ctx: RequestContext, settings: MerchantPlatformSettingsEntity): Promise<MerchantPlatformSettingsEntity>;
|
|
36
38
|
getBaseSettings(ctx: RequestContext, platform: string): Promise<MerchantPlatformSettingsEntity | null>;
|
|
39
|
+
enqueueFullSync(ctx: RequestContext, platform: string): Promise<boolean>;
|
|
40
|
+
getSyncHistory(ctx: RequestContext, platform: string, take?: number): Promise<import("../entities/merchant-sync-run.entity.js").MerchantSyncRun[]>;
|
|
37
41
|
getPlatformAutoUpdateSettings(ctx: RequestContext): Promise<{
|
|
38
42
|
googleAutoUpdate: boolean;
|
|
39
43
|
facebookAutoUpdate: boolean;
|
|
@@ -38,22 +38,24 @@ const facebook_platform_integration_service_js_1 = require("./facebook-platform-
|
|
|
38
38
|
const google_merchant_api_js_1 = require("./google-merchant-api.js");
|
|
39
39
|
const google_platform_integration_service_js_1 = require("./google-platform-integration.service.js");
|
|
40
40
|
const merchant_strategy_service_js_1 = require("./merchant-strategy.service.js");
|
|
41
|
+
const merchant_sync_history_service_js_1 = require("./merchant-sync-history.service.js");
|
|
41
42
|
const BATCH_SIZE = 200;
|
|
42
43
|
const WORKER_THRESHOLD = 5000;
|
|
43
44
|
let PlatformIntegrationService = class PlatformIntegrationService {
|
|
44
|
-
constructor(connection, jobQueueService, googleService, facebookService, productService, strategy) {
|
|
45
|
+
constructor(connection, jobQueueService, googleService, facebookService, productService, strategy, syncHistory) {
|
|
45
46
|
this.connection = connection;
|
|
46
47
|
this.jobQueueService = jobQueueService;
|
|
47
48
|
this.googleService = googleService;
|
|
48
49
|
this.facebookService = facebookService;
|
|
49
50
|
this.productService = productService;
|
|
50
51
|
this.strategy = strategy;
|
|
52
|
+
this.syncHistory = syncHistory;
|
|
51
53
|
this.logger = new core_1.Logger();
|
|
52
54
|
this.log = (message) => this.logger.log(message, "Merchant Platform Service");
|
|
53
55
|
}
|
|
54
56
|
async removeOrphanItems(ctx, platform) {
|
|
55
57
|
if (["facebook", "google"].includes(platform)) {
|
|
56
|
-
await this.OrphanItemsQueue.add({ platform });
|
|
58
|
+
await this.OrphanItemsQueue.add({ platform }, { retries: 3 });
|
|
57
59
|
return true;
|
|
58
60
|
}
|
|
59
61
|
return false;
|
|
@@ -65,13 +67,17 @@ let PlatformIntegrationService = class PlatformIntegrationService {
|
|
|
65
67
|
const start = worker * WORKER_THRESHOLD;
|
|
66
68
|
for (let i = start; i < totalToFetch; i += BATCH_SIZE) {
|
|
67
69
|
const { items } = yield __await(this.productService.findAll(ctx, {
|
|
70
|
+
sort: { id: admin_types_1.SortOrder.ASC },
|
|
68
71
|
take: BATCH_SIZE,
|
|
69
72
|
skip: i,
|
|
70
73
|
}));
|
|
74
|
+
if (items.length === 0)
|
|
75
|
+
break;
|
|
71
76
|
for (const product of items) {
|
|
72
77
|
const baseProduct = yield __await(this.strategy.getBaseData(ctx, product));
|
|
73
78
|
if (callback) {
|
|
74
|
-
const progress = Math.floor((i /
|
|
79
|
+
const progress = Math.min(100, Math.floor(((i - start + items.indexOf(product) + 1) / WORKER_THRESHOLD) *
|
|
80
|
+
100));
|
|
75
81
|
callback(progress);
|
|
76
82
|
}
|
|
77
83
|
if (!baseProduct)
|
|
@@ -117,10 +123,17 @@ let PlatformIntegrationService = class PlatformIntegrationService {
|
|
|
117
123
|
let googleResponse = false;
|
|
118
124
|
let facebookResponse = false;
|
|
119
125
|
if (platform === "google") {
|
|
126
|
+
const run = await this.syncHistory.start(ctx, {
|
|
127
|
+
jobId: job.id == null ? undefined : String(job.id),
|
|
128
|
+
platform,
|
|
129
|
+
total: products.length,
|
|
130
|
+
trigger: "FULL_SYNC",
|
|
131
|
+
});
|
|
120
132
|
const response = await this.googleService.batchProductsAction({
|
|
121
133
|
ctx,
|
|
122
134
|
products,
|
|
123
135
|
});
|
|
136
|
+
await this.syncHistory.finishGoogleRun(ctx, run, response.results, response.error);
|
|
124
137
|
if (response.status === "success") {
|
|
125
138
|
this.log("Products sent to google");
|
|
126
139
|
googleResponse = true;
|
|
@@ -229,36 +242,24 @@ let PlatformIntegrationService = class PlatformIntegrationService {
|
|
|
229
242
|
});
|
|
230
243
|
}
|
|
231
244
|
async savePlatformIntegrationSettings(ctx, settings) {
|
|
232
|
-
const repository = this.connection.getRepository(ctx, platform_integration_settings_entity_js_1.MerchantPlatformSettingsEntity);
|
|
233
|
-
const existing = await repository.findOne({
|
|
234
|
-
where: { platform: settings.platform },
|
|
235
|
-
});
|
|
236
|
-
if (existing)
|
|
237
|
-
await repository.delete(existing.id);
|
|
238
|
-
let response = await repository.save(settings);
|
|
239
|
-
if (!response.id)
|
|
240
|
-
return;
|
|
241
245
|
const [isFirstSync, isAutoUpdate] = [
|
|
242
246
|
this.lookup(settings, "firstSync") === "true",
|
|
243
247
|
this.lookup(settings, "autoUpdate") === "true",
|
|
244
248
|
];
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
+
const shouldStartFullSync = isFirstSync && isAutoUpdate;
|
|
250
|
+
const settingsToSave = new platform_integration_settings_entity_js_1.MerchantPlatformSettingsEntity(Object.assign(Object.assign({}, settings), { entries: settings.entries.map((entry) => shouldStartFullSync && entry.key === "firstSync"
|
|
251
|
+
? Object.assign(Object.assign({}, entry), { value: "false" }) : entry) }));
|
|
252
|
+
const response = await this.connection.withTransaction(ctx, async (transactionCtx) => {
|
|
253
|
+
const repository = this.connection.getRepository(transactionCtx, platform_integration_settings_entity_js_1.MerchantPlatformSettingsEntity);
|
|
254
|
+
const existing = await repository.findOne({
|
|
255
|
+
where: { platform: settings.platform },
|
|
249
256
|
});
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
platform: response.platform,
|
|
257
|
-
action: "SEND_ALL_PRODUCTS",
|
|
258
|
-
},
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
response = await repository.save(Object.assign(Object.assign({}, settings), { entries: settings.entries.map((entry) => entry.key === "firstSync" ? Object.assign(Object.assign({}, entry), { value: "false" }) : entry) }));
|
|
257
|
+
if (existing)
|
|
258
|
+
await repository.delete(existing.id);
|
|
259
|
+
return repository.save(settingsToSave);
|
|
260
|
+
});
|
|
261
|
+
if (response.id && shouldStartFullSync) {
|
|
262
|
+
await this.enqueueFullSync(ctx, response.platform);
|
|
262
263
|
}
|
|
263
264
|
return response;
|
|
264
265
|
}
|
|
@@ -267,6 +268,28 @@ let PlatformIntegrationService = class PlatformIntegrationService {
|
|
|
267
268
|
.getRepository(ctx, platform_integration_settings_entity_js_1.MerchantPlatformSettingsEntity)
|
|
268
269
|
.findOne({ relations: ["entries"], where: { platform } });
|
|
269
270
|
}
|
|
271
|
+
async enqueueFullSync(ctx, platform) {
|
|
272
|
+
if (!["google", "facebook"].includes(platform))
|
|
273
|
+
return false;
|
|
274
|
+
const { totalItems } = await this.productService.findAll(ctx, {
|
|
275
|
+
take: 1,
|
|
276
|
+
skip: 0,
|
|
277
|
+
});
|
|
278
|
+
const workers = Math.max(1, Math.ceil(totalItems / WORKER_THRESHOLD));
|
|
279
|
+
for (let worker = 0; worker < workers; worker += 1) {
|
|
280
|
+
await this.MerchantPlatformQueue.add({
|
|
281
|
+
worker,
|
|
282
|
+
payload: {
|
|
283
|
+
platform,
|
|
284
|
+
action: "SEND_ALL_PRODUCTS",
|
|
285
|
+
},
|
|
286
|
+
}, { retries: 3 });
|
|
287
|
+
}
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
getSyncHistory(ctx, platform, take) {
|
|
291
|
+
return this.syncHistory.findLatest(ctx, platform, take);
|
|
292
|
+
}
|
|
270
293
|
async getPlatformAutoUpdateSettings(ctx) {
|
|
271
294
|
const [googlePlatformSettings, facebookPlatformSettings] = await Promise.all([
|
|
272
295
|
this.getBaseSettings(ctx, "google"),
|
|
@@ -290,5 +313,6 @@ exports.PlatformIntegrationService = PlatformIntegrationService = __decorate([
|
|
|
290
313
|
google_platform_integration_service_js_1.GooglePlatformIntegrationService,
|
|
291
314
|
facebook_platform_integration_service_js_1.FacebookPlatformIntegrationService,
|
|
292
315
|
core_1.ProductService,
|
|
293
|
-
merchant_strategy_service_js_1.MerchantStrategyService
|
|
316
|
+
merchant_strategy_service_js_1.MerchantStrategyService,
|
|
317
|
+
merchant_sync_history_service_js_1.MerchantSyncHistoryService])
|
|
294
318
|
], PlatformIntegrationService);
|
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
import { CreateProductInput, CreateProductVariantInput, UpdateProductInput, UpdateProductVariantInput } from "@deenruv/common/lib/generated-types.js";
|
|
2
|
-
import { EventBus, ID, Product, ProductService, RequestContext } from "@deenruv/core";
|
|
2
|
+
import { EventBus, ID, JobQueueService, Product, ProductService, RequestContext, TransactionalConnection } from "@deenruv/core";
|
|
3
3
|
import { OnApplicationBootstrap } from "@nestjs/common";
|
|
4
4
|
import { MerchantPluginOptions } from "../types.js";
|
|
5
5
|
import { FacebookPlatformIntegrationService } from "./facebook-platform-integration.service.js";
|
|
6
6
|
import { GooglePlatformIntegrationService } from "./google-platform-integration.service.js";
|
|
7
7
|
import { MerchantStrategyService } from "./merchant-strategy.service.js";
|
|
8
|
+
import { MerchantSyncHistoryService } from "./merchant-sync-history.service.js";
|
|
8
9
|
import { PlatformIntegrationService } from "./platform-integration.service.js";
|
|
9
10
|
type ProductInputTypes = CreateProductInput | UpdateProductInput | ID;
|
|
10
11
|
type ProductVariantInputTypes = CreateProductVariantInput[] | UpdateProductVariantInput[] | ID | ID[];
|
|
12
|
+
export declare function getMerchantOperationsForEvent(type: "created" | "updated" | "deleted"): {
|
|
13
|
+
readonly google: "insertProduct";
|
|
14
|
+
readonly facebook: "createProduct";
|
|
15
|
+
} | {
|
|
16
|
+
readonly google: "insertProduct";
|
|
17
|
+
readonly facebook: "updateProduct";
|
|
18
|
+
} | {
|
|
19
|
+
readonly google: "deleteProduct";
|
|
20
|
+
readonly facebook: "deleteProduct";
|
|
21
|
+
};
|
|
22
|
+
export declare function getGoogleDeleteIds(product: {
|
|
23
|
+
variants?: Array<{
|
|
24
|
+
customFields?: {
|
|
25
|
+
communicateID?: string | null;
|
|
26
|
+
};
|
|
27
|
+
sku?: string;
|
|
28
|
+
}>;
|
|
29
|
+
}, data?: Array<{
|
|
30
|
+
communicateID: string;
|
|
31
|
+
}>): string[];
|
|
11
32
|
export declare class SubscriberService implements OnApplicationBootstrap {
|
|
12
33
|
private readonly options;
|
|
13
34
|
private readonly eventBus;
|
|
@@ -16,15 +37,22 @@ export declare class SubscriberService implements OnApplicationBootstrap {
|
|
|
16
37
|
private readonly facebookService;
|
|
17
38
|
private readonly strategy;
|
|
18
39
|
private readonly productService;
|
|
19
|
-
|
|
40
|
+
private readonly jobQueueService;
|
|
41
|
+
private readonly syncHistory;
|
|
42
|
+
private readonly connection;
|
|
43
|
+
private readonly logger;
|
|
44
|
+
private productEventQueue;
|
|
45
|
+
constructor(options: MerchantPluginOptions, eventBus: EventBus, integrationService: PlatformIntegrationService, googleService: GooglePlatformIntegrationService, facebookService: FacebookPlatformIntegrationService, strategy: MerchantStrategyService, productService: ProductService, jobQueueService: JobQueueService, syncHistory: MerchantSyncHistoryService, connection: TransactionalConnection);
|
|
20
46
|
onApplicationBootstrap(): Promise<void>;
|
|
21
|
-
event({ ctx, entity, type, input, }: {
|
|
47
|
+
event({ ctx, entity, type, input, deleteIds, }: {
|
|
22
48
|
ctx: RequestContext;
|
|
23
49
|
entity: Product;
|
|
24
50
|
type: "created" | "updated" | "deleted";
|
|
25
51
|
input?: ProductInputTypes | ProductVariantInputTypes;
|
|
52
|
+
deleteIds?: string[];
|
|
26
53
|
}): Promise<void>;
|
|
54
|
+
private processEvent;
|
|
27
55
|
private isValidEventData;
|
|
28
|
-
private
|
|
56
|
+
private observeOperations;
|
|
29
57
|
}
|
|
30
58
|
export {};
|
|
@@ -12,16 +12,41 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
12
12
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.SubscriberService = void 0;
|
|
15
|
+
exports.SubscriberService = exports.getGoogleDeleteIds = exports.getMerchantOperationsForEvent = void 0;
|
|
16
16
|
const core_1 = require("@deenruv/core");
|
|
17
17
|
const common_1 = require("@nestjs/common");
|
|
18
18
|
const constants_js_1 = require("../constants.js");
|
|
19
19
|
const facebook_platform_integration_service_js_1 = require("./facebook-platform-integration.service.js");
|
|
20
20
|
const google_platform_integration_service_js_1 = require("./google-platform-integration.service.js");
|
|
21
21
|
const merchant_strategy_service_js_1 = require("./merchant-strategy.service.js");
|
|
22
|
+
const merchant_sync_history_service_js_1 = require("./merchant-sync-history.service.js");
|
|
22
23
|
const platform_integration_service_js_1 = require("./platform-integration.service.js");
|
|
24
|
+
function getMerchantOperationsForEvent(type) {
|
|
25
|
+
const operationMap = {
|
|
26
|
+
created: { google: "insertProduct", facebook: "createProduct" },
|
|
27
|
+
updated: { google: "insertProduct", facebook: "updateProduct" },
|
|
28
|
+
deleted: { google: "deleteProduct", facebook: "deleteProduct" },
|
|
29
|
+
};
|
|
30
|
+
return operationMap[type];
|
|
31
|
+
}
|
|
32
|
+
exports.getMerchantOperationsForEvent = getMerchantOperationsForEvent;
|
|
33
|
+
function getGoogleDeleteIds(product, data) {
|
|
34
|
+
var _a, _b, _c;
|
|
35
|
+
const ids = new Set();
|
|
36
|
+
for (const variant of (_a = product.variants) !== null && _a !== void 0 ? _a : []) {
|
|
37
|
+
const id = (_c = (_b = variant.customFields) === null || _b === void 0 ? void 0 : _b.communicateID) !== null && _c !== void 0 ? _c : variant.sku;
|
|
38
|
+
if (id)
|
|
39
|
+
ids.add(String(id));
|
|
40
|
+
}
|
|
41
|
+
for (const item of data !== null && data !== void 0 ? data : []) {
|
|
42
|
+
if (item.communicateID)
|
|
43
|
+
ids.add(String(item.communicateID));
|
|
44
|
+
}
|
|
45
|
+
return [...ids];
|
|
46
|
+
}
|
|
47
|
+
exports.getGoogleDeleteIds = getGoogleDeleteIds;
|
|
23
48
|
let SubscriberService = class SubscriberService {
|
|
24
|
-
constructor(options, eventBus, integrationService, googleService, facebookService, strategy, productService) {
|
|
49
|
+
constructor(options, eventBus, integrationService, googleService, facebookService, strategy, productService, jobQueueService, syncHistory, connection) {
|
|
25
50
|
this.options = options;
|
|
26
51
|
this.eventBus = eventBus;
|
|
27
52
|
this.integrationService = integrationService;
|
|
@@ -29,8 +54,20 @@ let SubscriberService = class SubscriberService {
|
|
|
29
54
|
this.facebookService = facebookService;
|
|
30
55
|
this.strategy = strategy;
|
|
31
56
|
this.productService = productService;
|
|
57
|
+
this.jobQueueService = jobQueueService;
|
|
58
|
+
this.syncHistory = syncHistory;
|
|
59
|
+
this.connection = connection;
|
|
60
|
+
this.logger = new core_1.Logger();
|
|
32
61
|
}
|
|
33
62
|
async onApplicationBootstrap() {
|
|
63
|
+
this.productEventQueue = await this.jobQueueService.createQueue({
|
|
64
|
+
name: "MerchantProductEventQueue",
|
|
65
|
+
process: async (job) => {
|
|
66
|
+
const ctx = core_1.RequestContext.deserialize(job.data.ctx);
|
|
67
|
+
const product = await this.productService.findOne(ctx, job.data.productId);
|
|
68
|
+
await this.processEvent(ctx, product !== null && product !== void 0 ? product : undefined, job.data.type, job.data.deleteIds, job.id == null ? undefined : String(job.id));
|
|
69
|
+
},
|
|
70
|
+
});
|
|
34
71
|
this.eventBus.ofType(core_1.ProductEvent).subscribe(async (props) => {
|
|
35
72
|
await this.event(props);
|
|
36
73
|
});
|
|
@@ -42,39 +79,117 @@ let SubscriberService = class SubscriberService {
|
|
|
42
79
|
const product = await this.productService.findOne(ctx, entity[0].productId);
|
|
43
80
|
if (!product)
|
|
44
81
|
return;
|
|
45
|
-
await this.event({
|
|
82
|
+
await this.event({
|
|
83
|
+
ctx,
|
|
84
|
+
deleteIds: getGoogleDeleteIds({ variants: entity }),
|
|
85
|
+
entity: product,
|
|
86
|
+
type,
|
|
87
|
+
input,
|
|
88
|
+
});
|
|
46
89
|
});
|
|
47
90
|
}
|
|
48
|
-
async event({ ctx, entity, type, input, }) {
|
|
91
|
+
async event({ ctx, entity, type, input, deleteIds, }) {
|
|
49
92
|
if (!this.isValidEventData(ctx, entity))
|
|
50
93
|
return;
|
|
94
|
+
const resolvedDeleteIds = new Set(deleteIds !== null && deleteIds !== void 0 ? deleteIds : getGoogleDeleteIds(entity));
|
|
95
|
+
if (type === "deleted" || resolvedDeleteIds.size === 0) {
|
|
96
|
+
const variants = await this.connection
|
|
97
|
+
.getRepository(ctx, core_1.ProductVariant)
|
|
98
|
+
.find({
|
|
99
|
+
relations: { product: true },
|
|
100
|
+
where: { product: { id: entity.id } },
|
|
101
|
+
withDeleted: true,
|
|
102
|
+
});
|
|
103
|
+
for (const id of getGoogleDeleteIds({ variants })) {
|
|
104
|
+
resolvedDeleteIds.add(id);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
await this.productEventQueue.add({
|
|
108
|
+
ctx: ctx.serialize(),
|
|
109
|
+
deleteIds: [...resolvedDeleteIds],
|
|
110
|
+
productId: entity.id,
|
|
111
|
+
type,
|
|
112
|
+
}, { retries: 3 });
|
|
113
|
+
}
|
|
114
|
+
async processEvent(ctx, entity, type, persistedDeleteIds, jobId) {
|
|
51
115
|
const { googleAutoUpdate, facebookAutoUpdate } = await this.integrationService.getPlatformAutoUpdateSettings(ctx);
|
|
52
116
|
if (!googleAutoUpdate && !facebookAutoUpdate)
|
|
53
117
|
return;
|
|
54
|
-
const data =
|
|
55
|
-
|
|
118
|
+
const data = entity && type !== "deleted"
|
|
119
|
+
? await this.strategy.getBaseData(ctx, entity)
|
|
120
|
+
: undefined;
|
|
121
|
+
const hasExportableData = Boolean(data === null || data === void 0 ? void 0 : data.length);
|
|
122
|
+
if (type === "deleted" || !hasExportableData) {
|
|
123
|
+
if (googleAutoUpdate) {
|
|
124
|
+
const deleteIds = [
|
|
125
|
+
...new Set([
|
|
126
|
+
...persistedDeleteIds,
|
|
127
|
+
...(entity ? getGoogleDeleteIds(entity, data) : []),
|
|
128
|
+
]),
|
|
129
|
+
];
|
|
130
|
+
const run = await this.syncHistory.start(ctx, {
|
|
131
|
+
jobId,
|
|
132
|
+
platform: "google",
|
|
133
|
+
total: deleteIds.length,
|
|
134
|
+
trigger: "PRODUCT_EVENT",
|
|
135
|
+
});
|
|
136
|
+
const response = await this.googleService.deleteProductsByCommunicationIds(ctx, deleteIds);
|
|
137
|
+
await this.syncHistory.finishGoogleRun(ctx, run, response.results, response.error);
|
|
138
|
+
if (response.status === "error") {
|
|
139
|
+
throw response.error instanceof Error
|
|
140
|
+
? response.error
|
|
141
|
+
: new Error("Google product deletion failed");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (facebookAutoUpdate && (data === null || data === void 0 ? void 0 : data.length) && entity) {
|
|
145
|
+
await this.facebookService.deleteProduct({ ctx, data, entity });
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (!data || !entity)
|
|
56
150
|
return;
|
|
57
|
-
const operations =
|
|
151
|
+
const operations = getMerchantOperationsForEvent(type);
|
|
58
152
|
const eventPayload = { ctx, data, entity };
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
153
|
+
if (googleAutoUpdate) {
|
|
154
|
+
const run = await this.syncHistory.start(ctx, {
|
|
155
|
+
jobId,
|
|
156
|
+
platform: "google",
|
|
157
|
+
total: data.length,
|
|
158
|
+
trigger: "PRODUCT_EVENT",
|
|
159
|
+
});
|
|
160
|
+
const response = await this.googleService[operations.google](eventPayload);
|
|
161
|
+
await this.syncHistory.finishGoogleRun(ctx, run, response.results, response.error);
|
|
162
|
+
if (response.status === "error") {
|
|
163
|
+
throw response.error instanceof Error
|
|
164
|
+
? response.error
|
|
165
|
+
: new Error("Google product synchronization failed");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (facebookAutoUpdate) {
|
|
169
|
+
await this.observeOperations([this.facebookService[operations.facebook](eventPayload)], type);
|
|
170
|
+
}
|
|
67
171
|
}
|
|
68
172
|
isValidEventData(ctx, entity) {
|
|
69
173
|
return Boolean(ctx && entity && entity instanceof core_1.Product);
|
|
70
174
|
}
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
175
|
+
async observeOperations(operations, eventType) {
|
|
176
|
+
const results = await Promise.allSettled(operations);
|
|
177
|
+
for (const result of results) {
|
|
178
|
+
if (result.status === "rejected") {
|
|
179
|
+
this.logger.error(`Merchant ${eventType} operation rejected`, result.reason instanceof Error
|
|
180
|
+
? result.reason.stack
|
|
181
|
+
: String(result.reason), "Merchant Product Subscriber");
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const value = result.value;
|
|
185
|
+
if (typeof value === "object" &&
|
|
186
|
+
value !== null &&
|
|
187
|
+
"status" in value &&
|
|
188
|
+
value.status === "error") {
|
|
189
|
+
const error = "error" in value ? value.error : undefined;
|
|
190
|
+
this.logger.error(`Merchant ${eventType} operation failed`, error instanceof Error ? error.stack : String(error), "Merchant Product Subscriber");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
78
193
|
}
|
|
79
194
|
};
|
|
80
195
|
exports.SubscriberService = SubscriberService;
|
|
@@ -86,5 +201,8 @@ exports.SubscriberService = SubscriberService = __decorate([
|
|
|
86
201
|
google_platform_integration_service_js_1.GooglePlatformIntegrationService,
|
|
87
202
|
facebook_platform_integration_service_js_1.FacebookPlatformIntegrationService,
|
|
88
203
|
merchant_strategy_service_js_1.MerchantStrategyService,
|
|
89
|
-
core_1.ProductService
|
|
204
|
+
core_1.ProductService,
|
|
205
|
+
core_1.JobQueueService,
|
|
206
|
+
merchant_sync_history_service_js_1.MerchantSyncHistoryService,
|
|
207
|
+
core_1.TransactionalConnection])
|
|
90
208
|
], SubscriberService);
|
|
@@ -21,3 +21,10 @@ export const saveMerchantPlatformSettings = mutation({
|
|
|
21
21
|
export const removeOrphanItems = mutation({
|
|
22
22
|
removeOrphanItems: [{ platform: $("platform", "String!") }, true],
|
|
23
23
|
});
|
|
24
|
+
|
|
25
|
+
export const sendAllProductsToMerchantPlatform = mutation({
|
|
26
|
+
sendAllProductsToMerchantPlatform: [
|
|
27
|
+
{ platform: $("platform", "String!") },
|
|
28
|
+
true,
|
|
29
|
+
],
|
|
30
|
+
});
|