@foxtware/mineral 0.1.9 → 0.1.11
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/.creds.yml.sample +27 -6
- package/AGENTS.md +6 -1
- package/README.md +1 -1
- package/api/bleckmann/.gitkeep +0 -0
- package/api/bleckmann/bleckmann.constants.js +8 -0
- package/api/bleckmann/bleckmann.utils.js +32 -0
- package/api/bleckmann/bleckmannAsnGet.js +52 -0
- package/api/bleckmann/bleckmannAsnsGet.js +65 -0
- package/api/constants.js +2 -1
- package/api/github/github.constants.js +11 -0
- package/api/github/github.utils.js +65 -0
- package/api/github/githubGet.js +144 -0
- package/api/github/githubIssueCreate.js +71 -0
- package/api/github/githubIssueGet.js +65 -0
- package/api/github/githubIssueUpdate.js +71 -0
- package/api/github/githubIssuesGet.js +74 -0
- package/api/github/githubPullRequestCreate.js +74 -0
- package/api/github/githubPullRequestGet.js +65 -0
- package/api/github/githubPullRequestMerge.js +77 -0
- package/api/github/githubPullRequestUpdate.js +72 -0
- package/api/github/githubPullRequestsGet.js +74 -0
- package/api/github/githubRepoCreate.js +80 -0
- package/api/github/githubRepoDelete.js +60 -0
- package/api/github/githubRepoGet.js +61 -0
- package/api/github/githubRepoUpdate.js +68 -0
- package/api/github/githubReposGet.js +69 -0
- package/api/github/githubUserGet.js +64 -0
- package/api/linear/linear.utils.js +9 -14
- package/api/linear/linearIssuesGet.js +9 -9
- package/api/logiwa/logiwa.utils.js +27 -25
- package/api/logiwa/logiwaInventoriesGet.js +5 -3
- package/api/logiwa/logiwaOrderGet.js +4 -2
- package/api/logiwa/logiwaOrdersGet.js +5 -3
- package/api/logiwa/logiwaProductGet.js +4 -2
- package/api/logiwa/logiwaProductsGet.js +5 -3
- package/api/logiwa/logiwaReportGetAvailableToPromise.js +5 -3
- package/api/logiwa/logiwaReportGetInventoryCalculation.js +5 -3
- package/api/logiwa/logiwaReportGetInventorySnapshot.js +5 -3
- package/api/logiwa/logiwaReportGetTotalInventory.js +5 -3
- package/api/logiwa/logiwaWebhookStatusGet.js +4 -2
- package/api/logiwa/logiwaWebhookSubscribe.js +8 -6
- package/api/logiwa/logiwaWebhookUnsubscribe.js +4 -2
- package/api/logiwa/logiwaWebhooksGet.js +4 -2
- package/api/loop/loop.utils.js +9 -15
- package/api/loop/loopGet.js +20 -24
- package/api/loop/loopReturnGet.js +6 -6
- package/api/peoplevox/peoplevox.constants.js +1 -1
- package/api/peoplevox/peoplevox.sessions.js +0 -33
- package/api/peoplevox/peoplevox.utils.js +63 -19
- package/api/peoplevox/peoplevoxGetSingle.js +7 -5
- package/api/peoplevox/peoplevoxOrderEdit.js +7 -5
- package/api/peoplevox/peoplevoxReportGet.js +11 -9
- package/api/pipelineSteps.js +49 -0
- package/api/shopify/shopify.utils.js +93 -22
- package/api/shopify/shopifyBulkMutationDo.js +211 -0
- package/api/shopify/shopifyBulkOperationGet.js +86 -0
- package/api/shopify/shopifyBulkOperationRunMutation.js +83 -0
- package/api/shopify/shopifyBulkOperationRunQuery.js +70 -0
- package/api/shopify/shopifyBulkQueryDo.js +127 -0
- package/api/shopify/shopifyCheckoutAndAccountsConfigurationUpdate.js +79 -0
- package/api/shopify/shopifyCollectionGet.js +6 -6
- package/api/shopify/shopifyCustomerGet.js +6 -6
- package/api/shopify/shopifyGet.js +18 -13
- package/api/shopify/shopifyGetSingle.js +9 -9
- package/api/shopify/shopifyMutationDo.js +6 -6
- package/api/shopify/shopifyOrdersGet.js +11 -11
- package/api/shopify/shopifyStagedUploadCreate.js +96 -0
- package/api/shopify/shopifyTagsAddBulk.js +77 -0
- package/api/slack/slack.utils.js +10 -19
- package/api/slack/slackMessagePost.js +11 -11
- package/api/starshipit/starshipit.constants.js +8 -0
- package/api/starshipit/starshipit.utils.js +71 -0
- package/api/starshipit/starshipitAddressCreate.js +77 -0
- package/api/starshipit/starshipitAddressDelete.js +55 -0
- package/api/starshipit/starshipitAddressUpdate.js +69 -0
- package/api/starshipit/starshipitAddressesGet.js +52 -0
- package/api/starshipit/starshipitGet.js +146 -0
- package/api/starshipit/starshipitOrderDelete.js +102 -0
- package/api/starshipit/starshipitOrderGet.js +74 -0
- package/api/starshipit/starshipitOrderUpdate.js +109 -0
- package/api/starshipit/starshipitOrdersListShipped.js +64 -0
- package/api/starshipit/starshipitProductAdd.js +100 -0
- package/api/starshipit/starshipitProductsDelete.js +57 -0
- package/api/starshipit/starshipitProductsGet.js +49 -0
- package/api/starshipit/starshipitTrackingGet.js +69 -0
- package/api/stripe/_example.js +7 -7
- package/api/stripe/stripe.utils.js +9 -14
- package/api/stripe/stripeCardTokenCreate.js +11 -11
- package/api/stripe/stripeChargeCapture.js +11 -11
- package/api/stripe/stripeChargeCreate.js +11 -11
- package/api/stripe/stripeChargeGet.js +5 -5
- package/api/stripe/stripeChargesGet.js +6 -6
- package/api/stripe/stripeRefundCreate.js +10 -10
- package/api/stripe/stripeRefundGet.js +5 -5
- package/api/stripe/stripeRefundsGet.js +6 -6
- package/api/stripe/stripeTokenGet.js +5 -5
- package/api/stylearcade/stylearcade.constants.js +5 -0
- package/api/stylearcade/stylearcade.utils.js +40 -0
- package/api/stylearcade/stylearcadeDataGet.js +62 -0
- package/api/stylearcade/stylearcadeGet.js +133 -0
- package/api/stylearcade/stylearcadeMetadataGet.js +47 -0
- package/api/threeclicks/threeclicks.constants.js +8 -0
- package/api/threeclicks/threeclicks.utils.js +119 -0
- package/api/threeclicks/threeclicksCollectionsGet.js +40 -0
- package/api/threeclicks/threeclicksGet.js +138 -0
- package/api/threeclicks/threeclicksKeycodeCreate.js +55 -0
- package/api/threeclicks/threeclicksKeycodeGet.js +47 -0
- package/api/threeclicks/threeclicksPurchaseOrderCreate.js +34 -0
- package/api/threeclicks/threeclicksPurchaseOrderCreateBulk.js +34 -0
- package/api/threeclicks/threeclicksPurchaseOrderGet.js +33 -0
- package/api/threeclicks/threeclicksPurchaseOrderQuantitiesGet.js +33 -0
- package/api/threeclicks/threeclicksPurchaseOrderQuantityCreate.js +34 -0
- package/api/threeclicks/threeclicksPurchaseOrderQuantityCreateBulk.js +34 -0
- package/api/threeclicks/threeclicksPurchaseOrderQuantityUpdate.js +46 -0
- package/api/threeclicks/threeclicksPurchaseOrderQuantityUpdateBulk.js +46 -0
- package/api/threeclicks/threeclicksPurchaseOrderUpdate.js +35 -0
- package/api/threeclicks/threeclicksPurchaseOrdersGet.js +30 -0
- package/api/threeclicks/threeclicksShipmentCreate.js +34 -0
- package/api/threeclicks/threeclicksShipmentGet.js +33 -0
- package/api/threeclicks/threeclicksShipmentOrderAdd.js +38 -0
- package/api/threeclicks/threeclicksShipmentOrderQuantityCreate.js +35 -0
- package/api/threeclicks/threeclicksShipmentQuantityUpdate.js +35 -0
- package/api/threeclicks/threeclicksShipmentStatusUpdate.js +45 -0
- package/api/threeclicks/threeclicksShipmentsGet.js +52 -0
- package/api/threeclicks/threeclicksSignatureCalculate.js +73 -0
- package/api/threeclicks/threeclicksStyleCodesGet.js +46 -0
- package/api/threeclicks/threeclicksStyleColourCreate.js +47 -0
- package/api/threeclicks/threeclicksStyleColourDelete.js +34 -0
- package/api/threeclicks/threeclicksStyleCreate.js +44 -0
- package/api/threeclicks/threeclicksStyleDelete.js +33 -0
- package/api/threeclicks/threeclicksStyleGet.js +43 -0
- package/api/threeclicks/threeclicksStylePriceUpdate.js +70 -0
- package/api/threeclicks/threeclicksStyleUpdate.js +47 -0
- package/api/threeclicks/threeclicksStylesGet.js +45 -0
- package/api/utils.js +85 -117
- package/api/validators.js +1 -1
- package/api/workable/workable.utils.js +8 -13
- package/api/workable/workableGet.js +16 -11
- package/api/workable/workableJobCandidateCreate.js +10 -10
- package/api/workable/workableJobGet.js +5 -5
- package/api/workable/workableJobMembersGet.js +5 -5
- package/api/workable/workableJobStagesGet.js +5 -5
- package/api/yotpo/yotpo.utils.js +17 -22
- package/api/yotpo/yotpoCampaignsGet.js +6 -6
- package/api/yotpo/yotpoCustomerActionRecord.js +7 -7
- package/api/yotpo/yotpoCustomerAnniversaryGet.js +7 -7
- package/api/yotpo/yotpoCustomerAnniversarySet.js +7 -7
- package/api/yotpo/yotpoCustomerBirthdaySet.js +7 -7
- package/api/yotpo/yotpoCustomerGet.js +6 -6
- package/api/yotpo/yotpoCustomerPointsAdjust.js +7 -7
- package/api/yotpo/yotpoCustomerUpsert.js +7 -7
- package/api/yotpo/yotpoCustomersRecentGet.js +6 -6
- package/api/yotpo/yotpoRedemptionCreate.js +7 -7
- package/api/yotpo/yotpoRedemptionOptionsGet.js +6 -6
- package/api/yotpo/yotpoVipTiersGet.js +5 -5
- package/api/youtube/youtube.constants.js +5 -0
- package/api/youtube/youtube.utils.js +77 -0
- package/api/youtube/youtubeChannelVideosGet.js +25 -70
- package/package.json +1 -1
|
@@ -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 {
|
|
4
|
-
const {
|
|
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
|
-
|
|
49
|
-
const
|
|
50
|
-
const {
|
|
51
|
-
const {
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
171
|
-
|
|
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.
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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,11 +1,13 @@
|
|
|
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
|
+
jsonlToObjectArray,
|
|
7
8
|
pathAsArray,
|
|
8
9
|
objectDigNodeAtPath,
|
|
10
|
+
sentenceCaseString,
|
|
9
11
|
} = require('../utils');
|
|
10
12
|
|
|
11
13
|
const handleMutationUserErrors = async (state) => {
|
|
@@ -49,11 +51,11 @@ const handleMutationUserErrors = async (state) => {
|
|
|
49
51
|
};
|
|
50
52
|
};
|
|
51
53
|
|
|
52
|
-
const
|
|
54
|
+
const useUrlAndAuthHeaders = async (state) => {
|
|
53
55
|
const { requestPayload, context } = state;
|
|
54
56
|
const {
|
|
55
|
-
creds,
|
|
56
57
|
apiVersion = DEFAULT_API_VERSION,
|
|
58
|
+
creds,
|
|
57
59
|
} = context;
|
|
58
60
|
const {
|
|
59
61
|
STORE_HANDLE,
|
|
@@ -61,15 +63,13 @@ const addUrlAndAuthHeaders = async (state) => {
|
|
|
61
63
|
} = creds;
|
|
62
64
|
|
|
63
65
|
const baseUrl = `https://${ STORE_HANDLE }.myshopify.com/admin/api/${ apiVersion }/graphql.json`;
|
|
64
|
-
const baseHeaders = {
|
|
65
|
-
'X-Shopify-Access-Token': API_KEY,
|
|
66
|
-
};
|
|
67
66
|
|
|
68
67
|
return {
|
|
69
68
|
requestPayload: {
|
|
69
|
+
...requestPayload,
|
|
70
70
|
url: appendUrlToBase(baseUrl, requestPayload.url),
|
|
71
71
|
headers: {
|
|
72
|
-
|
|
72
|
+
'X-Shopify-Access-Token': API_KEY,
|
|
73
73
|
...requestPayload.headers,
|
|
74
74
|
},
|
|
75
75
|
},
|
|
@@ -99,25 +99,96 @@ const movePageInfoToMeta = async (state) => {
|
|
|
99
99
|
};
|
|
100
100
|
};
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
// https://shopify.dev/docs/api/usage/bulk-operations/queries
|
|
103
|
+
const parseShopifyJsonl = (jsonl) => {
|
|
104
|
+
const objects = jsonlToObjectArray(jsonl);
|
|
105
|
+
const objectsMap = new Map();
|
|
106
|
+
const objectsWithoutStitching = [];
|
|
107
|
+
|
|
108
|
+
for (const object of objects) {
|
|
109
|
+
const {
|
|
110
|
+
id: gid,
|
|
111
|
+
__parentId: parentGid,
|
|
112
|
+
} = object;
|
|
113
|
+
|
|
114
|
+
if (!gid) {
|
|
115
|
+
objectsWithoutStitching.push(object);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const [objectType] = gid.split('gid://shopify/')[1].split(/[^a-zA-Z0-9]+/);
|
|
120
|
+
|
|
121
|
+
objectsMap.set(gid, {
|
|
122
|
+
...object,
|
|
123
|
+
selfType: objectType,
|
|
124
|
+
...parentGid && { parentGid },
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const objectTypeToProperty = (objectType) => `${ sentenceCaseString(objectType) }s`;
|
|
129
|
+
|
|
130
|
+
for (const [gid, object] of objectsMap) {
|
|
131
|
+
const {
|
|
132
|
+
selfType,
|
|
133
|
+
parentGid,
|
|
134
|
+
} = object;
|
|
135
|
+
|
|
136
|
+
if (!parentGid) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const objectProperty = objectTypeToProperty(selfType);
|
|
141
|
+
|
|
142
|
+
let parentObject = objectsMap.get(parentGid);
|
|
143
|
+
if (!parentObject) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
105
146
|
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
147
|
+
const nestedParent = gid.split('?')?.[1];
|
|
148
|
+
|
|
149
|
+
if (nestedParent) {
|
|
150
|
+
let [nestedParentType] = nestedParent.split('=');
|
|
151
|
+
nestedParentType = nestedParentType
|
|
152
|
+
.replaceAll('_id', '')
|
|
153
|
+
.split('_')
|
|
154
|
+
.map((word) => word[0].toUpperCase() + word.slice(1))
|
|
155
|
+
.join('');
|
|
156
|
+
nestedParentType = sentenceCaseString(nestedParentType);
|
|
157
|
+
parentObject = parentObject[nestedParentType];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!parentObject) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
parentObject[objectProperty] = parentObject[objectProperty] || [];
|
|
165
|
+
parentObject[objectProperty].push(objectsMap.get(gid));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const topLevelObjects = Array.from(objectsMap.values()).filter((object) => !object?.parentGid);
|
|
169
|
+
|
|
170
|
+
return [
|
|
171
|
+
...topLevelObjects,
|
|
172
|
+
...objectsWithoutStitching,
|
|
173
|
+
];
|
|
174
|
+
};
|
|
115
175
|
|
|
116
176
|
const shopifyClient = new FetchClient({
|
|
117
|
-
|
|
118
|
-
|
|
177
|
+
pipeline: [
|
|
178
|
+
resolveCreds,
|
|
179
|
+
useUrlAndAuthHeaders,
|
|
180
|
+
'fetch',
|
|
181
|
+
movePageInfoToMeta,
|
|
182
|
+
fetchClientCommonSteps.stripEdgesAndNodes,
|
|
183
|
+
fetchClientCommonSteps.collapseDataWithOneValue,
|
|
184
|
+
fetchClientCommonSteps.exitEarlyOnNotOk,
|
|
185
|
+
fetchClientCommonSteps.exitEarlyOnGraphqlErrors,
|
|
186
|
+
fetchClientCommonSteps.digToPath,
|
|
187
|
+
handleMutationUserErrors,
|
|
188
|
+
],
|
|
119
189
|
});
|
|
120
190
|
|
|
121
191
|
module.exports = {
|
|
192
|
+
parseShopifyJsonl,
|
|
122
193
|
shopifyClient,
|
|
123
|
-
};
|
|
194
|
+
};
|
|
@@ -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
|
+
*/
|