@foxtware/mineral 0.1.9 → 0.1.10

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 (157) hide show
  1. package/.creds.yml.sample +27 -6
  2. package/AGENTS.md +6 -1
  3. package/README.md +1 -1
  4. package/api/bleckmann/.gitkeep +0 -0
  5. package/api/bleckmann/bleckmann.constants.js +8 -0
  6. package/api/bleckmann/bleckmann.utils.js +32 -0
  7. package/api/bleckmann/bleckmannAsnGet.js +52 -0
  8. package/api/bleckmann/bleckmannAsnsGet.js +65 -0
  9. package/api/constants.js +2 -1
  10. package/api/github/github.constants.js +11 -0
  11. package/api/github/github.utils.js +65 -0
  12. package/api/github/githubGet.js +144 -0
  13. package/api/github/githubIssueCreate.js +71 -0
  14. package/api/github/githubIssueGet.js +65 -0
  15. package/api/github/githubIssueUpdate.js +71 -0
  16. package/api/github/githubIssuesGet.js +74 -0
  17. package/api/github/githubPullRequestCreate.js +74 -0
  18. package/api/github/githubPullRequestGet.js +65 -0
  19. package/api/github/githubPullRequestMerge.js +77 -0
  20. package/api/github/githubPullRequestUpdate.js +72 -0
  21. package/api/github/githubPullRequestsGet.js +74 -0
  22. package/api/github/githubRepoCreate.js +80 -0
  23. package/api/github/githubRepoDelete.js +60 -0
  24. package/api/github/githubRepoGet.js +61 -0
  25. package/api/github/githubRepoUpdate.js +68 -0
  26. package/api/github/githubReposGet.js +69 -0
  27. package/api/github/githubUserGet.js +64 -0
  28. package/api/linear/linear.utils.js +9 -14
  29. package/api/linear/linearIssuesGet.js +9 -9
  30. package/api/logiwa/logiwa.utils.js +27 -25
  31. package/api/logiwa/logiwaInventoriesGet.js +5 -3
  32. package/api/logiwa/logiwaOrderGet.js +4 -2
  33. package/api/logiwa/logiwaOrdersGet.js +5 -3
  34. package/api/logiwa/logiwaProductGet.js +4 -2
  35. package/api/logiwa/logiwaProductsGet.js +5 -3
  36. package/api/logiwa/logiwaReportGetAvailableToPromise.js +5 -3
  37. package/api/logiwa/logiwaReportGetInventoryCalculation.js +5 -3
  38. package/api/logiwa/logiwaReportGetInventorySnapshot.js +5 -3
  39. package/api/logiwa/logiwaReportGetTotalInventory.js +5 -3
  40. package/api/logiwa/logiwaWebhookStatusGet.js +4 -2
  41. package/api/logiwa/logiwaWebhookSubscribe.js +8 -6
  42. package/api/logiwa/logiwaWebhookUnsubscribe.js +4 -2
  43. package/api/logiwa/logiwaWebhooksGet.js +4 -2
  44. package/api/loop/loop.utils.js +9 -15
  45. package/api/loop/loopGet.js +20 -24
  46. package/api/loop/loopReturnGet.js +6 -6
  47. package/api/peoplevox/peoplevox.constants.js +1 -1
  48. package/api/peoplevox/peoplevox.sessions.js +0 -33
  49. package/api/peoplevox/peoplevox.utils.js +63 -19
  50. package/api/peoplevox/peoplevoxGetSingle.js +7 -5
  51. package/api/peoplevox/peoplevoxOrderEdit.js +7 -5
  52. package/api/peoplevox/peoplevoxReportGet.js +11 -9
  53. package/api/pipelineSteps.js +49 -0
  54. package/api/shopify/shopify.utils.js +18 -24
  55. package/api/shopify/shopifyBulkMutationDo.js +211 -0
  56. package/api/shopify/shopifyBulkOperationGet.js +86 -0
  57. package/api/shopify/shopifyBulkOperationRunMutation.js +83 -0
  58. package/api/shopify/shopifyCheckoutAndAccountsConfigurationUpdate.js +79 -0
  59. package/api/shopify/shopifyCollectionGet.js +6 -6
  60. package/api/shopify/shopifyCustomerGet.js +6 -6
  61. package/api/shopify/shopifyGet.js +18 -13
  62. package/api/shopify/shopifyGetSingle.js +9 -9
  63. package/api/shopify/shopifyMutationDo.js +6 -6
  64. package/api/shopify/shopifyOrdersGet.js +11 -11
  65. package/api/shopify/shopifyStagedUploadCreate.js +96 -0
  66. package/api/shopify/shopifyTagsAddBulk.js +90 -0
  67. package/api/slack/slack.utils.js +10 -19
  68. package/api/slack/slackMessagePost.js +11 -11
  69. package/api/starshipit/starshipit.constants.js +8 -0
  70. package/api/starshipit/starshipit.utils.js +71 -0
  71. package/api/starshipit/starshipitAddressCreate.js +77 -0
  72. package/api/starshipit/starshipitAddressDelete.js +55 -0
  73. package/api/starshipit/starshipitAddressUpdate.js +69 -0
  74. package/api/starshipit/starshipitAddressesGet.js +52 -0
  75. package/api/starshipit/starshipitGet.js +146 -0
  76. package/api/starshipit/starshipitOrderDelete.js +102 -0
  77. package/api/starshipit/starshipitOrderGet.js +74 -0
  78. package/api/starshipit/starshipitOrderUpdate.js +109 -0
  79. package/api/starshipit/starshipitOrdersListShipped.js +64 -0
  80. package/api/starshipit/starshipitProductAdd.js +100 -0
  81. package/api/starshipit/starshipitProductsDelete.js +57 -0
  82. package/api/starshipit/starshipitProductsGet.js +49 -0
  83. package/api/starshipit/starshipitTrackingGet.js +69 -0
  84. package/api/stripe/_example.js +7 -7
  85. package/api/stripe/stripe.utils.js +9 -14
  86. package/api/stripe/stripeCardTokenCreate.js +11 -11
  87. package/api/stripe/stripeChargeCapture.js +11 -11
  88. package/api/stripe/stripeChargeCreate.js +11 -11
  89. package/api/stripe/stripeChargeGet.js +5 -5
  90. package/api/stripe/stripeChargesGet.js +6 -6
  91. package/api/stripe/stripeRefundCreate.js +10 -10
  92. package/api/stripe/stripeRefundGet.js +5 -5
  93. package/api/stripe/stripeRefundsGet.js +6 -6
  94. package/api/stripe/stripeTokenGet.js +5 -5
  95. package/api/stylearcade/stylearcade.constants.js +5 -0
  96. package/api/stylearcade/stylearcade.utils.js +40 -0
  97. package/api/stylearcade/stylearcadeDataGet.js +62 -0
  98. package/api/stylearcade/stylearcadeGet.js +133 -0
  99. package/api/stylearcade/stylearcadeMetadataGet.js +47 -0
  100. package/api/threeclicks/threeclicks.constants.js +8 -0
  101. package/api/threeclicks/threeclicks.utils.js +119 -0
  102. package/api/threeclicks/threeclicksCollectionsGet.js +40 -0
  103. package/api/threeclicks/threeclicksGet.js +138 -0
  104. package/api/threeclicks/threeclicksKeycodeCreate.js +55 -0
  105. package/api/threeclicks/threeclicksKeycodeGet.js +47 -0
  106. package/api/threeclicks/threeclicksPurchaseOrderCreate.js +34 -0
  107. package/api/threeclicks/threeclicksPurchaseOrderCreateBulk.js +34 -0
  108. package/api/threeclicks/threeclicksPurchaseOrderGet.js +33 -0
  109. package/api/threeclicks/threeclicksPurchaseOrderQuantitiesGet.js +33 -0
  110. package/api/threeclicks/threeclicksPurchaseOrderQuantityCreate.js +34 -0
  111. package/api/threeclicks/threeclicksPurchaseOrderQuantityCreateBulk.js +34 -0
  112. package/api/threeclicks/threeclicksPurchaseOrderQuantityUpdate.js +46 -0
  113. package/api/threeclicks/threeclicksPurchaseOrderQuantityUpdateBulk.js +46 -0
  114. package/api/threeclicks/threeclicksPurchaseOrderUpdate.js +35 -0
  115. package/api/threeclicks/threeclicksPurchaseOrdersGet.js +30 -0
  116. package/api/threeclicks/threeclicksShipmentCreate.js +34 -0
  117. package/api/threeclicks/threeclicksShipmentGet.js +33 -0
  118. package/api/threeclicks/threeclicksShipmentOrderAdd.js +38 -0
  119. package/api/threeclicks/threeclicksShipmentOrderQuantityCreate.js +35 -0
  120. package/api/threeclicks/threeclicksShipmentQuantityUpdate.js +35 -0
  121. package/api/threeclicks/threeclicksShipmentStatusUpdate.js +45 -0
  122. package/api/threeclicks/threeclicksShipmentsGet.js +52 -0
  123. package/api/threeclicks/threeclicksSignatureCalculate.js +73 -0
  124. package/api/threeclicks/threeclicksStyleCodesGet.js +46 -0
  125. package/api/threeclicks/threeclicksStyleColourCreate.js +47 -0
  126. package/api/threeclicks/threeclicksStyleColourDelete.js +34 -0
  127. package/api/threeclicks/threeclicksStyleCreate.js +44 -0
  128. package/api/threeclicks/threeclicksStyleDelete.js +33 -0
  129. package/api/threeclicks/threeclicksStyleGet.js +43 -0
  130. package/api/threeclicks/threeclicksStylePriceUpdate.js +70 -0
  131. package/api/threeclicks/threeclicksStyleUpdate.js +47 -0
  132. package/api/threeclicks/threeclicksStylesGet.js +45 -0
  133. package/api/utils.js +85 -117
  134. package/api/validators.js +1 -1
  135. package/api/workable/workable.utils.js +8 -13
  136. package/api/workable/workableGet.js +16 -11
  137. package/api/workable/workableJobCandidateCreate.js +10 -10
  138. package/api/workable/workableJobGet.js +5 -5
  139. package/api/workable/workableJobMembersGet.js +5 -5
  140. package/api/workable/workableJobStagesGet.js +5 -5
  141. package/api/yotpo/yotpo.utils.js +17 -22
  142. package/api/yotpo/yotpoCampaignsGet.js +6 -6
  143. package/api/yotpo/yotpoCustomerActionRecord.js +7 -7
  144. package/api/yotpo/yotpoCustomerAnniversaryGet.js +7 -7
  145. package/api/yotpo/yotpoCustomerAnniversarySet.js +7 -7
  146. package/api/yotpo/yotpoCustomerBirthdaySet.js +7 -7
  147. package/api/yotpo/yotpoCustomerGet.js +6 -6
  148. package/api/yotpo/yotpoCustomerPointsAdjust.js +7 -7
  149. package/api/yotpo/yotpoCustomerUpsert.js +7 -7
  150. package/api/yotpo/yotpoCustomersRecentGet.js +6 -6
  151. package/api/yotpo/yotpoRedemptionCreate.js +7 -7
  152. package/api/yotpo/yotpoRedemptionOptionsGet.js +6 -6
  153. package/api/yotpo/yotpoVipTiersGet.js +5 -5
  154. package/api/youtube/youtube.constants.js +5 -0
  155. package/api/youtube/youtube.utils.js +77 -0
  156. package/api/youtube/youtubeChannelVideosGet.js +25 -70
  157. package/package.json +1 -1
