cloudcommerce 0.0.52 → 0.0.55

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/package.json +6 -6
  3. package/packages/api/lib/index.js +14 -3
  4. package/packages/api/lib/index.js.map +1 -1
  5. package/packages/api/lib/types.d.ts +1 -1
  6. package/packages/api/package.json +1 -1
  7. package/packages/api/src/index.ts +14 -3
  8. package/packages/api/src/types.ts +1 -1
  9. package/packages/apps/discounts/lib/discounts.d.ts +1 -1
  10. package/packages/apps/discounts/lib/discounts.js +2 -3
  11. package/packages/apps/discounts/lib/discounts.js.map +1 -1
  12. package/packages/apps/discounts/lib-cjs/apply-discount.cjs +330 -0
  13. package/packages/apps/discounts/lib-cjs/helpers.cjs +167 -0
  14. package/packages/apps/discounts/package.json +2 -1
  15. package/packages/apps/discounts/src/discounts.ts +2 -3
  16. package/packages/apps/tiny-erp/CHANGELOG.md +1 -0
  17. package/packages/apps/tiny-erp/README.md +1 -0
  18. package/packages/apps/tiny-erp/package.json +27 -0
  19. package/packages/apps/tiny-erp/src/index.ts +0 -0
  20. package/packages/apps/tiny-erp/tsconfig.json +3 -0
  21. package/packages/cli/lib/config-gcloud.js +9 -0
  22. package/packages/cli/package.json +1 -1
  23. package/packages/cli/src/config-gcloud.ts +11 -4
  24. package/packages/events/package.json +1 -1
  25. package/packages/firebase/lib/config.d.ts +5 -0
  26. package/packages/firebase/lib/config.js +8 -0
  27. package/packages/firebase/lib/config.js.map +1 -1
  28. package/packages/firebase/lib/handlers/check-store-events.js +92 -15
  29. package/packages/firebase/lib/handlers/check-store-events.js.map +1 -1
  30. package/packages/firebase/package.json +1 -1
  31. package/packages/firebase/src/config.ts +9 -0
  32. package/packages/firebase/src/handlers/check-store-events.ts +98 -18
  33. package/packages/modules/lib/firebase/call-app-module.js +36 -2
  34. package/packages/modules/lib/firebase/call-app-module.js.map +1 -1
  35. package/packages/modules/lib/firebase/handle-module.js +6 -1
  36. package/packages/modules/lib/firebase/handle-module.js.map +1 -1
  37. package/packages/modules/package.json +1 -1
  38. package/packages/modules/src/firebase/call-app-module.ts +37 -3
  39. package/packages/modules/src/firebase/handle-module.ts +6 -1
  40. package/packages/passport/package.json +1 -1
  41. package/packages/ssr/package.json +1 -1
  42. package/packages/storefront/package.json +4 -4
  43. package/packages/types/index.ts +4 -1
  44. package/packages/types/package.json +1 -1
  45. package/packages/firebase/lib/types.d.ts +0 -6
  46. package/packages/firebase/lib/types.js +0 -2
  47. package/packages/firebase/lib/types.js.map +0 -1
  48. package/packages/firebase/src/types.ts +0 -11
