@foxtware/mineral 0.1.17 → 0.1.19

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 (96) hide show
  1. package/.creds.yml.sample +16 -7
  2. package/AGENTS.md +4 -0
  3. package/_build_scripts/createNewFunction.js +10 -5
  4. package/_build_scripts/publish.js +15 -1
  5. package/api/backblaze/backblaze.constants.js +11 -0
  6. package/api/backblaze/backblaze.utils.js +322 -0
  7. package/api/backblaze/backblazeBucketsGet.js +68 -0
  8. package/api/backblaze/backblazeFileDelete.js +62 -0
  9. package/api/backblaze/backblazeFileHide.js +62 -0
  10. package/api/backblaze/backblazeFileUpload.js +142 -0
  11. package/api/backblaze/backblazeFilesGet.js +85 -0
  12. package/api/backblaze/backblazeGet.js +139 -0
  13. package/api/backblaze/docs.md +6 -0
  14. package/api/etsy/etsyListingUpdate.js +1 -1
  15. package/api/gorgias/gorgias.constants.js +8 -0
  16. package/api/gorgias/gorgias.utils.js +46 -0
  17. package/api/gorgias/gorgiasAccountGet.js +51 -0
  18. package/api/gorgias/gorgiasCustomerGet.js +70 -0
  19. package/api/gorgias/gorgiasCustomersGet.js +56 -0
  20. package/api/gorgias/gorgiasGet.js +150 -0
  21. package/api/gorgias/gorgiasGetSingle.js +26 -0
  22. package/api/gorgias/gorgiasMessagesGet.js +56 -0
  23. package/api/gorgias/gorgiasTagsGet.js +52 -0
  24. package/api/gorgias/gorgiasTicketCreate.js +66 -0
  25. package/api/gorgias/gorgiasTicketGet.js +70 -0
  26. package/api/gorgias/gorgiasTicketMessageCreate.js +65 -0
  27. package/api/gorgias/gorgiasTicketMessagesGet.js +66 -0
  28. package/api/gorgias/gorgiasTicketUpdate.js +62 -0
  29. package/api/gorgias/gorgiasTicketsGet.js +58 -0
  30. package/api/gorgias/gorgiasUsersGet.js +52 -0
  31. package/api/gorgias/gorgiasViewItemsGet.js +57 -0
  32. package/api/gorgias/gorgiasViewsGet.js +72 -0
  33. package/api/linear/docs.md +6 -0
  34. package/api/linear/linear.constants.js +3 -0
  35. package/api/linear/linear.utils.js +59 -0
  36. package/api/linear/linearAttachmentCreate.js +77 -0
  37. package/api/linear/linearAttachmentDelete.js +68 -0
  38. package/api/linear/linearCommentCreate.js +77 -0
  39. package/api/linear/linearCommentDelete.js +68 -0
  40. package/api/linear/linearCommentGet.js +77 -0
  41. package/api/linear/linearCommentUpdate.js +87 -0
  42. package/api/linear/linearCommentsGet.js +70 -0
  43. package/api/linear/linearCyclesGet.js +67 -0
  44. package/api/linear/linearGet.js +218 -0
  45. package/api/linear/linearIssueAddLabel.js +77 -0
  46. package/api/linear/linearIssueArchive.js +74 -0
  47. package/api/linear/linearIssueCreate.js +94 -0
  48. package/api/linear/linearIssueDelete.js +74 -0
  49. package/api/linear/linearIssueGet.js +87 -0
  50. package/api/linear/linearIssueLabelCreate.js +81 -0
  51. package/api/linear/linearIssueLabelsGet.js +66 -0
  52. package/api/linear/linearIssueRemoveLabel.js +77 -0
  53. package/api/linear/linearIssueUpdate.js +96 -0
  54. package/api/linear/linearIssuesGet.js +39 -63
  55. package/api/linear/linearOrganizationGet.js +64 -0
  56. package/api/linear/linearProjectCreate.js +92 -0
  57. package/api/linear/linearProjectGet.js +74 -0
  58. package/api/linear/linearProjectUpdate.js +80 -0
  59. package/api/linear/linearProjectsGet.js +71 -0
  60. package/api/linear/linearTeamGet.js +71 -0
  61. package/api/linear/linearTeamsGet.js +66 -0
  62. package/api/linear/linearUserGet.js +72 -0
  63. package/api/linear/linearUsersGet.js +71 -0
  64. package/api/linear/linearViewerGet.js +65 -0
  65. package/api/linear/linearWebhookCreate.js +77 -0
  66. package/api/linear/linearWebhookDelete.js +68 -0
  67. package/api/linear/linearWebhooksGet.js +67 -0
  68. package/api/linear/linearWorkflowStatesGet.js +73 -0
  69. package/api/peoplevox/peoplevox.utils.js +25 -0
  70. package/api/pipe17/pipe17.utils.js +7 -20
  71. package/api/pipe17/pipe17ArrivalGet.js +1 -1
  72. package/api/pipe17/pipe17FulfillmentGet.js +1 -1
  73. package/api/pipe17/pipe17InventoryItemGet.js +1 -1
  74. package/api/pipe17/pipe17JobGet.js +11 -24
  75. package/api/pipe17/pipe17LocationGet.js +1 -1
  76. package/api/pipe17/pipe17OrderGet.js +1 -1
  77. package/api/pipe17/pipe17ProductGet.js +1 -1
  78. package/api/pipe17/pipe17PurchaseGet.js +1 -1
  79. package/api/pipe17/pipe17ReceiptGet.js +1 -1
  80. package/api/pipe17/pipe17ReturnGet.js +1 -1
  81. package/api/shopify/shopifyFileCreate.js +107 -0
  82. package/api/shopify/shopifyFulfillmentCreate.js +180 -0
  83. package/api/shopify/shopifyFulfillmentTrackingInfoUpdate.js +92 -0
  84. package/api/shopify/shopifyMetafieldDefinitionCreate.js +2 -0
  85. package/api/shopify/shopifyMetafieldDefinitionGet.js +108 -0
  86. package/api/shopify/shopifyMetafieldDefinitionPropagate.js +93 -86
  87. package/api/shopify/shopifyOrderFulfill.js +322 -0
  88. package/api/shopify/shopifyThemeFileGet.js +112 -0
  89. package/api/shopify/shopifyThemeFilePropagate.js +410 -0
  90. package/api/shopify/shopifyThemeFilesUpsert.js +88 -0
  91. package/api/starshipit/starshipit.utils.js +29 -3
  92. package/api/starshipit/starshipitOrderGet.js +2 -10
  93. package/api/starshipit/starshipitOrdersGet.js +96 -0
  94. package/api/utils.js +96 -5
  95. package/package.json +1 -1
  96. package/server.utils.js +1 -1
