@deenruv/merchant-plugin 1.0.15 → 1.0.17-dev.14
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 +33 -1
- package/dist/plugin-server/api/platform-integration-admin-resolver.js +16 -3
- package/dist/plugin-server/index.d.ts +2 -2
- package/dist/plugin-server/services/facebook-platform-integration.service.d.ts +9 -12
- package/dist/plugin-server/services/facebook-platform-integration.service.js +14 -6
- package/dist/plugin-server/services/google-merchant-api.d.ts +84 -0
- package/dist/plugin-server/services/google-merchant-api.js +310 -0
- package/dist/plugin-server/services/google-platform-integration.service.d.ts +18 -27
- package/dist/plugin-server/services/google-platform-integration.service.js +164 -272
- package/dist/plugin-server/services/platform-integration.service.js +34 -22
- package/dist/plugin-server/types.d.ts +16 -7
- package/dist/plugin-server/ui/pages/GooglePage.tsx +21 -0
- package/dist/plugin-ui/pages/FacebookPage.d.ts +2 -1
- package/dist/plugin-ui/pages/GooglePage.d.ts +2 -1
- package/dist/plugin-ui/pages/GooglePage.js +10 -3
- package/package.json +12 -12
|
@@ -35,6 +35,7 @@ const core_1 = require("@deenruv/core");
|
|
|
35
35
|
const common_1 = require("@nestjs/common");
|
|
36
36
|
const platform_integration_settings_entity_js_1 = require("../entities/platform-integration-settings.entity.js");
|
|
37
37
|
const facebook_platform_integration_service_js_1 = require("./facebook-platform-integration.service.js");
|
|
38
|
+
const google_merchant_api_js_1 = require("./google-merchant-api.js");
|
|
38
39
|
const google_platform_integration_service_js_1 = require("./google-platform-integration.service.js");
|
|
39
40
|
const merchant_strategy_service_js_1 = require("./merchant-strategy.service.js");
|
|
40
41
|
const BATCH_SIZE = 200;
|
|
@@ -127,6 +128,9 @@ let PlatformIntegrationService = class PlatformIntegrationService {
|
|
|
127
128
|
else {
|
|
128
129
|
this.log("Error sending products to google");
|
|
129
130
|
googleResponse = false;
|
|
131
|
+
throw response.error instanceof Error
|
|
132
|
+
? response.error
|
|
133
|
+
: new Error("Google product sync failed");
|
|
130
134
|
}
|
|
131
135
|
}
|
|
132
136
|
if (platform === "facebook") {
|
|
@@ -141,6 +145,7 @@ let PlatformIntegrationService = class PlatformIntegrationService {
|
|
|
141
145
|
else {
|
|
142
146
|
this.log("Error sending products to facebook");
|
|
143
147
|
facebookResponse = false;
|
|
148
|
+
throw new Error(response.message || "Facebook product sync failed");
|
|
144
149
|
}
|
|
145
150
|
}
|
|
146
151
|
return { status: "SUCCESS", facebookResponse, googleResponse };
|
|
@@ -166,33 +171,40 @@ let PlatformIntegrationService = class PlatformIntegrationService {
|
|
|
166
171
|
const service = map[platform];
|
|
167
172
|
if (!service)
|
|
168
173
|
throw new Error("Unknown platform");
|
|
169
|
-
const
|
|
174
|
+
const remoteProducts = await service.getAllProducts(ctx);
|
|
170
175
|
const products = [];
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
176
|
+
const { totalItems } = await this.productService.findAll(ctx, {
|
|
177
|
+
take: 1,
|
|
178
|
+
skip: 0,
|
|
179
|
+
});
|
|
180
|
+
const workers = Math.ceil(totalItems / WORKER_THRESHOLD);
|
|
181
|
+
for (let worker = 0; worker < workers; worker++) {
|
|
182
|
+
try {
|
|
183
|
+
for (var _d = true, _e = (e_2 = void 0, __asyncValues(this.fetchProducts({ ctx, worker }, (progress) => {
|
|
184
|
+
if (job.state === admin_types_1.JobState.CANCELLED) {
|
|
185
|
+
throw new Error("Job was cancelled");
|
|
186
|
+
}
|
|
187
|
+
else
|
|
188
|
+
job.setProgress(progress);
|
|
189
|
+
}))), _f; _f = await _e.next(), _a = _f.done, !_a; _d = true) {
|
|
190
|
+
_c = _f.value;
|
|
191
|
+
_d = false;
|
|
192
|
+
const product = _c;
|
|
193
|
+
products.push(...product);
|
|
175
194
|
}
|
|
176
|
-
else
|
|
177
|
-
job.setProgress(progress);
|
|
178
|
-
})), _f; _f = await _e.next(), _a = _f.done, !_a; _d = true) {
|
|
179
|
-
_c = _f.value;
|
|
180
|
-
_d = false;
|
|
181
|
-
const product = _c;
|
|
182
|
-
products.push(...product);
|
|
183
195
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
196
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
197
|
+
finally {
|
|
198
|
+
try {
|
|
199
|
+
if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
|
|
200
|
+
}
|
|
201
|
+
finally { if (e_2) throw e_2.error; }
|
|
189
202
|
}
|
|
190
|
-
finally { if (e_2) throw e_2.error; }
|
|
191
203
|
}
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
194
|
-
this.log(`Found ${
|
|
195
|
-
await service.removeOrphanItems(ctx,
|
|
204
|
+
const orphanProducts = (0, google_merchant_api_js_1.selectRemoteOrphanProducts)(remoteProducts, products);
|
|
205
|
+
if (orphanProducts.length > 0) {
|
|
206
|
+
this.log(`Found ${orphanProducts.length} orphan items for platform ${platform}`);
|
|
207
|
+
await service.removeOrphanItems(ctx, orphanProducts);
|
|
196
208
|
}
|
|
197
209
|
else {
|
|
198
210
|
this.log(`No orphan items found for platform ${platform}`);
|
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
import { InjectableStrategy, Product, RequestContext } from "@deenruv/core";
|
|
2
|
-
import {
|
|
2
|
+
import type { protos } from "@google-shopping/products";
|
|
3
3
|
export type BaseData = {
|
|
4
4
|
communicateID: string;
|
|
5
5
|
variantID: string | number;
|
|
6
6
|
};
|
|
7
7
|
export type BaseProductData<T extends BaseData> = Array<T>;
|
|
8
8
|
export type MerchantPluginOptions = {
|
|
9
|
-
strategy?: MerchantExportStrategy<BaseProductData<
|
|
9
|
+
strategy?: MerchantExportStrategy<BaseProductData<BaseData>>;
|
|
10
|
+
};
|
|
11
|
+
export type GoogleProductInput = protos.google.shopping.merchant.products.v1.IProductInput;
|
|
12
|
+
export type GoogleProcessedProduct = protos.google.shopping.merchant.products.v1.IProduct;
|
|
13
|
+
export type GoogleProduct = BaseData & {
|
|
14
|
+
productAttributes: NonNullable<GoogleProductInput["productAttributes"]>;
|
|
15
|
+
customAttributes?: GoogleProductInput["customAttributes"];
|
|
16
|
+
versionNumber?: GoogleProductInput["versionNumber"];
|
|
17
|
+
};
|
|
18
|
+
export type RemoteProduct = Pick<BaseData, "communicateID"> & {
|
|
19
|
+
name?: string;
|
|
10
20
|
};
|
|
11
|
-
export type GoogleProduct = Omit<content_v2_1.Schema$Product, "brand"> & BaseData;
|
|
12
21
|
export type FacebookProduct = Record<string, unknown> & {
|
|
13
22
|
communicateID: string;
|
|
14
23
|
variantID: string | number;
|
|
15
24
|
};
|
|
16
|
-
export interface MerchantExportStrategy<T extends BaseProductData<
|
|
17
|
-
getBaseData
|
|
18
|
-
prepareGoogleProductPayload
|
|
19
|
-
prepareFacebookProductPayload
|
|
25
|
+
export interface MerchantExportStrategy<T extends BaseProductData<BaseData>> extends InjectableStrategy {
|
|
26
|
+
getBaseData(ctx: RequestContext, product: Product): Promise<T | undefined>;
|
|
27
|
+
prepareGoogleProductPayload(ctx: RequestContext, data: T): Promise<Array<GoogleProduct> | undefined>;
|
|
28
|
+
prepareFacebookProductPayload(ctx: RequestContext, data: T): Promise<Array<FacebookProduct> | undefined>;
|
|
20
29
|
}
|
|
21
30
|
declare module "@deenruv/core" {
|
|
22
31
|
interface CustomProductFields {
|
|
@@ -54,6 +54,7 @@ export const GooglePage = () => {
|
|
|
54
54
|
const [settingsForm, setSettingsForm] = useState({
|
|
55
55
|
brand: "",
|
|
56
56
|
merchantId: "",
|
|
57
|
+
dataSource: "",
|
|
57
58
|
credentials: "",
|
|
58
59
|
autoUpdate: true,
|
|
59
60
|
firstSync: true,
|
|
@@ -75,6 +76,9 @@ export const GooglePage = () => {
|
|
|
75
76
|
if (key === "merchantId") {
|
|
76
77
|
acc.merchantId = value;
|
|
77
78
|
}
|
|
79
|
+
if (key === "dataSource") {
|
|
80
|
+
acc.dataSource = value;
|
|
81
|
+
}
|
|
78
82
|
if (key === "credentials") {
|
|
79
83
|
acc.credentials = value;
|
|
80
84
|
}
|
|
@@ -89,6 +93,7 @@ export const GooglePage = () => {
|
|
|
89
93
|
{} as {
|
|
90
94
|
brand: string;
|
|
91
95
|
merchantId: string;
|
|
96
|
+
dataSource: string;
|
|
92
97
|
credentials: string;
|
|
93
98
|
autoUpdate: boolean;
|
|
94
99
|
firstSync: boolean;
|
|
@@ -183,6 +188,7 @@ export const GooglePage = () => {
|
|
|
183
188
|
<label>Merchant ID</label>
|
|
184
189
|
<input
|
|
185
190
|
className="w-full"
|
|
191
|
+
required
|
|
186
192
|
value={settingsForm.merchantId}
|
|
187
193
|
onChange={(e) =>
|
|
188
194
|
setSettingsForm({
|
|
@@ -193,6 +199,21 @@ export const GooglePage = () => {
|
|
|
193
199
|
/>
|
|
194
200
|
</div>
|
|
195
201
|
</div>
|
|
202
|
+
<div className="w-full flex flex-col gap-2">
|
|
203
|
+
<label>Data source</label>
|
|
204
|
+
<input
|
|
205
|
+
className="w-full"
|
|
206
|
+
placeholder="accounts/{merchantId}/dataSources/{id}"
|
|
207
|
+
required
|
|
208
|
+
value={settingsForm.dataSource}
|
|
209
|
+
onChange={(e) =>
|
|
210
|
+
setSettingsForm({
|
|
211
|
+
...settingsForm,
|
|
212
|
+
dataSource: e.target.value,
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
/>
|
|
216
|
+
</div>
|
|
196
217
|
<div className="flex flex-col gap-4">
|
|
197
218
|
<div className="flex flex-col">
|
|
198
219
|
<label>Google Account Credentials</label>
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import React from "react";
|
|
2
|
+
export declare const FacebookPage: () => React.JSX.Element;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import React from "react";
|
|
2
|
+
export declare const GooglePage: () => React.JSX.Element;
|
|
@@ -41,6 +41,7 @@ export const GooglePage = () => {
|
|
|
41
41
|
const [settingsForm, setSettingsForm] = useState({
|
|
42
42
|
brand: "",
|
|
43
43
|
merchantId: "",
|
|
44
|
+
dataSource: "",
|
|
44
45
|
credentials: "",
|
|
45
46
|
autoUpdate: true,
|
|
46
47
|
firstSync: true,
|
|
@@ -59,6 +60,9 @@ export const GooglePage = () => {
|
|
|
59
60
|
if (key === "merchantId") {
|
|
60
61
|
acc.merchantId = value;
|
|
61
62
|
}
|
|
63
|
+
if (key === "dataSource") {
|
|
64
|
+
acc.dataSource = value;
|
|
65
|
+
}
|
|
62
66
|
if (key === "credentials") {
|
|
63
67
|
acc.credentials = value;
|
|
64
68
|
}
|
|
@@ -128,10 +132,13 @@ export const GooglePage = () => {
|
|
|
128
132
|
alignItems: "center",
|
|
129
133
|
width: "42px",
|
|
130
134
|
height: "42px",
|
|
131
|
-
}, className: "spinner" }) })), _jsxs("form", { className: "flex flex-col gap-4", onSubmit: onSubmit, children: [_jsxs("div", { className: "flex justify-between gap-4", children: [_jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "Brand" }), _jsx(Input, { className: "w-full", value: settingsForm.brand, onChange: (e) => setSettingsForm({ ...settingsForm, brand: e.target.value }) })] }), _jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "Merchant ID" }), _jsx(Input, { className: "w-full", value: settingsForm.merchantId, onChange: (e) => setSettingsForm({
|
|
135
|
+
}, className: "spinner" }) })), _jsxs("form", { className: "flex flex-col gap-4", onSubmit: onSubmit, children: [_jsxs("div", { className: "flex justify-between gap-4", children: [_jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "Brand" }), _jsx(Input, { className: "w-full", value: settingsForm.brand, onChange: (e) => setSettingsForm({ ...settingsForm, brand: e.target.value }) })] }), _jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "Merchant ID" }), _jsx(Input, { className: "w-full", required: true, value: settingsForm.merchantId, onChange: (e) => setSettingsForm({
|
|
132
136
|
...settingsForm,
|
|
133
137
|
merchantId: e.target.value,
|
|
134
|
-
}) })] })] }), _jsxs("div", { className: "flex flex-col gap-
|
|
138
|
+
}) })] })] }), _jsxs("div", { className: "w-full flex flex-col gap-2", children: [_jsx(Label, { children: "Data source" }), _jsx(Input, { className: "w-full", placeholder: "accounts/{merchantId}/dataSources/{id}", required: true, value: settingsForm.dataSource, onChange: (e) => setSettingsForm({
|
|
139
|
+
...settingsForm,
|
|
140
|
+
dataSource: e.target.value,
|
|
141
|
+
}) })] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col", children: [_jsx(Label, { children: "Google Account Credentials" }), _jsx(Input, { style: {
|
|
135
142
|
border: "none",
|
|
136
143
|
backgroundColor: "transparent",
|
|
137
144
|
background: "none",
|
|
@@ -143,7 +150,7 @@ export const GooglePage = () => {
|
|
|
143
150
|
firstSync: typeof checked === "boolean" ? checked : false,
|
|
144
151
|
}) })] }), _jsx(Button, { children: "Save" })] }) })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx("span", { children: "Connection status" }), serviceInfo.connectionStatus ? _jsx("div", { children: "\uD83D\uDC9A" }) : _jsx("div", { children: "\uD83D\uDC94" })] }), serviceInfo.connectionStatus ? (_jsx("div", { className: "mt-8", children: _jsx(Button, { onClick: async () => {
|
|
145
152
|
try {
|
|
146
|
-
await removeOldItems({ platform: "
|
|
153
|
+
await removeOldItems({ platform: "google" });
|
|
147
154
|
toast.success("Old items removed successfully");
|
|
148
155
|
refetch();
|
|
149
156
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deenruv/merchant-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17-dev.14",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -16,27 +16,26 @@
|
|
|
16
16
|
"./plugin-ui": "./dist/plugin-ui/index.js"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"@google-shopping/products": "0.9.0",
|
|
19
20
|
"@nestjs/common": "~10.3.10",
|
|
20
21
|
"@nestjs/graphql": "~12.2.0",
|
|
21
22
|
"date-fns": "^4.1.0",
|
|
22
23
|
"facebook-nodejs-business-sdk": "^23.0.1",
|
|
23
|
-
"google-auth-library": "^9.15.0",
|
|
24
|
-
"googleapis": "^144.0.0",
|
|
25
24
|
"graphql": "~16.9.0",
|
|
26
25
|
"graphql-tag": "^2.12.6",
|
|
27
26
|
"graphql-zeus": "^5.4.2",
|
|
28
27
|
"html-to-text": "^9.0.5",
|
|
29
|
-
"lucide-react": "^
|
|
28
|
+
"lucide-react": "^1.21.0",
|
|
30
29
|
"react": "^19.0.0",
|
|
31
30
|
"react-dom": "^19.0.0",
|
|
32
31
|
"react-i18next": "^14.0.5",
|
|
33
|
-
"sonner": "^1.4.41",
|
|
34
32
|
"recharts": "^2.12.7",
|
|
35
|
-
"
|
|
36
|
-
"@deenruv/
|
|
37
|
-
"@deenruv/
|
|
38
|
-
"@deenruv/
|
|
39
|
-
"@deenruv/
|
|
33
|
+
"sonner": "^1.4.41",
|
|
34
|
+
"@deenruv/admin-types": "^1.0.17-dev.14",
|
|
35
|
+
"@deenruv/common": "^1.0.17-dev.14",
|
|
36
|
+
"@deenruv/admin-ui": "^1.0.17-dev.14",
|
|
37
|
+
"@deenruv/react-ui-devkit": "^1.0.17-dev.14",
|
|
38
|
+
"@deenruv/ui-devkit": "^1.0.17-dev.14"
|
|
40
39
|
},
|
|
41
40
|
"devDependencies": {
|
|
42
41
|
"@graphql-typed-document-node/core": "3.2.0",
|
|
@@ -48,16 +47,17 @@
|
|
|
48
47
|
"rimraf": "^5.0.10",
|
|
49
48
|
"tslib": "^2.6.2",
|
|
50
49
|
"typescript": "5.3.3",
|
|
51
|
-
"@deenruv/core": "^1.0.
|
|
50
|
+
"@deenruv/core": "^1.0.17-dev.14"
|
|
52
51
|
},
|
|
53
52
|
"peerDependencies": {
|
|
54
|
-
"@deenruv/core": "^1.0.0"
|
|
53
|
+
"@deenruv/core": "^1.0.0 || ^1.0.17-dev.0"
|
|
55
54
|
},
|
|
56
55
|
"scripts": {
|
|
57
56
|
"build": "rimraf dist && tsc --build && cp -r ./src/plugin-server/ui ./dist/plugin-server",
|
|
58
57
|
"watch": "concurrently -k -p \"[{name}]\" -n \"SERVER,UI\" -c \"yellow.bold,cyan.bold\" \"tsc --watch --project src/plugin-server/tsconfig.json\" \"tsc --watch --project src/plugin-ui/tsconfig.json\"",
|
|
59
58
|
"lint": "eslint ./src/**/*.ts",
|
|
60
59
|
"lint:fix": "eslint --fix ./src/**/*.ts",
|
|
60
|
+
"test": "vitest --run --exclude='src/plugin-server/e2e/**'",
|
|
61
61
|
"zeus": "zeus http://localhost:6100/admin-api ./src/plugin-server --es && zeus http://localhost:6100/admin-api ./src/plugin-server/ui --td && zeus http://localhost:6100/admin-api ./src/plugin-ui --td --es"
|
|
62
62
|
}
|
|
63
63
|
}
|