@@ -0,0 +1,167 @@
1
+ const ecomUtils = require('@ecomplus/utils');
2
+
3
+ const validateDateRange = (rule) => {
4
+ // filter campaings by date
5
+ const timestamp = Date.now();
6
+ if (rule.date_range) {
7
+ if (rule.date_range.start && new Date(rule.date_range.start).getTime() > timestamp) {
8
+ return false;
9
+ }
10
+ if (rule.date_range.end && new Date(rule.date_range.end).getTime() < timestamp) {
11
+ return false;
12
+ }
13
+ }
14
+ return true;
15
+ };
16
+
17
+ const validateCustomerId = (rule, params) => {
18
+ if (
19
+ Array.isArray(rule.customer_ids)
20
+ && rule.customer_ids.length
21
+ && rule.customer_ids.indexOf(params.customer && params.customer._id) === -1
22
+ ) {
23
+ // unavailable for current customer
24
+ return false;
25
+ }
26
+ return true;
27
+ };
28
+
29
+ const checkOpenPromotion = (rule) => {
30
+ return !rule.discount_coupon && !rule.utm_campaign
31
+ && (!Array.isArray(rule.customer_ids) || !rule.customer_ids.length);
32
+ };
33
+
34
+ const getValidDiscountRules = (discountRules, params, items) => {
35
+ if (Array.isArray(discountRules) && discountRules.length) {
36
+ // validate rules objects
37
+ return discountRules.filter((rule) => {
38
+ if (!rule || !validateCustomerId(rule, params)) {
39
+ return false;
40
+ }
41
+
42
+ if (Array.isArray(rule.product_ids) && Array.isArray(items)) {
43
+ const checkProductId = (item) => {
44
+ return (!rule.product_ids.length || rule.product_ids.indexOf(item.product_id) > -1);
45
+ };
46
+ // set/add discount value from lowest item price
47
+ let value;
48
+ if (rule.discount_lowest_price) {
49
+ items.forEach((item) => {
50
+ const price = ecomUtils.price(item);
51
+ if (price > 0 && checkProductId(item) && (!value || value > price)) {
52
+ value = price;
53
+ }
54
+ });
55
+ } else if (rule.discount_kit_subtotal) {
56
+ value = 0;
57
+ items.forEach((item) => {
58
+ const price = ecomUtils.price(item);
59
+ if (price > 0 && checkProductId(item)) {
60
+ value += price * item.quantity;
61
+ }
62
+ });
63
+ }
64
+ if (value) {
65
+ if (rule.discount && rule.discount.value) {
66
+ if (rule.discount.type === 'percentage') {
67
+ value *= rule.discount.value / 100;
68
+ } else {
69
+ value += rule.discount.value;
70
+ }
71
+ }
72
+ rule.discount = {
73
+ ...rule.discount,
74
+ type: 'fixed',
75
+ value,
76
+ };
77
+ }
78
+ }
79
+ if (!rule.discount || !rule.discount.value) {
80
+ return false;
81
+ }
82
+
83
+ return validateDateRange(rule);
84
+ });
85
+ }
86
+
87
+ // returns array anyway
88
+ return [];
89
+ };
90
+
91
+ const matchDiscountRule = (discountRules, params) => {
92
+ // try to match a promotion
93
+ if (params.discount_coupon) {
94
+ // match only by discount coupon
95
+ return {
96
+ discountRule: discountRules.find((rule) => {
97
+ return rule.case_insensitive
98
+ ? typeof rule.discount_coupon === 'string'
99
+ && rule.discount_coupon.toUpperCase() === params.discount_coupon.toUpperCase()
100
+ : rule.discount_coupon === params.discount_coupon;
101
+ }),
102
+ discountMatchEnum: 'COUPON',
103
+ };
104
+ }
105
+
106
+ // try to match by UTM campaign first
107
+ if (params.utm && params.utm.campaign) {
108
+ const discountRule = discountRules.find((rule) => {
109
+ return rule.case_insensitive
110
+ ? typeof rule.utm_campaign === 'string'
111
+ && rule.utm_campaign.toUpperCase() === params.utm.campaign.toUpperCase()
112
+ : rule.utm_campaign === params.utm.campaign;
113
+ });
114
+ if (discountRule) {
115
+ return {
116
+ discountRule,
117
+ discountMatchEnum: 'UTM',
118
+ };
119
+ }
120
+ }
121
+
122
+ // then try to match by customer
123
+ if (params.customer && params.customer._id) {
124
+ const discountRule = discountRules.find((rule) => Array.isArray(rule.customer_ids)
125
+ && rule.customer_ids.indexOf(params.customer._id) > -1);
126
+ if (discountRule) {
127
+ return {
128
+ discountRule,
129
+ discountMatchEnum: 'CUSTOMER',
130
+ };
131
+ }
132
+ }
133
+
134
+ // last try to match by open promotions
135
+ return {
136
+ discountRule: discountRules.find(checkOpenPromotion),
137
+ discountMatchEnum: 'OPEN',
138
+ };
139
+ };
140
+
141
+ const checkCampaignProducts = (campaignProducts, params) => {
142
+ if (Array.isArray(campaignProducts) && campaignProducts.length) {
143
+ // must check at least one campaign product on cart
144
+ let hasProductMatch;
145
+ if (params.items && params.items.length) {
146
+ for (let i = 0; i < campaignProducts.length; i++) {
147
+ if (params.items.find((item) => item.quantity && item.product_id === campaignProducts[i])) {
148
+ hasProductMatch = true;
149
+ break;
150
+ }
151
+ }
152
+ }
153
+ if (!hasProductMatch) {
154
+ return false;
155
+ }
156
+ }
157
+ return true;
158
+ };
159
+
160
+ module.exports = {
161
+ validateDateRange,
162
+ validateCustomerId,
163
+ checkOpenPromotion,
164
+ getValidDiscountRules,
165
+ matchDiscountRule,
166
+ checkCampaignProducts,
167
+ };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/app-discounts",
3
3
  "type": "module",