@@ -1,3 +1,3 @@
1
1
  module.exports = {
2
2
  MAX_REQUEST_ITEMS: 100,
3
- };
3
+ };
@@ -48,40 +48,7 @@ const setSessionId = async (credsPayload, sessionId) => {
48
48
  SESSION_IDS.set(CLIENT_ID, sessionId);
49
49
  };
50
50
 
51
- const withPeoplevoxAuth = async (fetchPayload, next) => {
52
- const { context = {} } = fetchPayload;
53
- const { credsPayload } = context;
54
-
55
- const sessionIdResponse = await getSessionId(credsPayload);
56
-
57
- const {
58
- ok: sessionIdOk,
59
- data: sessionId,
60
- } = sessionIdResponse;
61
-
62
- if (!sessionIdOk) {
63
- return sessionIdResponse;
64
- }
65
-
66
- const response = await next({
67
- ...fetchPayload,
68
- context: {
69
- ...context,
70
- sessionId,
71
- },
72
- });
73
-
74
- if (response.ok) {
75
- await setSessionId(credsPayload, sessionId);
76
- return response;
77
- }
78
-
79
- // TODO: Check if it was an auth error, and try another auth method if so
80
- return response;
81
- };
82
-
83
51
  module.exports = {
84
52
  getSessionId,
85
53
  setSessionId,
86
- withPeoplevoxAuth,
87
54
  };
