@sommpicks/sommpicks-shopify 24.12.0

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 (71) hide show
  1. package/Logger.ts +18 -0
  2. package/README.md +258 -0
  3. package/addTypings.sh +2 -0
  4. package/bitbucket-pipelines.yml +38 -0
  5. package/index.ts +132 -0
  6. package/package.json +57 -0
  7. package/publish.sh +20 -0
  8. package/services/CacheWrapper.ts +30 -0
  9. package/services/CountryCodeService.ts +507 -0
  10. package/shopify/ShopifyAppService.ts +109 -0
  11. package/shopify/ShopifyAssetService.ts +20 -0
  12. package/shopify/ShopifyBillingService.ts +73 -0
  13. package/shopify/ShopifyCartTrasnformationService.ts +207 -0
  14. package/shopify/ShopifyCollectionService.ts +523 -0
  15. package/shopify/ShopifyCustomerService.ts +472 -0
  16. package/shopify/ShopifyDeliveryCustomisationService.ts +220 -0
  17. package/shopify/ShopifyDiscountService.ts +131 -0
  18. package/shopify/ShopifyDraftOrderService.ts +125 -0
  19. package/shopify/ShopifyFulfillmentService.ts +41 -0
  20. package/shopify/ShopifyFunctionsProductDiscountsService.ts +166 -0
  21. package/shopify/ShopifyInventoryService.ts +415 -0
  22. package/shopify/ShopifyLocationService.ts +29 -0
  23. package/shopify/ShopifyOrderRefundsService.ts +138 -0
  24. package/shopify/ShopifyOrderRiskService.ts +19 -0
  25. package/shopify/ShopifyOrderService.ts +1143 -0
  26. package/shopify/ShopifyPageService.ts +62 -0
  27. package/shopify/ShopifyProductService.ts +772 -0
  28. package/shopify/ShopifyShippingZonesService.ts +37 -0
  29. package/shopify/ShopifyShopService.ts +101 -0
  30. package/shopify/ShopifyTemplateService.ts +30 -0
  31. package/shopify/ShopifyThemeService.ts +33 -0
  32. package/shopify/ShopifyUtils.ts +56 -0
  33. package/shopify/ShopifyWebhookService.ts +110 -0
  34. package/shopify/base/APIVersion.ts +4 -0
  35. package/shopify/base/AbstractService.ts +152 -0
  36. package/shopify/base/ErrorHelper.ts +24 -0
  37. package/shopify/errors/InspiraShopifyCustomError.ts +7 -0
  38. package/shopify/errors/InspiraShopifyError.ts +15 -0
  39. package/shopify/errors/InspiraShopifyUnableToReserveInventoryError.ts +7 -0
  40. package/shopify/helpers/ShopifyProductServiceHelper.ts +450 -0
  41. package/shopify/product/ShopifyProductCountService.ts +110 -0
  42. package/shopify/product/ShopifyProductListService.ts +333 -0
  43. package/shopify/product/ShopifyProductMetafieldsService.ts +405 -0
  44. package/shopify/product/ShopifyProductPublicationsService.ts +112 -0
  45. package/shopify/product/ShopifyVariantService.ts +584 -0
  46. package/shopify/router/ShopifyMandatoryRouter.ts +37 -0
  47. package/shopify/router/ShopifyRouter.ts +85 -0
  48. package/shopify/router/ShopifyRouterBis.ts +85 -0
  49. package/shopify/router/ShopifyRouterBisBis.ts +85 -0
  50. package/shopify/router/ShopifyRouterBisBisBis.ts +85 -0
  51. package/shopify/router/ShopifyRouterBisBisBisBis.ts +85 -0
  52. package/shopify/router/WebhookSkipMiddleware.ts +73 -0
  53. package/shopify/router/services/CryptoService.ts +26 -0
  54. package/shopify/router/services/HmacValidator.ts +36 -0
  55. package/shopify/router/services/OauthService.ts +17 -0
  56. package/shopify/router/services/RestUtils.ts +13 -0
  57. package/shopify/router/services/rateLimiter/MemoryStores.ts +46 -0
  58. package/shopify/router/services/rateLimiter/StoreRateLimiter.ts +46 -0
  59. package/test/README.md +223 -0
  60. package/test/router/ShopifyRouter.test.ts +71 -0
  61. package/test/router/WebhookSkipMiddleware.test.ts +86 -0
  62. package/test/router/services/HmacValidator.test.ts +24 -0
  63. package/test/router/services/RestUtils.test.ts +13 -0
  64. package/test/router/services/rateLimiter/StoreRateLimiter.test.ts +62 -0
  65. package/test/services/CacheWrapper.test.ts +30 -0
  66. package/test/shopify/ShopifyOrderService.test.ts +29 -0
  67. package/test/shopify/ShopifyProductService.test.ts +118 -0
  68. package/test/shopify/ShopifyWebhookService.test.ts +105 -0
  69. package/tsconfig.json +10 -0
  70. package/typings/axios.d.ts +8 -0
  71. package/typings/index.d.ts +1682 -0