4
- "version": "0.0.52",
4
+ "version": "0.0.55",
5
5
  "description": "E-Com Plus Cloud Commerce app for complex discount rules",
6
6
  "main": "lib/discounts.js",
7
7
  "repository": {
@@ -21,6 +21,7 @@
21
21
  "dependencies": {
22
22
  "@cloudcommerce/api": "workspace:*",
23
23
  "@cloudcommerce/firebase": "workspace:*",
24
+ "@ecomplus/utils": "^1.4.1",
24
25
  "firebase-admin": "^11.0.1",
25
26
  "firebase-functions": "^3.22.0"
26
27
  },
@@ -1,8 +1,7 @@
1
1
  /* eslint-disable import/prefer-default-export */
2
2
  import type { AppModuleBody } from '@cloudcommerce/types';
3
- import { logger } from 'firebase-functions';
3
+ import * as handleApplyDiscount from '../lib-cjs/apply-discount.cjs';
4
4
 
5
5
  export const applyDiscount = async (modBody: AppModuleBody) => {
6
- logger.info(modBody);
7
- return {};
6
+ return handleApplyDiscount(modBody);
8
7
  };
@@ -0,0 +1 @@
1
+ Please refer to GitHub [repository releases](https://github.com/ecomplus/cloud-commerce/releases) or monorepo unified [CHANGELOG.md](https://github.com/ecomplus/cloud-commerce/blob/main/CHANGELOG.md).
@@ -0,0 +1 @@
1
+ # `@cloudcommerce/tiny`
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@cloudcommerce/app-tiny-erp",
3
+ "type": "module",
4
+ "version": "0.0.55",
5
+ "description": "E-Com Plus Cloud Commerce",
6
+ "main": "lib/index.js",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/ecomplus/cloud-commerce.git",
10
+ "directory": "packages/tiny"
11
+ },
12
+ "author": "E-Com Club Softwares para E-commerce <ti@e-com.club>",
13
+ "license": "Apache 2.0 with Commons Clause",
14
+ "bugs": {
15
+ "url": "https://github.com/ecomplus/cloud-commerce/issues"
16
+ },
17
+ "homepage": "https://github.com/ecomplus/cloud-commerce/tree/main/packages/tiny#readme",
18
+ "scripts": {
19
+ "build": "echo '@ecomplus/tiny-erp'"
20
+ },
21
+ "dependencies": {
22
+ "@cloudcommerce/api": "workspace:*"
23
+ },
24
+ "devDependencies": {
25
+ "@cloudcommerce/types": "workspace:*"
26
+ }
27
+ }
File without changes
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../tsconfig.json"
3
+ }
@@ -30,6 +30,7 @@ const siginGcloudAndSetIAM = async (projectId, pwd) => {
30
30
  'roles/iam.serviceAccountUser',
31
31
  'roles/run.viewer',
32
32
  'roles/serviceusage.apiKeysViewer',
33
+ 'roles/serviceusage.serviceUsageAdmin',
33
34
  ];
34
35
  const serviceAccount = await checkServiceAccountExists(projectId);
35
36
  if (!serviceAccount) {
@@ -53,6 +54,14 @@ const siginGcloudAndSetIAM = async (projectId, pwd) => {
53
54
  ],
54
55
  role,
55
56
  };
57
+ if (role === 'roles/serviceusage.serviceUsageAdmin') {
58
+ const roleExpiration = Date.now() + 1000 * 60 * 60 * 12;
59
+ newBinding.condition = {
60
+ expression: `request.time < timestamp("${new Date(roleExpiration).toISOString()}")`,
61
+ title: 'Enable APIs on first deploy',
62
+ description: null,
63
+ };
64
+ }
56
65
  bindings.push(newBinding);