@@ -0,0 +1,322 @@
1
+ const { credsValidator } = require('../validators');
2
+ const { ArgsWarden, operationQueueRunner, logDeep, askQuestion } = require('../utils');
3
+ const { objHasAny } = require('../utils');
4
+ const { shopifyOrderGet } = require('../shopify/shopifyOrderGet');
5
+ const { shopifyFulfillmentCreate } = require('../shopify/shopifyFulfillmentCreate');
6
+
7
+ const orderIdentifierValidator = (orderIdentifier) => {
8
+ return objHasAny(orderIdentifier, [
9
+ 'orderId',
10
+ 'orderName',
11
+ ]);
12
+ };
13
+
14
+ const fulfillmentPayloadValidator = (fulfillmentPayload) => {
15
+
16
+ const {
17
+ fulfillAll,
18
+ itemsBySku,
19
+ // TODO: Support fulfilling items by line item id
20
+
21
+ } = fulfillmentPayload;
22
+
23
+ if (fulfillAll === true) {
24
+ return true;
25
+ }
26
+
27
+ if (itemsBySku) {
28
+ const quantities = Object.values(itemsBySku);
29
+ return quantities.length > 0 && quantities.every(quantity => typeof quantity === 'number');
30
+ }
31
+
32
+ return false;
33
+ };
34
+
35
+ const argsWarden = new ArgsWarden([
36
+ ['credsPayload', credsValidator],
37
+ ['orderIdentifier', orderIdentifierValidator],
38
+ ['fulfillmentPayload', fulfillmentPayloadValidator],
39
+ ]);
40
+
41
+ const shopifyOrderFulfill = async (
42
+ credsPayload,
43
+ orderIdentifier,
44
+ fulfillmentPayload,
45
+ {
46
+ apiVersion,
47
+ // TODO: Support return schema
48
+ // returnSchema = 'deletedThingId',
49
+ } = {},
50
+ ) => {
51
+
52
+ const rejectResponse = await argsWarden.responseIfRejectingArgs({
53
+ credsPayload,
54
+ orderIdentifier,
55
+ fulfillmentPayload,
56
+ });
57
+ if (rejectResponse) {
58
+ return rejectResponse;
59
+ }
60
+
61
+ const {
62
+ fulfillAll = false,
63
+ itemsBySku,
64
+ notifyCustomer = false,
65
+ originAddress,
66
+ trackingInfo,
67
+ } = fulfillmentPayload;
68
+
69
+ const fetchFulfillmentOrdersLimit = 10;
70
+ const fetchFulfillmentsLimit = 10;
71
+ const fetchTrackingInfoLimit = 10;
72
+ const fetchLineItemsLimit = 250;
73
+
74
+ const openFulfillmentOrdersQuery = `displayable:true AND (request_status:UNSUBMITTED OR request_status:ACCEPTED)`;
75
+
76
+ const ORDER_ATTRS = `
77
+ fulfillable
78
+ fulfillments(first: ${ fetchFulfillmentsLimit }) {
79
+ trackingInfo(first: ${ fetchTrackingInfoLimit }) {
80
+ number
81
+ }
82
+ }
83
+ fulfillmentOrders(first: ${ fetchFulfillmentOrdersLimit }, query: "${ openFulfillmentOrdersQuery }") {
84
+ edges {
85
+ node {
86
+ id
87
+ requestStatus
88
+ ${ itemsBySku ? `
89
+ lineItems (first: ${ fetchLineItemsLimit }) {
90
+ edges {
91
+ node {
92
+ id
93
+ sku
94
+ remainingQuantity
95
+ requiresShipping
96
+ }
97
+ }
98
+ }
99
+ ` : '' }
100
+ }
101
+ }
102
+ }
103
+ `;
104
+
105
+ // Get open fulfillment orders
106
+ const orderResponse = await shopifyOrderGet(
107
+ credsPayload,
108
+ orderIdentifier,
109
+ {
110
+ apiVersion,
111
+ attrs: ORDER_ATTRS,
112
+ },
113
+ );
114
+
115
+ const { ok: orderOk, data: order } = orderResponse;
116
+ if (!orderOk || !order) {
117
+ return orderResponse;
118
+ }
119
+
120
+ const {
121
+ fulfillable,
122
+ fulfillments,
123
+ fulfillmentOrders,
124
+ } = order;
125
+
126
+ if (fulfillments.length >= fetchFulfillmentsLimit) {
127
+ return {
128
+ ok: false,
129
+ error: {
130
+ code: 'LIMIT_REACHED',
131
+ message: `We retrieved ${ fetchFulfillmentsLimit } fulfillments, so there may be more. Please adjust the function.`,
132
+ },
133
+ data: order,
134
+ };
135
+ }
136
+
137
+ if (fulfillments.some(f => f?.trackingInfo?.length > fetchTrackingInfoLimit)) {
138
+ return {
139
+ ok: false,
140
+ error: {
141
+ code: 'LIMIT_REACHED',
142
+ message: `We retrieved ${ fetchTrackingInfoLimit } tracking info, so there may be more. Please adjust the function.`,
143
+ },
144
+ data: order,
145
+ };
146
+ }
147
+
148
+ if (fulfillmentOrders.length >= fetchFulfillmentOrdersLimit) {
149
+ return {
150
+ ok: false,
151
+ error: {
152
+ code: 'LIMIT_REACHED',
153
+ message: `We retrieved ${ fetchFulfillmentOrdersLimit } open fulfillment orders, so there may be more. Please adjust the function.`,
154
+ },
155
+ data: order,
156
+ };
157
+ }
158
+
159
+ if (!fulfillable) {
160
+ return {
161
+ ok: false,
162
+ error: 'Order is not fulfillable',
163
+ data: order,
164
+ };
165
+ }
166
+
167
+ const seenTrackingNumbers = fulfillments.map(f => f?.trackingInfo?.map(t => t.number)).flat().filter(Boolean);
168
+
169
+ if (trackingInfo?.number && seenTrackingNumbers.includes(trackingInfo.number)) {
170
+ return {
171
+ ok: true,
172
+ meta: {
173
+ alreadyFulfilled: true,
174
+ trackingNumber: trackingInfo.number,
175
+ },
176
+ };
177
+ }
178
+
179
+ // if using fulfillAll, fulfill them
180
+ if (fulfillAll === true) {
181
+ return shopifyFulfillmentCreate(
182
+ credsPayload,
183
+ fulfillmentOrders.map(fulfillmentOrder => {
184
+ const {
185
+ id: fulfillmentOrderGid,
186
+ } = fulfillmentOrder;
187
+
188
+ return {
189
+ lineItemsByFulfillmentOrder: [{
190
+ fulfillmentOrderId: fulfillmentOrderGid,
191
+ }],
192
+ notifyCustomer,
193
+ originAddress,
194
+ trackingInfo,
195
+ };
196
+ }),
197
+ {
198
+ apiVersion,
199
+ },
200
+ );
201
+ }
202
+
203
+ // if using itemsBySku, iterate over unfulfilled line items and decrement until complete, making a queue of fulfillments to action
204
+ if (itemsBySku) {
205
+ const depletableItemsBySku = { ...itemsBySku };
206
+ const shopifyFulfillmentCreatePayloads = [];
207
+
208
+ for (const fulfillmentOrder of fulfillmentOrders) {
209
+ const {
210
+ id: fulfillmentOrderGid,
211
+ lineItems = [],
212
+ } = fulfillmentOrder;
213
+
214
+ if (lineItems.length >= fetchLineItemsLimit) {
215
+ return {
216
+ ok: false,
217
+ error: {
218
+ code: 'LIMIT_REACHED',
219
+ message: `We retrieved ${ fetchLineItemsLimit } line items on a fulfillment order, so there may be more. Please adjust the function.`,
220
+ },
221
+ data: order,
222
+ };
223
+ }
224
+
225
+ const fulfillmentOrderLineItems = [];
226
+
227
+ for (const lineItem of lineItems) {
228
+ const {
229
+ id: lineItemGid,
230
+ sku,
231
+ remainingQuantity,
232
+ requiresShipping,
233
+ } = lineItem;
234
+
235
+ if (!requiresShipping || remainingQuantity <= 0) {
236
+ continue;
237
+ }
238
+
239
+ const depletableQuantity = depletableItemsBySku[sku];
240
+ if (!depletableQuantity) {
241
+ continue;
242
+ }
243
+
244
+ const quantity = Math.min(remainingQuantity, depletableQuantity);
245
+ fulfillmentOrderLineItems.push({
246
+ id: lineItemGid,
247
+ quantity,
248
+ });
249
+ depletableItemsBySku[sku] -= quantity;
250
+ }
251
+
252
+ if (fulfillmentOrderLineItems.length > 0) {
253
+ shopifyFulfillmentCreatePayloads.push([
254
+ credsPayload,
255
+ {
256
+ lineItemsByFulfillmentOrder: [{
257
+ fulfillmentOrderId: fulfillmentOrderGid,
258
+ fulfillmentOrderLineItems,
259
+ }],
260
+ notifyCustomer,
261
+ originAddress,
262
+ trackingInfo,
263
+ },
264
+ {
265
+ apiVersion,
266
+ },
267
+ ]);
268
+ }
269
+ }
270
+
271
+ if (shopifyFulfillmentCreatePayloads.length === 0) {
272
+ return {
273
+ ok: false,
274
+ error: {
275
+ message: 'No fulfillable line items found',
276
+ },
277
+ data: order,
278
+ };
279
+ }
280
+
281
+ logDeep({ depletableItemsBySku, shopifyFulfillmentCreatePayloads });
282
+ await askQuestion('Continue?');
283
+
284
+ return operationQueueRunner(
285
+ shopifyFulfillmentCreate,
286
+ shopifyFulfillmentCreatePayloads,
287
+ );
288
+ }
289
+
290
+ return {
291
+ ok: false,
292
+ error: `I guess we don't do that yet`,
293
+ };
294
+ };
295
+
296
+ const funcApiConfig = {
297
+ argsWarden,
298
+ };
299
+
300
+ module.exports = {
301
+ shopifyOrderFulfill,
302
+ funcApiConfig,
303
+ };
304
+
305
+ /*
306
+ curl -X POST "http://localhost:8000/shopifyOrderFulfill" \
307
+ -H "Content-Type: application/json" \
308
+ -d `{
309
+ "credsPayload": { "credsPath": "shopify.au" },
310
+ "orderIdentifier": { "orderId": "104188477512" },
311
+ "fulfillmentPayload": {
312
+ "fulfillAll": true,
313
+ "notifyCustomer": true,
314
+ "originAddress": { "countryCode": "AU" },
315
+ "trackingInfo": {
316
+ "number": "1234567890",
317
+ "company": "Kiki's Delivery Service",
318
+ "url": "https://www.studioghibli.com.au/kikisdeliveryservice"
319
+ }
320
+ }
321
+ }`
322
+ */
@@ -0,0 +1,112 @@
1
+ // https://shopify.dev/docs/api/admin-graphql/latest/queries/theme
2
+
3
+ const { credsValidator } = require('../validators');
4
+ const { ArgsWarden, ensureArray } = require('../utils');
5
+ const { shopifyClient } = require('./shopify.utils');
6
+
7
+ const defaultAttrs = `
8
+ filename
9
+ contentType
10
+ size
11
+ body {
12
+ ... on OnlineStoreThemeFileBodyText {
13
+ content
14
+ }
15
+ ... on OnlineStoreThemeFileBodyBase64 {
16
+ contentBase64
17
+ }
18
+ ... on OnlineStoreThemeFileBodyUrl {
19
+ url
20
+ }
21
+ }
22
+ `.trim();
23
+
24
+ const argsWarden = new ArgsWarden([
25
+ ['credsPayload', credsValidator],
26
+ ['themeId'],
27
+ ['filenames'],
28
+ ]);
29
+
30
+ const shopifyThemeFileGet = async (
31
+ credsPayload,
32
+ themeId,
33
+ filenames,
34
+ {
35
+ apiVersion,
36
+ attrs = defaultAttrs,
37
+ } = {},
38
+ ) => {
39
+
40
+ const rejectResponse = await argsWarden.responseIfRejectingArgs({
41
+ credsPayload,
42
+ themeId,
43
+ filenames,
44
+ });
45
+ if (rejectResponse) {
46
+ return rejectResponse;
47
+ }
48
+
49
+ const filenamesList = ensureArray(filenames);
50
+
51
+ const response = await shopifyClient.fetch({
52
+ requestPayload: {
53
+ method: 'post',
54
+ body: {
55
+ query: `
56
+ query ThemeFiles($themeId: ID!, $filenames: [String!]!) {
57
+ theme(id: $themeId) {
58
+ files(filenames: $filenames) {
59
+ nodes {
60
+ ${ attrs }
61
+ }
62
+ userErrors {
63
+ code
64
+ filename
65
+ }
66
+ }
67
+ }
68
+ }
69
+ `,
70
+ variables: {
71
+ themeId: `gid://shopify/OnlineStoreTheme/${ themeId }`,
72
+ filenames: filenamesList,
73
+ },
74
+ },
75
+ },
76
+ context: {
77
+ credsPayload,
78
+ apiVersion,
79
+ resultPath: 'data.theme.files',
80
+ },
81
+ });
82
+
83
+ if (!response.ok) {
84
+ return response;
85
+ }
86
+
87
+ // stripEdgesAndNodes turns files.nodes into an array (userErrors on the
88
+ // connection are dropped by the shared Shopify client pipeline).
89
+ return {
90
+ ok: true,
91
+ data: ensureArray(response.data),
92
+ };
93
+ };
94
+
95
+ const funcApiConfig = {
96
+ argsWarden,
97
+ };
98
+
99
+ module.exports = {
100
+ shopifyThemeFileGet,
101
+ funcApiConfig,
102
+ };
103
+
104
+ /*
105
+ curl -X POST "http://localhost:8000/shopifyThemeFileGet" \
106
+ -H "Content-Type: application/json" \
107
+ -d '{
108
+ "credsPayload": { "credsPath": "shopify.au" },
109
+ "themeId": "129840808008",
110
+ "filenames": "templates/page.sign_up_beauty.json"
111
+ }'
112
+ */