@@ -0,0 +1,118 @@
1
+ import { ShopifyUtils } from '../../shopify/ShopifyUtils';
2
+ import { ShopifyProductService } from '../../shopify/ShopifyProductService';
3
+
4
+
5
+ test('Should throw an exception if a post call fails', async () => {
6
+ const mockPost = jest.fn(() => {
7
+ return new Promise((resolve) => resolve({ data: { errors: [{ message: 'errormessage' } ] }}));
8
+ });
9
+ const mockAxiosInstance = { post: mockPost };
10
+
11
+ ShopifyUtils.getAxiosInstance = (): any => mockAxiosInstance;
12
+
13
+ const shopifyProductService = new ShopifyProductService(ShopifyUtils.getAxiosInstance('shop', 'token', 'key', null, null));
14
+
15
+ const metafields = getMetafields(123, 1234);
16
+ let thrown = false;
17
+
18
+ try {
19
+ await shopifyProductService.metafields.addMetafieldForProducts(metafields);
20
+ } catch(e) {
21
+ thrown = true;
22
+ }
23
+ expect(thrown).toBeTruthy();
24
+ expect(mockPost).toHaveBeenCalledTimes(1);
25
+ });
26
+
27
+ test('Update the existing metafields of the sent products', async () => {
28
+ let index = 0;
29
+ const queries: string[] = [];
30
+ const mockPost = jest.fn((url, query) => {
31
+ index ++;
32
+ if(index === 1){
33
+ const response = getMetafieldsRepsonse(5, 1234);
34
+ response['product1238'] = { metafield1: { value: 'value_diff', type: 'single_line_text_field', namespace: 'name', key: 'key', id: 'gid://shopify/Metafield/1211'}};
35
+ return new Promise((resolve) => resolve({ data: { data: response } }));
36
+ } else if (index === 2) {
37
+ queries.push(query.query);
38
+ const response = getMetafieldsUpdateRepsonse(5, 1234);
39
+ return new Promise((resolve) => resolve({ data: { data: response } }));
40
+ } else if (index === 3) {
41
+ const response = getMetafieldsRepsonse(5, 1239);
42
+ response['product1241'] = { metafield1: { value: 'value_diff', type: 'single_line_text_field', namespace: 'name', key: 'key', id: 'gid://shopify/Metafield/1291'}};
43
+ return new Promise((resolve) => resolve({ data: { data: response } }));
44
+ } else if (index === 4) {
45
+ queries.push(query.query);
46
+ const response = getMetafieldsUpdateRepsonse(5, 1239);
47
+ return new Promise((resolve) => resolve({ data: { data: response } }));
48
+ } else if (index === 5) {
49
+ const response = getMetafieldsRepsonse(5, 1244);
50
+ return new Promise((resolve) => resolve({ data: { data: response } }));
51
+ } else if (index === 6) {
52
+ queries.push(query.query);
53
+ const response = getMetafieldsUpdateRepsonse(5, 1244);
54
+ return new Promise((resolve) => resolve({ data: { data: response } }));
55
+ } else if (index === 7) {
56
+ const response = getMetafieldsRepsonse(5, 1249);
57
+ return new Promise((resolve) => resolve({ data: { data: response } }));
58
+ } else if (index === 8) {
59
+ queries.push(query.query);
60
+ const response = getMetafieldsUpdateRepsonse(5, 1249);
61
+ return new Promise((resolve) => resolve({ data: { data: response } }));
62
+ } else if (index === 9) {
63
+ const response = getMetafieldsRepsonse(5, 1254);
64
+ return new Promise((resolve) => resolve({ data: { data: response } }));
65
+ } else if (index === 10) {
66
+ queries.push(query.query);
67
+ const response = getMetafieldsUpdateRepsonse(5, 1254);
68
+ return new Promise((resolve) => resolve({ data: { data: response } }));
69
+ } else if (index === 11) {
70
+ const response = getMetafieldsRepsonse(3, 1259);
71
+ return new Promise((resolve) => resolve({ data: { data: response } }));
72
+ } else if (index === 12) {
73
+ queries.push(query.query);
74
+ const response = getMetafieldsUpdateRepsonse(3, 1259);
75
+ return new Promise((resolve) => resolve({ data: { data: response } }));
76
+ }
77
+ });
78
+ const mockAxiosInstance = { post: mockPost };
79
+
80
+ ShopifyUtils.getAxiosInstance = (): any => mockAxiosInstance;
81
+
82
+ const shopifyProductService = new ShopifyProductService(ShopifyUtils.getAxiosInstance('shop', 'token', 'key', null, null));
83
+
84
+ const metafields = getMetafields(28, 1234);
85
+
86
+ const productIds = await shopifyProductService.metafields.addMetafieldForProducts(metafields);
87
+
88
+ expect(mockPost).toHaveBeenCalledTimes(12);
89
+ expect(productIds.length).toBe(28);
90
+ expect(productIds[0]).toBe(1234);
91
+ expect(productIds[27]).toBe(1261);
92
+ expect(queries[0]).toContain('gid://shopify/Metafield/1211');
93
+ expect(queries[1]).toContain('gid://shopify/Metafield/1291');
94
+ });
95
+
96
+ const getMetafields = (num: number, initialId: number): IMetafieldOfProduct[] => {
97
+ const metafields: IMetafieldOfProduct[] = [];
98
+ [ ...Array(num).keys()].forEach((i) => {
99
+ metafields.push({ product_id: initialId + i, value: `value_${i}`, type: 'single_line_text_field', namespace: 'name', key: 'key' });
100
+ });
101
+ return metafields;
102
+ };
103
+
104
+ const getMetafieldsRepsonse = (num: number, initialId: number): any => {
105
+ const metafieldsResponse = {};
106
+ [ ...Array(num).keys()].forEach((i) => {
107
+ metafieldsResponse[`product${initialId + i }`] = { metafield1: null };
108
+ });
109
+ return metafieldsResponse;
110
+ };
111
+
112
+ const getMetafieldsUpdateRepsonse = (num: number, initialId: number): any => {
113
+ const metafieldsUpdateResponse = {};
114
+ [ ...Array(num).keys()].forEach((i) => {
115
+ metafieldsUpdateResponse[`product${initialId + i }`] = { product: { id: `gid://shopify/Product/${initialId + i}` } };
116
+ });
117
+ return metafieldsUpdateResponse;
118
+ };
@@ -0,0 +1,105 @@
1
+ import { Logger } from '../../Logger';
2
+ import { ShopifyUtils } from '../../shopify/ShopifyUtils';
3
+ import { ShopifyWebhookService } from '../../shopify/ShopifyWebhookService';
4
+ import InspiraShopifyError from '../../shopify/errors/InspiraShopifyError';
5
+
6
+ beforeEach(async() => {
7
+ jest.clearAllMocks();
8
+ Logger.info = console.log;
9
+ Logger.error = console.error;
10
+ });
11
+
12
+ describe('deleteAll should,', () => {
13
+ test('Delete all existing webhooks', async () => {
14
+ const mockAxiosInstance = {
15
+ get: jest.fn(() => {
16
+ return new Promise<any>((resolve) => {
17
+ resolve({
18
+ data: {
19
+ webhooks: [{ topic: 'orders/create', id: 1, address: 'some callaback url', format: 'json' },
20
+ { topic: 'orders/delete', id: 2, address: 'some callaback url', format: 'json' }]
21
+ }
22
+ });
23
+ });
24
+ }),
25
+ delete: jest.fn()
26
+ };
27
+
28
+ const shopifyWebhookService = new ShopifyWebhookService(mockAxiosInstance as any);
29
+ await shopifyWebhookService.deleteAll();
30
+
31
+ expect(mockAxiosInstance.get).toHaveBeenCalledWith('/webhooks.json');
32
+ expect(mockAxiosInstance.delete).toHaveBeenCalledTimes(2);
33
+ });
34
+ });
35
+
36
+ describe('create should,', () => {
37
+ test('throw error when API fails', async () => {
38
+ const mockAxiosInstance = {
39
+ post: jest.fn().mockRejectedValue(new InspiraShopifyError({ message: 'API error'}))
40
+ };
41
+
42
+ const shopifyWebhookService = new ShopifyWebhookService(mockAxiosInstance as any);
43
+ const createwebHookFunc = async () => { await shopifyWebhookService.create({ address: 'some_url', format: 'json', topic: 'collections/create' }); };
44
+ expect(createwebHookFunc).rejects.toThrow('\\"API error\\"');
45
+
46
+ });
47
+ });
48
+
49
+ describe('createStoreWebHooks should,', () => {
50
+ test('create all webhooks when no webhooks are already in place', async () => {
51
+ const mockAxiosInstance = {
52
+ get: jest.fn().mockResolvedValue({ data: { webhooks: [] }}),
53
+ post: jest.fn().mockResolvedValue({ data: { webhooks: { id: 121123 }}})
54
+ };
55
+
56
+ ShopifyUtils.getAxiosInstance = (): any => mockAxiosInstance;
57
+
58
+ const shopifyWebhookService = new ShopifyWebhookService(mockAxiosInstance as any);
59
+ await shopifyWebhookService.createStoreWebHooks('baseURL', ['collections/create', 'customers/delete', 'inventory_items/update']);
60
+
61
+ expect(mockAxiosInstance.get).toHaveBeenCalledTimes(1);
62
+ expect(mockAxiosInstance.post).toHaveBeenCalledTimes(3);
63
+ expect(mockAxiosInstance.post).toHaveBeenCalledWith('/webhooks.json', { webhook: { address: 'baseURL/collections/create', format: 'json', topic: 'collections/create' } });
64
+ expect(mockAxiosInstance.post).toHaveBeenCalledWith('/webhooks.json', { webhook: { address: 'baseURL/customers/delete', format: 'json', topic: 'customers/delete' } });
65
+ expect(mockAxiosInstance.post).toHaveBeenCalledWith('/webhooks.json', { webhook: { address: 'baseURL/inventory_items/update', format: 'json', topic: 'inventory_items/update' } });
66
+ });
67
+
68
+ test('create only webhooks that are not already in place', async () => {
69
+ const mockAxiosInstance = {
70
+ get: jest.fn().mockResolvedValue({ data: { webhooks: [{ topic: 'customers/delete', id: 1, address: 'some callaback url', format: 'json' }] }}),
71
+ post: jest.fn().mockResolvedValue({ data: { webhooks: { id: 121123 }}})
72
+ };
73
+
74
+ ShopifyUtils.getAxiosInstance = (): any => mockAxiosInstance;
75
+
76
+ const shopifyWebhookService = new ShopifyWebhookService(mockAxiosInstance as any);
77
+ await shopifyWebhookService.createStoreWebHooks('baseURL', ['collections/create', 'customers/delete', 'inventory_items/update']);
78
+
79
+ expect(mockAxiosInstance.get).toHaveBeenCalledTimes(1);
80
+ expect(mockAxiosInstance.post).toHaveBeenCalledTimes(2);
81
+ expect(mockAxiosInstance.post).toHaveBeenCalledWith('/webhooks.json', { webhook: { address: 'baseURL/collections/create', format: 'json', topic: 'collections/create' } });
82
+ expect(mockAxiosInstance.post).toHaveBeenCalledWith('/webhooks.json', { webhook: { address: 'baseURL/inventory_items/update', format: 'json', topic: 'inventory_items/update' } });
83
+ });
84
+
85
+ test('return error when a webhooks fail to be created', async () => {
86
+ const mockAxiosInstance = {
87
+ get: jest.fn().mockResolvedValue({ data: { webhooks: [] }}),
88
+ post: jest.fn().mockRejectedValueOnce('API error').mockResolvedValue({ data: { webhooks: { id: 121123 }}})
89
+ };
90
+
91
+ ShopifyUtils.getAxiosInstance = (): any => mockAxiosInstance;
92
+
93
+ const shopifyWebhookService = new ShopifyWebhookService(mockAxiosInstance as any);
94
+ let error = '';
95
+ try {
96
+ await shopifyWebhookService.createStoreWebHooks('baseURL', ['collections/create', 'customers/delete', 'inventory_items/update']);
97
+ } catch(e) {
98
+ error = e.message;
99
+ }
100
+
101
+ expect(mockAxiosInstance.get).toHaveBeenCalledTimes(1);
102
+ expect(mockAxiosInstance.post).toHaveBeenCalledTimes(3);
103
+ expect(error).toBe('"\\"1 webhooks have not been created with errors \\\\\\"API error\\\\\\"\\""');
104
+ });
105
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "outDir": "./dist/",
4
+ "target": "es6",
5
+ "module": "commonjs",
6
+ "sourceMap": true,
7
+ "typeRoots": ["./node_modules/@types", "./typings"],
8
+ "declaration": true
9
+ }
10
+ }
@@ -0,0 +1,8 @@
1
+ import 'axios';
2
+
3
+ declare module 'axios' {
4
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
+ export interface AxiosRequestConfig<D = any> {
6
+ query_cost?: number;
7
+ }
8
+ }