@@ -1,7 +1,12 @@
1
1
  const csvtojson = require('csvtojson');
2
2
  const xml2js = require('xml2js');
3
- const { FetchClient, credsFromPayload, appendUrlToBase, logDeep, Chain } = require('../utils');
4
- const { withPeoplevoxAuth } = require('../peoplevox/peoplevox.sessions');
3
+ const { resolveCreds } = require('../pipelineSteps');
4
+ const {
5
+ FetchClient,
6
+ appendUrlToBase,
7
+ logDeep,
8
+ } = require('../utils');
9
+ const { getSessionId, setSessionId } = require('../peoplevox/peoplevox.sessions');
5
10
 
6
11
  const xml2jsBuilder = new xml2js.Builder({
7
12
  headless: true,
@@ -45,17 +50,17 @@ const buildSoapEnvelope = ({
45
50
  return xml2jsBuilder.buildObject(envelopeObject);
46
51
  };
47
52
 
48
- // TODO: Split into multiple steps, allow mutating context
49
- const peoplevoxRequestPreparer = async (requestPayload, context) => {
50
- const { headers, body } = requestPayload;
51
- const { credsPayload, action, sessionId } = context;
52
- const { CLIENT_ID } = await credsFromPayload(credsPayload);
53
+ const useSoapEnvelope = async (state) => {
54
+ const { requestPayload, context } = state;
55
+ const { creds, action, sessionId } = context;
56
+ const { CLIENT_ID } = creds;
53
57
 
54
58
  if (!sessionId) {
55
59
  throw new Error('PeopleVox sessionId is required');
56
60
  }
57
61
 
58
62
  const baseUrl = `https://ap.peoplevox.net/${ CLIENT_ID }/Resources/IntegrationServicev4.asmx`;
63
+ const { headers, body } = requestPayload;
59
64
 
60
65
  const envelopeXml = buildSoapEnvelope({
61
66
  action,
@@ -67,14 +72,16 @@ const peoplevoxRequestPreparer = async (requestPayload, context) => {
67
72
  logDeep({ envelopeXml });
68
73
 
69
74
  return {
70
- ...requestPayload,
71
- url: appendUrlToBase(baseUrl, requestPayload.url),
72
- headers: {
73
- ...headers,
74
- 'Content-Type': 'text/xml; charset=utf-8',
75
- 'SOAPAction': `http://www.peoplevox.net/${ action }`,
75
+ requestPayload: {
76
+ ...requestPayload,
77
+ url: appendUrlToBase(baseUrl, requestPayload.url),
78
+ headers: {
79
+ ...headers,
80
+ 'Content-Type': 'text/xml; charset=utf-8',
81
+ 'SOAPAction': `http://www.peoplevox.net/${ action }`,
82
+ },
83
+ body: envelopeXml,
76
84
  },
77
- body: envelopeXml,
78
85
  };
79
86
  };
80
87
 
@@ -167,17 +174,54 @@ const hoistDetail = (state) => {
167
174
  };
168
175
 
169
176
  const peoplevoxClient = new FetchClient({
170
- requestPreparer: peoplevoxRequestPreparer,
171
- responseInterpreter: new Chain([
177
+ pipeline: [
178
+ resolveCreds,
179
+ useSoapEnvelope,
180
+ 'fetch',
172
181
  stripEnvelope,
173
182
  tryToParseDetailAsCsv,
174
183
  unwrapSingleDetail,
175
184
  hoistDetail,
176
- ]),
185
+ ],
177
186
  });
178
187
 
179
- peoplevoxClient.use(withPeoplevoxAuth);
188
+ const fetchWithoutAuth = peoplevoxClient.fetch.bind(peoplevoxClient);
189
+
190
+ peoplevoxClient.fetch = async ({
191
+ requestPayload,
192
+ context = {},
193
+ inspect = false,
194
+ }) => {
195
+ const { credsPayload } = context;
196
+
197
+ const sessionIdResponse = await getSessionId(credsPayload);
198
+
199
+ const {
200
+ ok: sessionIdOk,
201
+ data: sessionId,
202
+ } = sessionIdResponse;
203
+
204
+ if (!sessionIdOk) {
205
+ return sessionIdResponse;
206
+ }
207
+
208
+ const response = await fetchWithoutAuth({
209
+ requestPayload,
210
+ context: {
211
+ ...context,
212
+ sessionId,
213
+ },
214
+ inspect,
215
+ });
216
+
217
+ if (response.ok) {
218
+ await setSessionId(credsPayload, sessionId);
219
+ }
220
+
221
+ // TODO: Check if it was an auth error, and try another auth method if so
222
+ return response;
223
+ };
180
224
 
181
225
  module.exports = {
182
226
  peoplevoxClient,
183
- };
227
+ };
@@ -32,11 +32,13 @@ const peoplevoxGetSingle = async (
32
32
  const resolvedSearchClause = searchClause || `${ idName }.Equals("${ id }")`;
33
33
 
34
34
  const response = await peoplevoxClient.fetch({
35
- method: 'post',
36
- body: {
37
- getRequest: {
38
- TemplateName: templateName,
39
- SearchClause: resolvedSearchClause,
35
+ requestPayload: {
36
+ method: 'post',
37
+ body: {
38
+ getRequest: {
39
+ TemplateName: templateName,
40
+ SearchClause: resolvedSearchClause,
41
+ },
40
42
  },
41
43
  },
42
44
  context: {
@@ -37,11 +37,13 @@ const peoplevoxOrderEdit = async (
37
37
  }
38
38
 
39
39
  return peoplevoxClient.fetch({
40
- method: 'post',
41
- body: {
42
- saveRequest: {
43
- TemplateName: 'Sales orders',
44
- CsvData: csvData,
40
+ requestPayload: {
41
+ method: 'post',
42
+ body: {
43
+ saveRequest: {
44
+ TemplateName: 'Sales orders',
45
+ CsvData: csvData,
46
+ },
45
47
  },
46
48
  },
47
49
  context: {
@@ -25,15 +25,17 @@ const peoplevoxReportGet = async (
25
25
  }
26
26
 
27
27
  const reportGetResponse = await peoplevoxClient.fetch({
28
- method: 'post',
29
- body: {
30
- getReportRequest: {
31
- TemplateName: reportName,
32
- ...(searchClause ? { SearchClause: searchClause } : {}),
33
- ...(perPage ? { ItemsPerPage: perPage } : {}),
34
- ...(filter ? { FilterClause: filter } : {}),
35
- ...(orderBy ? { OrderBy: orderBy } : {}),
36
- ...(columns ? { Columns: columns.join(',') } : {}),
28
+ requestPayload: {
29
+ method: 'post',
30
+ body: {
31
+ getReportRequest: {
32
+ TemplateName: reportName,
33
+ ...(searchClause ? { SearchClause: searchClause } : {}),
34
+ ...(perPage ? { ItemsPerPage: perPage } : {}),
35
+ ...(filter ? { FilterClause: filter } : {}),
36
+ ...(orderBy ? { OrderBy: orderBy } : {}),
37
+ ...(columns ? { Columns: columns.join(',') } : {}),
38
+ },
37
39
  },
38
40
  },
39
41
  context: {
@@ -0,0 +1,49 @@
1
+ const { appendUrlToBase, credsFromPayload } = require('./utils');
2
+
3
+ const resolveCreds = async (state) => {
4
+ const { context } = state;
5
+ const { creds, credsPayload } = context;
6
+
7
+ if (creds || !credsPayload) {
8
+ return {};
9
+ }
10
+
11
+ const resolvedCreds = await credsFromPayload(credsPayload);
12
+
13
+ return {
14
+ context: {
15
+ ...context,
16
+ creds: resolvedCreds,
17
+ },
18
+ };
19
+ };
20
+
21
+ const useBaseUrl = (baseUrl) => async (state) => {
22
+ const { requestPayload, context } = state;
23
+ const resolvedBaseUrl = baseUrl ?? context.baseUrl;
24
+
25
+ if (!resolvedBaseUrl) {
26
+ return {
27
+ breakChain: true,
28
+ response: {
29
+ ok: false,
30
+ error: {
31
+ code: 'NO_BASE_URL',
32
+ message: 'No base URL provided',
33
+ },
34
+ },
35
+ };
36
+ }
37
+
38
+ return {
39
+ requestPayload: {
40
+ ...requestPayload,
41
+ url: appendUrlToBase(resolvedBaseUrl, requestPayload.url),
42
+ },
43
+ };
44
+ };
45
+
46
+ module.exports = {
47
+ resolveCreds,
48
+ useBaseUrl,
49
+ };
@@ -1,7 +1,7 @@
1
1
  const { DEFAULT_API_VERSION } = require('../shopify/shopify.constants');
2
+ const { resolveCreds } = require('../pipelineSteps');
2
3
  const {
3
4
  FetchClient,
4
- Chain,
5
5
  appendUrlToBase,
6
6
  fetchClientCommonSteps,
7
7
  pathAsArray,
@@ -49,11 +49,11 @@ const handleMutationUserErrors = async (state) => {
49
49
  };
50
50
  };
51
51
 
52
- const addUrlAndAuthHeaders = async (state) => {
52
+ const useUrlAndAuthHeaders = async (state) => {
53
53
  const { requestPayload, context } = state;
54
54
  const {
55
- creds,
56
55
  apiVersion = DEFAULT_API_VERSION,
56
+ creds,
57
57
  } = context;
58
58
  const {
59
59
  STORE_HANDLE,
@@ -61,15 +61,13 @@ const addUrlAndAuthHeaders = async (state) => {
61
61
  } = creds;
62
62
 
63
63
  const baseUrl = `https://${ STORE_HANDLE }.myshopify.com/admin/api/${ apiVersion }/graphql.json`;
64
- const baseHeaders = {
65
- 'X-Shopify-Access-Token': API_KEY,
66
- };
67
64
 
68
65
  return {
69
66
  requestPayload: {
67
+ ...requestPayload,
70
68
  url: appendUrlToBase(baseUrl, requestPayload.url),
71
69
  headers: {
72
- ...baseHeaders,
70
+ 'X-Shopify-Access-Token': API_KEY,
73
71
  ...requestPayload.headers,
74
72
  },
75
73
  },
@@ -99,25 +97,21 @@ const movePageInfoToMeta = async (state) => {
99
97
  };
100
98
  };
101
99
 
102
- const shopifyClientRequestPreparer = new Chain([
103
- addUrlAndAuthHeaders,
104
- ]);
105
-
106
- const shopifyClientResponseInterpreter = new Chain([
107
- movePageInfoToMeta,
108
- fetchClientCommonSteps.stripEdgesAndNodes,
109
- fetchClientCommonSteps.collapseDataWithOneValue,
110
- fetchClientCommonSteps.exitEarlyOnNotOk,
111
- fetchClientCommonSteps.exitEarlyOnGraphqlErrors,
112
- fetchClientCommonSteps.digToPath,
113
- handleMutationUserErrors,
114
- ]);
115
-
116
100
  const shopifyClient = new FetchClient({
117
- requestPreparer: shopifyClientRequestPreparer,
118
- responseInterpreter: shopifyClientResponseInterpreter,
101
+ pipeline: [
102
+ resolveCreds,
103
+ useUrlAndAuthHeaders,
104
+ 'fetch',
105
+ movePageInfoToMeta,
106
+ fetchClientCommonSteps.stripEdgesAndNodes,
107
+ fetchClientCommonSteps.collapseDataWithOneValue,
108
+ fetchClientCommonSteps.exitEarlyOnNotOk,
109
+ fetchClientCommonSteps.exitEarlyOnGraphqlErrors,
110
+ fetchClientCommonSteps.digToPath,
111
+ handleMutationUserErrors,
112
+ ],
119
113
  });
120
114
 
121
115
  module.exports = {
122
116
  shopifyClient,
123
- };
117
+ };
@@ -0,0 +1,211 @@
1
+ const { randomUUID } = require('crypto');
2
+ const { createReadStream } = require('fs');
3
+ const fs = require('fs').promises;
4
+
5
+ const { HOSTED, TEMP_DIR } = require('../constants');
6
+ const { credsValidator } = require('../validators');
7
+ const {
8
+ ArgsWarden,
9
+ customFetch,
10
+ gidToId,
11
+ objectToFormData,
12
+ objHasAny,
13
+ objectArrayToJsonl,
14
+ jsonlToObjectArray,
15
+ wait,
16
+ valueProvided,
17
+ } = require('../utils');
18
+ const { shopifyStagedUploadCreate } = require('./shopifyStagedUploadCreate');
19
+ const { shopifyBulkOperationRunMutation } = require('./shopifyBulkOperationRunMutation');
20
+ const { shopifyBulkOperationGet } = require('./shopifyBulkOperationGet');
21
+
22
+ const bulkOpAttrs = `
23
+ id
24
+ status
25
+ type
26
+ objectCount
27
+ url
28
+ errorCode
29
+ `;
30
+
31
+ const createOrResumeBulkOpPayloadValidator = (payload) => {
32
+ const { mutation, input, bulkOperationId } = payload;
33
+
34
+ if (valueProvided(bulkOperationId)) {
35
+ return true;
36
+ }
37
+
38
+ return valueProvided(mutation) && objHasAny(input, ['data', 'filepath']);
39
+ };
40
+
41
+ const argsWarden = new ArgsWarden([
42
+ ['credsPayload', credsValidator],
43
+ ['createOrResumeBulkOpPayload', createOrResumeBulkOpPayloadValidator],
44
+ ]);
45
+
46
+ const shopifyBulkMutationDo = async (
47
+ credsPayload,
48
+ createOrResumeBulkOpPayload,
49
+ {
50
+ apiVersion,
51
+ clientIdentifier,
52
+ waitForResult = true,
53
+ } = {},
54
+ ) => {
55
+
56
+ const rejectResponse = await argsWarden.responseIfRejectingArgs({
57
+ credsPayload,
58
+ createOrResumeBulkOpPayload,
59
+ });
60
+ if (rejectResponse) {
61
+ return rejectResponse;
62
+ }
63
+
64
+ const {
65
+ mutation,
66
+ input,
67
+ bulkOperationId,
68
+ } = createOrResumeBulkOpPayload;
69
+
70
+ let bulkOperation;
71
+
72
+ if (!bulkOperationId) {
73
+
74
+ if (HOSTED) {
75
+ return {
76
+ ok: false,
77
+ error: 'This function is only available locally',
78
+ };
79
+ }
80
+
81
+ let {
82
+ data,
83
+ filepath,
84
+ } = input;
85
+
86
+ if (!filepath) {
87
+ await fs.mkdir(TEMP_DIR, { recursive: true });
88
+ filepath = `${ TEMP_DIR }/shopifyBulkMutationDo_${ randomUUID() }.jsonl`;
89
+ await fs.writeFile(filepath, objectArrayToJsonl(data));
90
+ }
91
+
92
+ const stagedUploadResponse = await shopifyStagedUploadCreate(
93
+ credsPayload,
94
+ {
95
+ resource: 'BULK_MUTATION_VARIABLES',
96
+ filename: filepath.split('/').pop(),
97
+ mimeType: 'text/jsonl',
98
+ httpMethod: 'POST',
99
+ },
100
+ { apiVersion },
101
+ );
102
+ if (!stagedUploadResponse.ok) {
103
+ return stagedUploadResponse;
104
+ }
105
+
106
+ const { url, parameters } = stagedUploadResponse.data.stagedTargets[0];
107
+
108
+ const formData = objectToFormData(
109
+ Object.fromEntries(parameters.map(({ name, value }) => [name, value])),
110
+ );
111
+ formData.append('file', createReadStream(filepath));
112
+
113
+ const uploadResponse = await customFetch(url, {
114
+ method: 'post',
115
+ body: formData,
116
+ });
117
+ if (!uploadResponse.ok) {
118
+ return uploadResponse;
119
+ }
120
+
121
+ const stagedUploadPath = uploadResponse.data?.PostResponse?.Key;
122
+
123
+ const mutationRunResponse = await shopifyBulkOperationRunMutation(
124
+ credsPayload,
125
+ mutation,
126
+ stagedUploadPath,
127
+ {
128
+ apiVersion,
129
+ clientIdentifier,
130
+ returnAttrs: bulkOpAttrs,
131
+ },
132
+ );
133
+
134
+ if (!waitForResult) {
135
+ return mutationRunResponse;
136
+ }
137
+
138
+ const { ok: mutationRunOk, data: mutationRunData } = mutationRunResponse;
139
+ if (!mutationRunOk) {
140
+ return mutationRunResponse;
141
+ }
142
+
143
+ bulkOperation = mutationRunData.bulkOperation;
144
+ bulkOperationId = gidToId(bulkOperation.id);
145
+ }
146
+
147
+ while (['CREATED', 'RUNNING'].includes(bulkOperation?.status)) {
148
+
149
+ if (bulkOperation) {
150
+ await wait(5000);
151
+ }
152
+
153
+ const operationResponse = await shopifyBulkOperationGet(
154
+ credsPayload,
155
+ bulkOperationId,
156
+ {
157
+ apiVersion,
158
+ attrs: bulkOpAttrs,
159
+ },
160
+ );
161
+ if (!operationResponse.ok) {
162
+ return operationResponse;
163
+ }
164
+
165
+ bulkOperation = operationResponse.data;
166
+ }
167
+
168
+ if (bulkOperation.status !== 'COMPLETED') {
169
+ return {
170
+ ok: false,
171
+ error: {
172
+ code: 'BULK_OPERATION_FAILED',
173
+ message: `Bulk operation failed with status ${ bulkOperation.status }`,
174
+ details: bulkOperation,
175
+ },
176
+ };
177
+ }
178
+
179
+ const resultsResponse = await customFetch(bulkOperation.url);
180
+ if (!resultsResponse.ok) {
181
+ return resultsResponse;
182
+ }
183
+
184
+ const results = jsonlToObjectArray(resultsResponse.data);
185
+
186
+ return {
187
+ ok: true,
188
+ data: results,
189
+ meta: {
190
+ bulkOperation,
191
+ },
192
+ };
193
+ };
194
+
195
+ const funcApiConfig = {
196
+ argsWarden,
197
+ };
198
+
199
+ module.exports = {
200
+ shopifyBulkMutationDo,
201
+ funcApiConfig,
202
+ };
203
+
204
+ /*
205
+ curl -X POST "http://localhost:8000/shopifyBulkMutationDo" \
206
+ -H "Content-Type: application/json" \
207
+ -d '{
208
+ "credsPayload": { "credsPath": "shopify.au" },
209
+ "arg": "1234"
210
+ }'
211
+ */
@@ -0,0 +1,86 @@
1
+ // https://shopify.dev/docs/api/admin-graphql/latest/queries/bulkOperation
2
+
3
+ const { credsValidator } = require('../validators');
4
+ const { ArgsWarden, actionSingleOrMultiple } = require('../utils');
5
+ const { shopifyGetSingle } = require('./shopifyGetSingle');
6
+
7
+ const defaultAttrs = `
8
+ id
9
+ status
10
+ type
11
+ objectCount
12
+ url
13
+ errorCode
14
+ `.trim();
15
+
16
+ const argsWarden = new ArgsWarden([
17
+ ['credsPayload', credsValidator],
18
+ ['bulkOperationId'],
19
+ ]);
20
+
21
+ const shopifyBulkOperationGetSingle = async (
22
+ credsPayload,
23
+ bulkOperationId,
24
+ {
25
+ apiVersion,
26
+ attrs = defaultAttrs,
27
+ } = {},
28
+ ) => {
29
+ return shopifyGetSingle(
30
+ credsPayload,
31
+ 'bulkOperation',
32
+ bulkOperationId,
33
+ {
34
+ apiVersion,
35
+ attrs,
36
+ },
37
+ );
38
+ };
39
+
40
+ const shopifyBulkOperationGet = async (
41
+ credsPayload,
42
+ bulkOperationId,
43
+ {
44
+ queueRunOptions,
45
+ apiVersion,
46
+ attrs = defaultAttrs,
47
+ } = {},
48
+ ) => {
49
+
50
+ const rejectResponse = await argsWarden.responseIfRejectingArgs({
51
+ credsPayload,
52
+ bulkOperationId,
53
+ });
54
+ if (rejectResponse) {
55
+ return rejectResponse;
56
+ }
57
+
58
+ return actionSingleOrMultiple(
59
+ bulkOperationId,
60
+ shopifyBulkOperationGetSingle,
61
+ (bulkOperationIdItem) => ({
62
+ args: [credsPayload, bulkOperationIdItem, { apiVersion, attrs }],
63
+ }),
64
+ {
65
+ ...(queueRunOptions ? { queueRunOptions } : {}),
66
+ },
67
+ );
68
+ };
69
+
70
+ const funcApiConfig = {
71
+ argsWarden,
72
+ };
73
+
74
+ module.exports = {
75
+ shopifyBulkOperationGet,
76
+ funcApiConfig,
77
+ };
78
+
79
+ /*
80
+ curl -X POST "http://localhost:8000/shopifyBulkOperationGet" \
81
+ -H "Content-Type: application/json" \
82
+ -d '{
83
+ "credsPayload": { "credsPath": "shopify.au" },
84
+ "bulkOperationId": "3525975375944"
85
+ }'
86
+ */