57
66
  mustUpdatePolicy = true;
58
67
  } else {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/cli",
3
3
  "type": "module",
4
- "version": "0.0.52",
4
+ "version": "0.0.55",
5
5
  "description": "E-Com Plus Cloud Commerce CLI tools",
6
6
  "bin": {
7
7
  "cloudcommerce": "./bin/run.mjs"
@@ -32,6 +32,7 @@ const siginGcloudAndSetIAM = async (projectId: string, pwd: string) => {
32
32
  'roles/iam.serviceAccountUser',
33
33
  'roles/run.viewer',
34
34
  'roles/serviceusage.apiKeysViewer',
35
+ 'roles/serviceusage.serviceUsageAdmin',
35
36
  ];
36
37
  const serviceAccount = await checkServiceAccountExists(projectId);
37
38
  if (!serviceAccount) {
@@ -47,17 +48,23 @@ const siginGcloudAndSetIAM = async (projectId: string, pwd: string) => {
47
48
 
48
49
  let mustUpdatePolicy = false;
49
50
  roles.forEach((role) => {
50
- const roleFound = bindings.find(
51
- (binding: { [key: string]: string | string[] }) => binding.role === role,
52
- );
51
+ const roleFound = bindings.find((binding) => binding.role === role);
53
52
  const memberServiceAccount = `serviceAccount:${getAccountEmail(projectId)}`;
54
53
  if (!roleFound) {
55
- const newBinding = {
54
+ const newBinding: { [key: string]: any } = {
56
55
  members: [
57
56
  memberServiceAccount,
58
57
  ],
59
58
  role,
60
59
  };
60
+ if (role === 'roles/serviceusage.serviceUsageAdmin') {
61
+ const roleExpiration = Date.now() + 1000 * 60 * 60 * 12;
62
+ newBinding.condition = {
63
+ expression: `request.time < timestamp("${new Date(roleExpiration).toISOString()}")`,
64
+ title: 'Enable APIs on first deploy',
65
+ description: null,
66
+ };
67
+ }
61
68
  bindings.push(newBinding);
62
69
  mustUpdatePolicy = true;
63
70
  } else {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/events",
3
3
  "type": "module",
4
- "version": "0.0.52",
4
+ "version": "0.0.55",
5
5
  "description": "E-Com Plus Cloud Commerce app events",
6
6
  "main": "lib/index.js",
7
7
  "exports": {
@@ -1,3 +1,4 @@
1
+ import type { AppEventsTopic } from '@cloudcommerce/types';
1
2
  declare const _default: {
2
3
  get(): {
3
4
  hello: string;
@@ -13,6 +14,10 @@ declare const _default: {
13
14
  discounts: {
14
15
  appId: number;
15
16
  };
17
+ tinyErp: {
18
+ appId: number;
19
+ events: AppEventsTopic[];
20
+ };
16
21
  };
17
22
  };
18
23
  set(config: any): void;
@@ -23,6 +23,14 @@ const self = {
23
23
  discounts: {
24
24
  appId: 1252,
25
25
  },
26
+ tinyErp: {
27
+ appId: 105922,
28
+ events: [
29
+ 'orders-anyStatusSet',
30
+ 'products-new',
31
+ 'products-priceSet',
32
+ ],
33
+ },
26
34
  },
27
35
  },
28
36
  };
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,aAAa;AACb,MAAM,GAAG,GAA8B,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,EAAE,GAAG,CAAC;OAC/E,CAAC,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC;OACtC,EAAE,CAAC;AAER,MAAM,SAAS,GAAG,SAAS,EAAE,CAAC;AAE9B,MAAM,IAAI,GAAG;IACX,QAAQ,EAAE;QACR,KAAK,EAAE,8BAA8B;QACrC,IAAI,EAAE,GAAG,CAAC,SAAS,IAAI,YAAY;QACnC,QAAQ,EAAE,GAAG,CAAC,aAAa,IAAI,gBAAgB;QAC/C,cAAc,EAAE,GAAG,CAAC,oBAAoB,IAAI,uBAAuB;QACnE,WAAW,EAAE,GAAG,CAAC,iBAAiB,IAAI,oBAAoB;QAC1D,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC;QAClC,oBAAoB,EAAE;YACpB,MAAM,EAAE,GAAG,CAAC,aAAa,IAAI,aAAa;SAC3C;QACD,IAAI,EAAE;YACJ,SAAS,EAAE;gBACT,KAAK,EAAE,IAAI;aACZ;SACF;KACF;CACF,CAAC;AAEF,eAAe;IACb,GAAG;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IACD,GAAG,CAAC,MAAM;QACR,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACjD,IAAI,MAAM,CAAC,OAAO,EAAE;YAClB,GAAG,CAAC,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC;SACpC;IACH,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,aAAa;AACb,MAAM,GAAG,GAA8B,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,EAAE,GAAG,CAAC;OAC/E,CAAC,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC;OACtC,EAAE,CAAC;AAER,MAAM,SAAS,GAAG,SAAS,EAAE,CAAC;AAE9B,MAAM,IAAI,GAAG;IACX,QAAQ,EAAE;QACR,KAAK,EAAE,8BAA8B;QACrC,IAAI,EAAE,GAAG,CAAC,SAAS,IAAI,YAAY;QACnC,QAAQ,EAAE,GAAG,CAAC,aAAa,IAAI,gBAAgB;QAC/C,cAAc,EAAE,GAAG,CAAC,oBAAoB,IAAI,uBAAuB;QACnE,WAAW,EAAE,GAAG,CAAC,iBAAiB,IAAI,oBAAoB;QAC1D,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC;QAClC,oBAAoB,EAAE;YACpB,MAAM,EAAE,GAAG,CAAC,aAAa,IAAI,aAAa;SAC3C;QACD,IAAI,EAAE;YACJ,SAAS,EAAE;gBACT,KAAK,EAAE,IAAI;aACZ;YACD,OAAO,EAAE;gBACP,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE;oBACN,qBAAqB;oBACrB,cAAc;oBACd,mBAAmB;iBACA;aACtB;SACF;KACF;CACF,CAAC;AAEF,eAAe;IACb,GAAG;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IACD,GAAG,CAAC,MAAM;QACR,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACjD,IAAI,MAAM,CAAC,OAAO,EAAE;YAClB,GAAG,CAAC,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC;SACpC;IACH,CAAC;CACF,CAAC"}
@@ -1,28 +1,105 @@
1
- // eslint-disable-next-line import/no-unresolved
2
- import { getFirestore } from 'firebase-admin/firestore';
3
1
  import logger from 'firebase-functions/lib/logger';
4
2
  import api from '@cloudcommerce/api';
5
3
  import getEnv from '../env.js';
4
+ import config from '../config.js';
5
+
6
+ const parseEventsTopic = (eventsTopic) => {
7
+ const [resource, eventName] = eventsTopic.split('-');
8
+ const params = {};
9
+ const bodySet = {};
10
+ if (eventName === 'new') {
11
+ params.action = 'create';
12
+ } else {
13
+ switch (resource) {
14
+ case 'orders':
15
+ switch (eventName) {
16
+ case 'paid':
17
+ bodySet['financial_status.current'] = 'paid';
18
+ break;
19
+ case 'readyForShipping':
20
+ bodySet['fulfillment_status.current'] = 'ready_for_shipping';
21
+ break;
22
+ case 'shipped':
23
+ case 'delivered':
24
+ bodySet['fulfillment_status.current'] = eventName;
25
+ break;
26
+ case 'cancelled':
27
+ bodySet.status = 'cancelled';
28
+ break;
29
+ default: // anyStatusSet
30
+ params.modified_fields = [
31
+ 'financial_status',
32
+ 'fulfillment_status',
33
+ 'status',
34
+ ];
35
+ }
36
+ break;
37
+ case 'products':
38
+ params.modified_fields = eventName === 'priceSet'
39
+ ? ['price', 'variations.price']
40
+ : ['quantity']; // quantitySet
41
+ break;
42
+ case 'carts':
43
+ params.modified_fields = ['customers']; // customerSet
44
+ break;
45
+ default:
46
+ }
47
+ }
48
+ Object.keys(bodySet).forEach((field) => {
49
+ params[`body.${field}`] = bodySet[field];
50
+ });
51
+ return { resource, params };
52
+ };
6
53
 
7
54
  export default async () => {
55
+ const { apps } = config.get();
8
56
  const { apiAuth } = getEnv();
9
- const eventsSubs = await getFirestore().collection('eventsSubs').get();
57
+ const subscribersApps = [];
58
+ Object.keys(apps).forEach((appName) => {
59
+ const appObj = apps[appName];
60
+ if (appObj.events && appObj.events.length) {
61
+ subscribersApps.push(appObj);
62
+ }
63
+ });
64
+ const activeSubscribersApps = (await api.get('applications', {
65
+ params: {
66
+ state: 'active',
67
+ app_id: subscribersApps.map(({ appId }) => appId),
68
+ fields: 'app_id',
69
+ },
70
+ })).data.result;
71
+ logger.info({ activeSubscribersApps });
10
72
  const listenedEvents = [];
11
- eventsSubs.forEach((doc) => {
12
- const eventSub = doc.data();
13
- if (!listenedEvents.includes(eventSub.event)) {
14
- listenedEvents.push(eventSub.event);
73
+ subscribersApps.forEach(({ appId, events }) => {
74
+ if (activeSubscribersApps.find((app) => app.app_id === appId)) {
75
+ events.forEach((eventsTopic) => {
76
+ listenedEvents.push(eventsTopic);
77
+ });
15
78
  }
16
79
  });
17
80
  logger.info({ listenedEvents });
18
- [
19
- 'orders',
20
- 'products',
21
- 'carts',
22
- ].forEach(async (resource) => {
23
- const { data: { result } } = await api
24
- .get(`events/${resource}`, apiAuth);
25
- logger.info(`${resource} events: `, result);
81
+ listenedEvents.forEach(async (eventsTopic) => {
82
+ const { resource, params } = parseEventsTopic(eventsTopic);
83
+ let { data: { result } } = await api.get(`events/${resource}`, {
84
+ ...apiAuth,
85
+ params,
86
+ });
87
+ /*
88
+ global.api_events_middleware = async (
89
+ resource: string,
90
+ result: EventsResult,
91
+ }) => {
92
+ if (resource === 'orders') {
93
+ await axios.port(url, result);
94
+ }
95
+ return result;
96
+ };
97
+ */
98
+ const middleware = global.api_events_middleware;
99
+ if (typeof middleware === 'function') {
100
+ result = await middleware(resource, result);
101
+ }
102
+ logger.info(`> '${eventsTopic}' events: `, result);
26
103
  });
27
104
  return true;
28
105
  };
@@ -1 +1 @@
1
- {"version":3,"file":"check-store-events.js","sourceRoot":"","sources":["../../src/handlers/check-store-events.ts"],"names":[],"mappings":"AACA,gDAAgD;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,MAAM,MAAM,+BAA+B,CAAC;AACnD,OAAO,GAAG,MAAM,oBAAoB,CAAC;AACrC,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,eAAe,KAAK,IAAI,EAAE;IACxB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAAC;IAC7B,MAAM,UAAU,GAAG,MAAM,YAAY,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,CAAC;IACvE,MAAM,cAAc,GAAwB,EAAE,CAAC;IAC/C,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QACzB,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,EAAc,CAAC;QACxC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;YAC5C,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SACrC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;IAChC;QACE,QAAQ;QACR,UAAU;QACV,OAAO;KACR,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;QAC3B,MAAM,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,GAAG;aACnC,GAAG,CAAC,UAAU,QAAoB,EAAE,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,WAAW,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
1
+ {"version":3,"file":"check-store-events.js","sourceRoot":"","sources":["../../src/handlers/check-store-events.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,+BAA+B,CAAC;AACnD,OAAO,GAAkB,MAAM,oBAAoB,CAAC;AACpD,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,MAAM,MAAM,WAAW,CAAC;AAE/B,MAAM,gBAAgB,GAAG,CAAC,WAA2B,EAAE,EAAE;IACvD,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrD,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,IAAI,SAAS,KAAK,KAAK,EAAE;QACvB,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC;KAC1B;SAAM;QACL,QAAQ,QAAQ,EAAE;YAChB,KAAK,QAAQ;gBACX,QAAQ,SAAS,EAAE;oBACjB,KAAK,MAAM;wBACT,OAAO,CAAC,0BAA0B,CAAC,GAAG,MAAM,CAAC;wBAC7C,MAAM;oBACR,KAAK,kBAAkB;wBACrB,OAAO,CAAC,4BAA4B,CAAC,GAAG,oBAAoB,CAAC;wBAC7D,MAAM;oBACR,KAAK,SAAS,CAAC;oBACf,KAAK,WAAW;wBACd,OAAO,CAAC,4BAA4B,CAAC,GAAG,SAAS,CAAC;wBAClD,MAAM;oBACR,KAAK,WAAW;wBACd,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC;wBAC7B,MAAM;oBACR,SAAS,eAAe;wBACtB,MAAM,CAAC,eAAe,GAAG;4BACvB,kBAAkB;4BAClB,oBAAoB;4BACpB,QAAQ;yBACT,CAAC;iBACL;gBACD,MAAM;YACR,KAAK,UAAU;gBACb,MAAM,CAAC,eAAe,GAAG,SAAS,KAAK,UAAU;oBAC/C,CAAC,CAAC,CAAC,OAAO,EAAE,kBAAkB,CAAC;oBAC/B,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,cAAc;gBAChC,MAAM;YACR,KAAK,OAAO;gBACV,MAAM,CAAC,eAAe,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc;gBACtD,MAAM;YACR,QAAQ;SACT;KACF;IACD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QACrC,MAAM,CAAC,QAAQ,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAGxB,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,KAAK,IAAI,EAAE;IACxB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC;IAC9B,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAAC;IAC7B,MAAM,eAAe,GAAuD,EAAE,CAAC;IAC/E,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7B,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;YACzC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SAC9B;IACH,CAAC,CAAC,CAAC;IACH,MAAM,qBAAqB,GAAG,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE;QAC3D,MAAM,EAAE;YACN,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC;YACjD,MAAM,EAAE,QAAQ;SACjB;KACF,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;IAChB,MAAM,CAAC,IAAI,CAAC,EAAE,qBAAqB,EAAE,CAAC,CAAC;IACvC,MAAM,cAAc,GAAqB,EAAE,CAAC;IAC5C,eAAe,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE;QAC5C,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,KAAK,CAAC,EAAE;YAC7D,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE;gBAC7B,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;SACJ;IACH,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;IAChC,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE;QAC3C,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAC3D,IAAI,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,UAAU,QAAQ,EAAE,EAAE;YAC7D,GAAG,OAAO;YACV,MAAM;SACP,CAAC,CAAC;QACH;;;;;;;;;;UAUE;QACF,MAAM,UAAU,GAAG,MAAM,CAAC,qBAAqB,CAAC;QAChD,IAAI,OAAO,UAAU,KAAK,UAAU,EAAE;YACpC,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;SAC7C;QACD,MAAM,CAAC,IAAI,CAAC,MAAM,WAAW,YAAY,EAAE,MAAM,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/firebase",
3
3
  "type": "module",
4
- "version": "0.0.52",
4
+ "version": "0.0.55",
5
5
  "description": "E-Com Plus Cloud Commerce on Firebase",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -1,3 +1,4 @@
1
+ import type { AppEventsTopic } from '@cloudcommerce/types';
1
2
  import Deepmerge from '@fastify/deepmerge';
2
3
  import {
3
4
  DEFAULT_LANG,
@@ -28,6 +29,14 @@ const self = {
28
29
  discounts: {
29
30
  appId: 1252,
30
31
  },
32
+ tinyErp: {
33
+ appId: 105922,
34
+ events: [
35
+ 'orders-anyStatusSet',
36
+ 'products-new',
37
+ 'products-priceSet',
38
+ ] as AppEventsTopic[],
39
+ },
31
40
  },
32
41
  },
33
42
  };
@@ -1,29 +1,109 @@
1
- import type { EventSub } from '../types';
2
- // eslint-disable-next-line import/no-unresolved
3
- import { getFirestore } from 'firebase-admin/firestore';
1
+ import type { Resource, AppEventsTopic } from '@cloudcommerce/types';
4
2
  import logger from 'firebase-functions/lib/logger';
5
- import api from '@cloudcommerce/api';
3
+ import api, { ApiConfig } from '@cloudcommerce/api';
6
4
  import getEnv from '../env';
5
+ import config from '../config';
6
+
7
+ const parseEventsTopic = (eventsTopic: AppEventsTopic) => {
8
+ const [resource, eventName] = eventsTopic.split('-');
9
+ const params: ApiConfig['params'] = {};
10
+ const bodySet: { [key: string]: any } = {};
11
+ if (eventName === 'new') {
12
+ params.action = 'create';
13
+ } else {
14
+ switch (resource) {
15
+ case 'orders':
16
+ switch (eventName) {
17
+ case 'paid':
18
+ bodySet['financial_status.current'] = 'paid';
19
+ break;
20
+ case 'readyForShipping':
21
+ bodySet['fulfillment_status.current'] = 'ready_for_shipping';
22
+ break;
23
+ case 'shipped':
24
+ case 'delivered':
25
+ bodySet['fulfillment_status.current'] = eventName;
26
+ break;
27
+ case 'cancelled':
28
+ bodySet.status = 'cancelled';
29
+ break;
30
+ default: // anyStatusSet
31
+ params.modified_fields = [
32
+ 'financial_status',
33
+ 'fulfillment_status',
34
+ 'status',
35
+ ];
36
+ }
37
+ break;
38
+ case 'products':
39
+ params.modified_fields = eventName === 'priceSet'
40
+ ? ['price', 'variations.price']
41
+ : ['quantity']; // quantitySet
42
+ break;
43
+ case 'carts':
44
+ params.modified_fields = ['customers']; // customerSet
45
+ break;
46
+ default:
47
+ }
48
+ }
49
+ Object.keys(bodySet).forEach((field) => {
50
+ params[`body.${field}`] = bodySet[field];
51
+ });
52
+ return { resource, params } as {
53
+ resource: Resource;
54
+ params: ApiConfig['params'];
55
+ };
56
+ };
7
57
 
8
58
  export default async () => {
59
+ const { apps } = config.get();
9
60
  const { apiAuth } = getEnv();
10
- const eventsSubs = await getFirestore().collection('eventsSubs').get();
11
- const listenedEvents: EventSub['event'][] = [];
12
- eventsSubs.forEach((doc) => {
13
- const eventSub = doc.data() as EventSub;
14
- if (!listenedEvents.includes(eventSub.event)) {
15
- listenedEvents.push(eventSub.event);
61
+ const subscribersApps: Array<{ appId: number, events: AppEventsTopic[] }> = [];
62
+ Object.keys(apps).forEach((appName) => {
63
+ const appObj = apps[appName];
64
+ if (appObj.events && appObj.events.length) {
65
+ subscribersApps.push(appObj);
66
+ }
67
+ });
68
+ const activeSubscribersApps = (await api.get('applications', {
69
+ params: {
70
+ state: 'active',
71
+ app_id: subscribersApps.map(({ appId }) => appId),
72
+ fields: 'app_id',
73
+ },
74
+ })).data.result;
75
+ logger.info({ activeSubscribersApps });
76
+ const listenedEvents: AppEventsTopic[] = [];
77
+ subscribersApps.forEach(({ appId, events }) => {
78
+ if (activeSubscribersApps.find((app) => app.app_id === appId)) {
79
+ events.forEach((eventsTopic) => {
80
+ listenedEvents.push(eventsTopic);
81
+ });
16
82
  }
17
83
  });
18
84
  logger.info({ listenedEvents });
19
- [
20
- 'orders',
21
- 'products',
22
- 'carts',
23
- ].forEach(async (resource) => {
24
- const { data: { result } } = await api
25
- .get(`events/${resource as 'orders'}`, apiAuth);
26
- logger.info(`${resource} events: `, result);
85
+ listenedEvents.forEach(async (eventsTopic) => {
86
+ const { resource, params } = parseEventsTopic(eventsTopic);
87
+ let { data: { result } } = await api.get(`events/${resource}`, {
88
+ ...apiAuth,
89
+ params,
90
+ });
91
+ /*
92
+ global.api_events_middleware = async (
93
+ resource: string,
94
+ result: EventsResult,
95
+ }) => {
96
+ if (resource === 'orders') {
97
+ await axios.port(url, result);
98
+ }
99
+ return result;
100
+ };
101
+ */
102
+ const middleware = global.api_events_middleware;
103
+ if (typeof middleware === 'function') {
104
+ result = await middleware(resource, result);
105
+ }
106
+ logger.info(`> '${eventsTopic}' events: `, result);
27
107
  });
28
108
  return true;
29
109
  };