@foxtware/mineral 0.1.40 → 1.0.1
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/README.md +68 -35
- package/api/bleckmann/bleckmann.utils.js +4 -1
- package/api/bleckmann/bleckmannAsnsGet.js +1 -0
- package/api/bleckmann/bleckmannPickticketGet.js +76 -0
- package/api/bleckmann/bleckmannPickticketsGet.js +72 -0
- package/api/linear/linearEntityExternalLinkCreate.js +81 -0
- package/api/linear/linearFileUpload.js +111 -0
- package/api/pipe17/pipe17ArrivalGet.js +12 -5
- package/api/pipe17/pipe17FulfillmentGet.js +10 -3
- package/api/pipe17/pipe17InventoryItemGet.js +7 -3
- package/api/pipe17/pipe17JobGet.js +2 -2
- package/api/pipe17/pipe17JobResultsGet.js +1 -1
- package/api/pipe17/pipe17LocationGet.js +2 -2
- package/api/pipe17/pipe17OrderGet.js +18 -4
- package/api/pipe17/pipe17ProductGet.js +2 -2
- package/api/pipe17/pipe17PurchaseGet.js +2 -2
- package/api/pipe17/pipe17ReceiptGet.js +1 -1
- package/api/pipe17/pipe17ReturnGet.js +2 -2
- package/api/shopify/shopifyGet.js +2 -0
- package/api/shopify/shopifyProductUpdateTrigger.js +16 -8
- package/api/shopify/shopifyThemeFilesGet.js +166 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
# mineral
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An everything middleware, centred around easy-to-bring credentials and instantly useful curl commands. Now importable into your project.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## How to get mineralling
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
1. From your project, install mineral: `npm i @foxtware/mineral`.
|
|
8
|
+
2. Set up a `.creds.yml` in your project directory, based on mineral's `.creds.yml.sample`. Add the credentials for the platform you want to use.
|
|
9
|
+
3. Add the `dev` command to your project's `package.json`:
|
|
10
|
+
```
|
|
11
|
+
"dev": "node --watch --watch-path=./api --watch-path=./.creds.yml --watch-path=./.env --watch-path=./hosting node_modules/@foxtware/mineral/server.js --workspace . --api_dirs api"
|
|
12
|
+
```
|
|
13
|
+
4. Run `npm run dev` to start a server.
|
|
14
|
+
5. Use an example curl command from the file whose function you want to use, with the creds path you set up.
|
|
14
15
|
|
|
15
|
-
##
|
|
16
|
+
## Key ideas
|
|
17
|
+
### The response format to rule them all
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
All functions should return data in this format.
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
19
22
|
interface Response {
|
|
20
23
|
ok: boolean
|
|
21
24
|
data?: unknown
|
|
@@ -32,28 +35,58 @@ The [bedrock](https://github.com/GorgonFreeman/bedrock) middleware, refactored f
|
|
|
32
35
|
}
|
|
33
36
|
results?: Response[]
|
|
34
37
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
```
|
|
39
|
+
- `ok` — `true` if the intent executed; `false` otherwise
|
|
40
|
+
- `data` — any info returned
|
|
41
|
+
- `error` — when present, multiple errors go in `error.details`
|
|
42
|
+
- `meta` — pagination, total items, etc.
|
|
43
|
+
- `results` — array of responses from a queue or batch of calls
|
|
44
|
+
- [Examples](_docs/standard_response_examples.md)
|
|
45
|
+
|
|
46
|
+
### BYO creds
|
|
47
|
+
|
|
48
|
+
Each function accepts an object like this, supplying auth info for each platform it contacts:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
{
|
|
52
|
+
credsPath,
|
|
53
|
+
credsObject,
|
|
54
|
+
credsProvider, // function that returns a credsObject
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
You can set up a `.creds.yml` in your workspace directory and refer to it using `credsPath` > `platform.account`, or supply credentials inline using `credsObject`.
|
|
59
|
+
|
|
60
|
+
### Monorepo context
|
|
61
|
+
|
|
62
|
+
Mineral gets pushed to from a larger repo. Private functions are contained in a sibling project, which imports mineral to power all of the core functionalities, and layers certain business logic on top - e.g. known creds paths.
|
|
63
|
+
|
|
64
|
+
Mineral is strictly core functionalities, useful for anyone.
|
|
65
|
+
|
|
66
|
+
Pass `--workspace` to locate `.creds.yml` and `--api_dirs` to serve additional function directories.
|
|
67
|
+
|
|
68
|
+
### Hosting
|
|
69
|
+
|
|
70
|
+
For cloud deploy, workspaces use `hosting/.hosting.yml` and `npm run host` (same `--workspace` / `--api_dirs` flags as dev/serve). See `hosting/.hosting.yml.sample`.
|
|
71
|
+
|
|
72
|
+
In the hosting YML, you can use wrappers which are additional layers to your hosted function. For instance, you may want to require an API key in requests to your middleware while it's live, or, require a hash in the headers that matches a hash of the body.
|
|
73
|
+
|
|
74
|
+
The decoupled nature of these desires to what the function actually does and their broad application is why they're implemented in the hosting config itself, rather than in a custom instance of the function.
|
|
75
|
+
|
|
76
|
+
### AI use
|
|
77
|
+
|
|
78
|
+
Not gonna lie, I have absolutely composed some of these platforms with AI. Some of them may not work, some may not stick closely to the core principles, some may use awkward auth methods - some error payloads are definitely not optimised. However, know that I have hand-coded precursors to this repo, that do largely the same thing, and that any additions benefit (or suffer) from the structure that is laid out in the earlier platforms.
|
|
79
|
+
|
|
80
|
+
### Function arguments shape
|
|
81
|
+
|
|
82
|
+
- Bound arguments, if they exist, are first
|
|
83
|
+
- Mandatory inputs are passed as separate arguments
|
|
84
|
+
- Optional inputs are passed in one 'options' object on the end, which falls back to `{}` if not supplied
|
|
53
85
|
|
|
54
|
-
|
|
86
|
+
This looks like:
|
|
87
|
+
```
|
|
88
|
+
const attack = (name, damage, accuracy, pp, type, { effects } = {}) => {
|
|
89
|
+
```
|
|
90
|
+
where 'effects' is optional and the others are mandatory.
|
|
55
91
|
|
|
56
|
-
|
|
57
|
-
- Server makes functions available from the api/ route, where an export matches the filename. Run `npm run serve`, and they're all curlable.
|
|
58
|
-
- .creds.yml is copied into .env when deploying, so creds can be accessed while hosted. Locally, it reads from the file directly.
|
|
59
|
-
- Cloud deploy reads `hosting/.hosting.yml` for per-function config — `before_wrappers` / `after_wrappers` like `requireHostedApiKey`, `max_instances`, schedules — and deploys each function to Google Cloud.
|
|
92
|
+
This documents what options are available, and allows invocations to only supply meaningful inputs.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { BASE_URL } = require('../bleckmann/bleckmann.constants');
|
|
2
2
|
const { resolveCreds, useBaseUrl } = require('../pipelineSteps');
|
|
3
|
-
const { FetchClient } = require('../utils');
|
|
3
|
+
const { FetchClient, fetchClientCommonSteps } = require('../utils');
|
|
4
4
|
|
|
5
5
|
const useAuthHeaders = async (state) => {
|
|
6
6
|
const { requestPayload, context } = state;
|
|
@@ -24,6 +24,9 @@ const bleckmannClient = new FetchClient({
|
|
|
24
24
|
useAuthHeaders,
|
|
25
25
|
useBaseUrl(BASE_URL),
|
|
26
26
|
'fetch',
|
|
27
|
+
fetchClientCommonSteps.exitEarlyOnNotOk,
|
|
28
|
+
// Bleckmann wraps payloads in `data` — handlers opt in with context.resultPath
|
|
29
|
+
fetchClientCommonSteps.digToPath,
|
|
27
30
|
],
|
|
28
31
|
});
|
|
29
32
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// https://app.swaggerhub.com/apis-docs/Bleckmann/warehousing/1.5.2#/PICKTICKET/getPickticketForId
|
|
2
|
+
|
|
3
|
+
const { credsValidator } = require('../validators');
|
|
4
|
+
const { ArgsWarden, objHasAny, oneFromManyInResponse } = require('../utils');
|
|
5
|
+
const { bleckmannClient } = require('../bleckmann/bleckmann.utils');
|
|
6
|
+
const { bleckmannPickticketsGet } = require('../bleckmann/bleckmannPickticketsGet');
|
|
7
|
+
|
|
8
|
+
const pickticketIdentifierValidator = (pickticketIdentifier) => {
|
|
9
|
+
return objHasAny(pickticketIdentifier, ['pickticketId', 'pickticketReference']);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const argsWarden = new ArgsWarden([
|
|
13
|
+
['credsPayload', credsValidator],
|
|
14
|
+
['pickticketIdentifier', pickticketIdentifierValidator],
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
const bleckmannPickticketGet = async (
|
|
18
|
+
credsPayload,
|
|
19
|
+
pickticketIdentifier,
|
|
20
|
+
{
|
|
21
|
+
fetchClient = bleckmannClient,
|
|
22
|
+
} = {},
|
|
23
|
+
) => {
|
|
24
|
+
|
|
25
|
+
const rejectResponse = await argsWarden.responseIfRejectingArgs({
|
|
26
|
+
credsPayload,
|
|
27
|
+
pickticketIdentifier,
|
|
28
|
+
});
|
|
29
|
+
if (rejectResponse) {
|
|
30
|
+
return rejectResponse;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const {
|
|
34
|
+
pickticketId,
|
|
35
|
+
pickticketReference,
|
|
36
|
+
} = pickticketIdentifier;
|
|
37
|
+
|
|
38
|
+
if (pickticketId) {
|
|
39
|
+
return fetchClient.fetch({
|
|
40
|
+
requestPayload: {
|
|
41
|
+
url: `/warehousing/picktickets/${ pickticketId }`,
|
|
42
|
+
},
|
|
43
|
+
context: {
|
|
44
|
+
credsPayload,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const response = await bleckmannPickticketsGet(
|
|
50
|
+
credsPayload,
|
|
51
|
+
{
|
|
52
|
+
reference: pickticketReference,
|
|
53
|
+
fetchClient,
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return oneFromManyInResponse(response, 'reference', pickticketReference);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const funcApiConfig = {
|
|
61
|
+
argsWarden,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
bleckmannPickticketGet,
|
|
66
|
+
funcApiConfig,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
curl -X POST "http://localhost:8000/bleckmannPickticketGet" \
|
|
71
|
+
-H "Content-Type: application/json" \
|
|
72
|
+
-d '{
|
|
73
|
+
"credsPayload": { "credsPath": "bleckmann" },
|
|
74
|
+
"pickticketIdentifier": { "pickticketReference": "1234567890" }
|
|
75
|
+
}'
|
|
76
|
+
*/
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// https://app.swaggerhub.com/apis-docs/Bleckmann/warehousing/1.5.2#/PICKTICKET/getPicktickets
|
|
2
|
+
|
|
3
|
+
const { credsValidator } = require('../validators');
|
|
4
|
+
const { ArgsWarden } = require('../utils');
|
|
5
|
+
const { MAX_PER_PAGE } = require('../bleckmann/bleckmann.constants');
|
|
6
|
+
const { bleckmannClient } = require('../bleckmann/bleckmann.utils');
|
|
7
|
+
|
|
8
|
+
const argsWarden = new ArgsWarden([
|
|
9
|
+
['credsPayload', credsValidator],
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
const bleckmannPickticketsGet = async (
|
|
13
|
+
credsPayload,
|
|
14
|
+
{
|
|
15
|
+
createdFrom,
|
|
16
|
+
createdTo,
|
|
17
|
+
shippedFrom,
|
|
18
|
+
shippedTo,
|
|
19
|
+
// status default is INPROGRESS in the API; use ANY unless provided
|
|
20
|
+
status = 'ANY',
|
|
21
|
+
reference,
|
|
22
|
+
customerReference,
|
|
23
|
+
perPage = MAX_PER_PAGE,
|
|
24
|
+
fetchClient = bleckmannClient,
|
|
25
|
+
} = {},
|
|
26
|
+
) => {
|
|
27
|
+
|
|
28
|
+
const rejectResponse = await argsWarden.responseIfRejectingArgs({
|
|
29
|
+
credsPayload,
|
|
30
|
+
});
|
|
31
|
+
if (rejectResponse) {
|
|
32
|
+
return rejectResponse;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return fetchClient.fetch({
|
|
36
|
+
requestPayload: {
|
|
37
|
+
url: '/warehousing/picktickets',
|
|
38
|
+
params: {
|
|
39
|
+
limit: perPage,
|
|
40
|
+
...(createdFrom && { createdFrom }),
|
|
41
|
+
...(createdTo && { createdTo }),
|
|
42
|
+
...(shippedFrom && { shippedFrom }),
|
|
43
|
+
...(shippedTo && { shippedTo }),
|
|
44
|
+
...(status && { status }),
|
|
45
|
+
...(reference && { reference }),
|
|
46
|
+
...(customerReference && { customerReference }),
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
context: {
|
|
50
|
+
credsPayload,
|
|
51
|
+
resultPath: 'data',
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const funcApiConfig = {
|
|
57
|
+
argsWarden,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
module.exports = {
|
|
61
|
+
bleckmannPickticketsGet,
|
|
62
|
+
funcApiConfig,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/*
|
|
66
|
+
curl -X POST "http://localhost:8000/bleckmannPickticketsGet" \
|
|
67
|
+
-H "Content-Type: application/json" \
|
|
68
|
+
-d '{
|
|
69
|
+
"credsPayload": { "credsPath": "bleckmann" },
|
|
70
|
+
"options": { "perPage": 20 }
|
|
71
|
+
}'
|
|
72
|
+
*/
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// https://linear.app/developers/graphql
|
|
2
|
+
|
|
3
|
+
const { ArgsWarden } = require('../utils');
|
|
4
|
+
const { credsValidator } = require('../validators');
|
|
5
|
+
const { linearClient } = require('../linear/linear.utils');
|
|
6
|
+
|
|
7
|
+
const inputValidator = (input) => Boolean(input?.url);
|
|
8
|
+
|
|
9
|
+
const argsWarden = new ArgsWarden([
|
|
10
|
+
['credsPayload', credsValidator],
|
|
11
|
+
['input', inputValidator],
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
const linearEntityExternalLinkCreate = async (
|
|
15
|
+
credsPayload,
|
|
16
|
+
input,
|
|
17
|
+
{
|
|
18
|
+
inspect = false,
|
|
19
|
+
fetchClient = linearClient,
|
|
20
|
+
} = {},
|
|
21
|
+
) => {
|
|
22
|
+
|
|
23
|
+
const rejectResponse = await argsWarden.responseIfRejectingArgs({
|
|
24
|
+
credsPayload,
|
|
25
|
+
input,
|
|
26
|
+
});
|
|
27
|
+
if (rejectResponse) {
|
|
28
|
+
return rejectResponse;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return fetchClient.fetch({
|
|
32
|
+
requestPayload: {
|
|
33
|
+
body: {
|
|
34
|
+
query: `
|
|
35
|
+
mutation EntityExternalLinkCreate($input: EntityExternalLinkCreateInput!) {
|
|
36
|
+
entityExternalLinkCreate(input: $input) {
|
|
37
|
+
success
|
|
38
|
+
entityExternalLink {
|
|
39
|
+
id
|
|
40
|
+
label
|
|
41
|
+
url
|
|
42
|
+
sortOrder
|
|
43
|
+
createdAt
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`,
|
|
48
|
+
variables: {
|
|
49
|
+
input,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
context: {
|
|
54
|
+
credsPayload,
|
|
55
|
+
resultPath: 'data.entityExternalLinkCreate',
|
|
56
|
+
},
|
|
57
|
+
inspect,
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const funcApiConfig = {
|
|
62
|
+
argsWarden,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
linearEntityExternalLinkCreate,
|
|
67
|
+
funcApiConfig,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/*
|
|
71
|
+
curl -X POST "http://localhost:8000/linearEntityExternalLinkCreate" \
|
|
72
|
+
-H "Content-Type: application/json" \
|
|
73
|
+
-d '{
|
|
74
|
+
"credsPayload": { "credsPath": "linear" },
|
|
75
|
+
"input": {
|
|
76
|
+
"projectId": "64e37bd2-af8c-4142-932a-66441b75311b",
|
|
77
|
+
"url": "https://example.com",
|
|
78
|
+
"label": "Example link"
|
|
79
|
+
}
|
|
80
|
+
}'
|
|
81
|
+
*/
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// https://linear.app/developers/graphql
|
|
2
|
+
|
|
3
|
+
const { ArgsWarden } = require('../utils');
|
|
4
|
+
const { credsValidator } = require('../validators');
|
|
5
|
+
const { linearClient } = require('../linear/linear.utils');
|
|
6
|
+
|
|
7
|
+
const argsWarden = new ArgsWarden([
|
|
8
|
+
['credsPayload', credsValidator],
|
|
9
|
+
['filename'],
|
|
10
|
+
['contentType'],
|
|
11
|
+
['size', (size) => Number.isInteger(size) && size > 0],
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
const linearFileUpload = async (
|
|
15
|
+
credsPayload,
|
|
16
|
+
filename,
|
|
17
|
+
contentType,
|
|
18
|
+
size,
|
|
19
|
+
{
|
|
20
|
+
metaData,
|
|
21
|
+
makePublic,
|
|
22
|
+
inspect = false,
|
|
23
|
+
fetchClient = linearClient,
|
|
24
|
+
} = {},
|
|
25
|
+
) => {
|
|
26
|
+
|
|
27
|
+
const rejectResponse = await argsWarden.responseIfRejectingArgs({
|
|
28
|
+
credsPayload,
|
|
29
|
+
filename,
|
|
30
|
+
contentType,
|
|
31
|
+
size,
|
|
32
|
+
});
|
|
33
|
+
if (rejectResponse) {
|
|
34
|
+
return rejectResponse;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return fetchClient.fetch({
|
|
38
|
+
requestPayload: {
|
|
39
|
+
body: {
|
|
40
|
+
query: `
|
|
41
|
+
mutation FileUpload(
|
|
42
|
+
$filename: String!
|
|
43
|
+
$contentType: String!
|
|
44
|
+
$size: Int!
|
|
45
|
+
$metaData: JSON
|
|
46
|
+
$makePublic: Boolean
|
|
47
|
+
) {
|
|
48
|
+
fileUpload(
|
|
49
|
+
filename: $filename
|
|
50
|
+
contentType: $contentType
|
|
51
|
+
size: $size
|
|
52
|
+
metaData: $metaData
|
|
53
|
+
makePublic: $makePublic
|
|
54
|
+
) {
|
|
55
|
+
success
|
|
56
|
+
uploadFile {
|
|
57
|
+
filename
|
|
58
|
+
contentType
|
|
59
|
+
size
|
|
60
|
+
uploadUrl
|
|
61
|
+
assetUrl
|
|
62
|
+
headers {
|
|
63
|
+
key
|
|
64
|
+
value
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
`,
|
|
70
|
+
variables: {
|
|
71
|
+
filename,
|
|
72
|
+
contentType,
|
|
73
|
+
size,
|
|
74
|
+
metaData,
|
|
75
|
+
makePublic,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
context: {
|
|
80
|
+
credsPayload,
|
|
81
|
+
resultPath: 'data.fileUpload',
|
|
82
|
+
},
|
|
83
|
+
inspect,
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const funcApiConfig = {
|
|
88
|
+
argsWarden,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
module.exports = {
|
|
92
|
+
linearFileUpload,
|
|
93
|
+
funcApiConfig,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
Returns a pre-signed `uploadUrl` to PUT the file to, plus the `headers` that
|
|
98
|
+
the PUT must carry. `assetUrl` is the resulting Linear-hosted URL, suitable
|
|
99
|
+
for linearAttachmentCreate or embedding in comment markdown.
|
|
100
|
+
*/
|
|
101
|
+
|
|
102
|
+
/*
|
|
103
|
+
curl -X POST "http://localhost:8000/linearFileUpload" \
|
|
104
|
+
-H "Content-Type: application/json" \
|
|
105
|
+
-d '{
|
|
106
|
+
"credsPayload": { "credsPath": "linear" },
|
|
107
|
+
"filename": "assets.zip",
|
|
108
|
+
"contentType": "application/zip",
|
|
109
|
+
"size": 12345
|
|
110
|
+
}'
|
|
111
|
+
*/
|
|
@@ -25,9 +25,9 @@ const resolveArrivalId = ({
|
|
|
25
25
|
extReferenceId,
|
|
26
26
|
}) => {
|
|
27
27
|
return arrivalId
|
|
28
|
-
|| (extArrivalId ? `ext:${ extArrivalId }` : null)
|
|
29
|
-
|| (extArrivalApiId ? `api:${ extArrivalApiId }` : null)
|
|
30
|
-
|| (extReferenceId ? `ref:${ extReferenceId }` : null);
|
|
28
|
+
|| (extArrivalId ? `ext:${ encodeURIComponent(extArrivalId) }` : null)
|
|
29
|
+
|| (extArrivalApiId ? `api:${ encodeURIComponent(extArrivalApiId) }` : null)
|
|
30
|
+
|| (extReferenceId ? `ref:${ encodeURIComponent(extReferenceId) }` : null);
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
const pipe17ArrivalGet = async (
|
|
@@ -52,7 +52,7 @@ const pipe17ArrivalGet = async (
|
|
|
52
52
|
'/arrivals',
|
|
53
53
|
resolveArrivalId(arrivalIdentifier),
|
|
54
54
|
{
|
|
55
|
-
resultPath: '
|
|
55
|
+
resultPath: 'arrival',
|
|
56
56
|
inspect,
|
|
57
57
|
fetchClient,
|
|
58
58
|
},
|
|
@@ -73,6 +73,13 @@ curl -X POST "http://localhost:8000/pipe17ArrivalGet" \
|
|
|
73
73
|
-H "Content-Type: application/json" \
|
|
74
74
|
-d '{
|
|
75
75
|
"credsPayload": { "credsPath": "pipe17" },
|
|
76
|
-
"arrivalIdentifier": { "arrivalId": "
|
|
76
|
+
"arrivalIdentifier": { "arrivalId": "8f146f632e55fd21" }
|
|
77
|
+
}'
|
|
78
|
+
|
|
79
|
+
curl -X POST "http://localhost:8000/pipe17ArrivalGet" \
|
|
80
|
+
-H "Content-Type: application/json" \
|
|
81
|
+
-d '{
|
|
82
|
+
"credsPayload": { "credsPath": "pipe17" },
|
|
83
|
+
"arrivalIdentifier": { "extArrivalId": "US-WF-021" }
|
|
77
84
|
}'
|
|
78
85
|
*/
|
|
@@ -18,7 +18,7 @@ const resolveFulfillmentId = ({
|
|
|
18
18
|
extFulfillmentId,
|
|
19
19
|
}) => {
|
|
20
20
|
return fulfillmentId
|
|
21
|
-
|| (extFulfillmentId ? `ext:${ extFulfillmentId }` : null);
|
|
21
|
+
|| (extFulfillmentId ? `ext:${ encodeURIComponent(extFulfillmentId) }` : null);
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
const pipe17FulfillmentGet = async (
|
|
@@ -43,7 +43,7 @@ const pipe17FulfillmentGet = async (
|
|
|
43
43
|
'/fulfillments',
|
|
44
44
|
resolveFulfillmentId(fulfillmentIdentifier),
|
|
45
45
|
{
|
|
46
|
-
resultPath: '
|
|
46
|
+
resultPath: 'fulfillment',
|
|
47
47
|
inspect,
|
|
48
48
|
fetchClient,
|
|
49
49
|
},
|
|
@@ -64,6 +64,13 @@ curl -X POST "http://localhost:8000/pipe17FulfillmentGet" \
|
|
|
64
64
|
-H "Content-Type: application/json" \
|
|
65
65
|
-d '{
|
|
66
66
|
"credsPayload": { "credsPath": "pipe17" },
|
|
67
|
-
"fulfillmentIdentifier": { "fulfillmentId": "
|
|
67
|
+
"fulfillmentIdentifier": { "fulfillmentId": "171889ac4c86b805" }
|
|
68
|
+
}'
|
|
69
|
+
|
|
70
|
+
curl -X POST "http://localhost:8000/pipe17FulfillmentGet" \
|
|
71
|
+
-H "Content-Type: application/json" \
|
|
72
|
+
-d '{
|
|
73
|
+
"credsPayload": { "credsPath": "pipe17" },
|
|
74
|
+
"fulfillmentIdentifier": { "extFulfillmentId": "bb558b1527b95c99:1Z179A841391432104" }
|
|
68
75
|
}'
|
|
69
76
|
*/
|
|
@@ -31,7 +31,7 @@ const pipe17InventoryItemGet = async (
|
|
|
31
31
|
'/inventory',
|
|
32
32
|
inventoryItemId,
|
|
33
33
|
{
|
|
34
|
-
resultPath: '
|
|
34
|
+
resultPath: 'inventory',
|
|
35
35
|
inspect,
|
|
36
36
|
fetchClient,
|
|
37
37
|
},
|
|
@@ -48,10 +48,14 @@ module.exports = {
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
/*
|
|
51
|
-
|
|
51
|
+
NOTE: no working call found. Pipe17 inventory records carry no id of their own — they are
|
|
52
|
+
keyed by sku + locationId — and GET /inventory/{id} returns 404 "No results" for a sku or a
|
|
53
|
+
locationId. Fetch by sku through pipe17InventoryItemsGet instead (one row per location):
|
|
54
|
+
|
|
55
|
+
curl -X POST "http://localhost:8000/pipe17InventoryItemsGet" \
|
|
52
56
|
-H "Content-Type: application/json" \
|
|
53
57
|
-d '{
|
|
54
58
|
"credsPayload": { "credsPath": "pipe17" },
|
|
55
|
-
"
|
|
59
|
+
"options": { "params": { "sku": "EXDAL2449-5-XS" } }
|
|
56
60
|
}'
|
|
57
61
|
*/
|
|
@@ -31,7 +31,7 @@ const pipe17JobGet = async (
|
|
|
31
31
|
'/jobs',
|
|
32
32
|
jobId,
|
|
33
33
|
{
|
|
34
|
-
resultPath: '
|
|
34
|
+
resultPath: 'job',
|
|
35
35
|
inspect,
|
|
36
36
|
fetchClient,
|
|
37
37
|
},
|
|
@@ -52,6 +52,6 @@ curl -X POST "http://localhost:8000/pipe17JobGet" \
|
|
|
52
52
|
-H "Content-Type: application/json" \
|
|
53
53
|
-d '{
|
|
54
54
|
"credsPayload": { "credsPath": "pipe17" },
|
|
55
|
-
"jobId": "
|
|
55
|
+
"jobId": "94fc44fdbe6ad3ae"
|
|
56
56
|
}'
|
|
57
57
|
*/
|
|
@@ -31,7 +31,7 @@ const pipe17LocationGet = async (
|
|
|
31
31
|
'/locations',
|
|
32
32
|
locationId,
|
|
33
33
|
{
|
|
34
|
-
resultPath: '
|
|
34
|
+
resultPath: 'location',
|
|
35
35
|
inspect,
|
|
36
36
|
fetchClient,
|
|
37
37
|
},
|
|
@@ -52,6 +52,6 @@ curl -X POST "http://localhost:8000/pipe17LocationGet" \
|
|
|
52
52
|
-H "Content-Type: application/json" \
|
|
53
53
|
-d '{
|
|
54
54
|
"credsPayload": { "credsPath": "pipe17" },
|
|
55
|
-
"locationId": "
|
|
55
|
+
"locationId": "727db407e94815a6"
|
|
56
56
|
}'
|
|
57
57
|
*/
|
|
@@ -19,8 +19,8 @@ const resolveOrderId = ({
|
|
|
19
19
|
extOrderApiId,
|
|
20
20
|
}) => {
|
|
21
21
|
return orderId
|
|
22
|
-
|| (extOrderId ? `ext:${ extOrderId }` : null)
|
|
23
|
-
|| (extOrderApiId ? `api:${ extOrderApiId }` : null);
|
|
22
|
+
|| (extOrderId ? `ext:${ encodeURIComponent(extOrderId) }` : null)
|
|
23
|
+
|| (extOrderApiId ? `api:${ encodeURIComponent(extOrderApiId) }` : null);
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
const pipe17OrderGet = async (
|
|
@@ -45,7 +45,7 @@ const pipe17OrderGet = async (
|
|
|
45
45
|
'/orders',
|
|
46
46
|
resolveOrderId(orderIdentifier),
|
|
47
47
|
{
|
|
48
|
-
resultPath: '
|
|
48
|
+
resultPath: 'order',
|
|
49
49
|
inspect,
|
|
50
50
|
fetchClient,
|
|
51
51
|
},
|
|
@@ -66,6 +66,20 @@ curl -X POST "http://localhost:8000/pipe17OrderGet" \
|
|
|
66
66
|
-H "Content-Type: application/json" \
|
|
67
67
|
-d '{
|
|
68
68
|
"credsPayload": { "credsPath": "pipe17" },
|
|
69
|
-
"orderIdentifier": { "orderId": "
|
|
69
|
+
"orderIdentifier": { "orderId": "243acd2642101951" }
|
|
70
|
+
}'
|
|
71
|
+
|
|
72
|
+
curl -X POST "http://localhost:8000/pipe17OrderGet" \
|
|
73
|
+
-H "Content-Type: application/json" \
|
|
74
|
+
-d '{
|
|
75
|
+
"credsPayload": { "credsPath": "pipe17" },
|
|
76
|
+
"orderIdentifier": { "extOrderId": "#USA4162713" }
|
|
77
|
+
}'
|
|
78
|
+
|
|
79
|
+
curl -X POST "http://localhost:8000/pipe17OrderGet" \
|
|
80
|
+
-H "Content-Type: application/json" \
|
|
81
|
+
-d '{
|
|
82
|
+
"credsPayload": { "credsPath": "pipe17" },
|
|
83
|
+
"orderIdentifier": { "extOrderApiId": "5820180987964" }
|
|
70
84
|
}'
|
|
71
85
|
*/
|
|
@@ -31,7 +31,7 @@ const pipe17ProductGet = async (
|
|
|
31
31
|
'/products',
|
|
32
32
|
productId,
|
|
33
33
|
{
|
|
34
|
-
resultPath: '
|
|
34
|
+
resultPath: 'product',
|
|
35
35
|
inspect,
|
|
36
36
|
fetchClient,
|
|
37
37
|
},
|
|
@@ -52,6 +52,6 @@ curl -X POST "http://localhost:8000/pipe17ProductGet" \
|
|
|
52
52
|
-H "Content-Type: application/json" \
|
|
53
53
|
-d '{
|
|
54
54
|
"credsPayload": { "credsPath": "pipe17" },
|
|
55
|
-
"productId": "
|
|
55
|
+
"productId": "3f91e016e3d433e8"
|
|
56
56
|
}'
|
|
57
57
|
*/
|
|
@@ -31,7 +31,7 @@ const pipe17PurchaseGet = async (
|
|
|
31
31
|
'/purchases',
|
|
32
32
|
purchaseId,
|
|
33
33
|
{
|
|
34
|
-
resultPath: '
|
|
34
|
+
resultPath: 'purchase',
|
|
35
35
|
inspect,
|
|
36
36
|
fetchClient,
|
|
37
37
|
},
|
|
@@ -52,6 +52,6 @@ curl -X POST "http://localhost:8000/pipe17PurchaseGet" \
|
|
|
52
52
|
-H "Content-Type: application/json" \
|
|
53
53
|
-d '{
|
|
54
54
|
"credsPayload": { "credsPath": "pipe17" },
|
|
55
|
-
"purchaseId": "
|
|
55
|
+
"purchaseId": "075e2df5318aa93e"
|
|
56
56
|
}'
|
|
57
57
|
*/
|
|
@@ -31,7 +31,7 @@ const pipe17ReturnGet = async (
|
|
|
31
31
|
'/returns',
|
|
32
32
|
returnId,
|
|
33
33
|
{
|
|
34
|
-
resultPath: '
|
|
34
|
+
resultPath: 'return',
|
|
35
35
|
inspect,
|
|
36
36
|
fetchClient,
|
|
37
37
|
},
|
|
@@ -52,6 +52,6 @@ curl -X POST "http://localhost:8000/pipe17ReturnGet" \
|
|
|
52
52
|
-H "Content-Type: application/json" \
|
|
53
53
|
-d '{
|
|
54
54
|
"credsPayload": { "credsPath": "pipe17" },
|
|
55
|
-
"returnId": "
|
|
55
|
+
"returnId": "b66396c696171143"
|
|
56
56
|
}'
|
|
57
57
|
*/
|
|
@@ -6,32 +6,41 @@ const { ArgsWarden } = require('../utils');
|
|
|
6
6
|
const { shopifyProductGet, productIdentifierValidator } = require('../shopify/shopifyProductGet');
|
|
7
7
|
const { shopifyProductUpdate } = require('../shopify/shopifyProductUpdate');
|
|
8
8
|
|
|
9
|
+
const productPayloadValidator = (productPayload) => {
|
|
10
|
+
const { productIdentifier, productTitle } = productPayload;
|
|
11
|
+
return productIdentifierValidator(productIdentifier);
|
|
12
|
+
};
|
|
13
|
+
|
|
9
14
|
const argsWarden = new ArgsWarden([
|
|
10
15
|
['credsPayload', credsValidator],
|
|
11
|
-
['
|
|
16
|
+
['productPayload', productPayloadValidator],
|
|
12
17
|
]);
|
|
13
18
|
|
|
14
19
|
const shopifyProductUpdateTrigger = async (
|
|
15
20
|
credsPayload,
|
|
16
|
-
|
|
21
|
+
productPayload,
|
|
17
22
|
{
|
|
18
23
|
apiVersion,
|
|
19
|
-
productTitle,
|
|
20
24
|
} = {},
|
|
21
25
|
) => {
|
|
22
26
|
|
|
23
27
|
const rejectResponse = await argsWarden.responseIfRejectingArgs({
|
|
24
28
|
credsPayload,
|
|
25
|
-
|
|
29
|
+
productPayload,
|
|
26
30
|
});
|
|
27
31
|
if (rejectResponse) {
|
|
28
32
|
return rejectResponse;
|
|
29
33
|
}
|
|
30
34
|
|
|
35
|
+
const {
|
|
36
|
+
productIdentifier,
|
|
37
|
+
productTitle,
|
|
38
|
+
} = productPayload;
|
|
39
|
+
|
|
31
40
|
if (!productTitle) {
|
|
32
41
|
const productResponse = await shopifyProductGet(
|
|
33
42
|
credsPayload,
|
|
34
|
-
|
|
43
|
+
productPayload,
|
|
35
44
|
{
|
|
36
45
|
attrs: 'title',
|
|
37
46
|
},
|
|
@@ -83,14 +92,13 @@ module.exports = {
|
|
|
83
92
|
-H "Content-Type: application/json" \
|
|
84
93
|
-d '{
|
|
85
94
|
"credsPayload": { "credsPath": "shopify.au" },
|
|
86
|
-
"productIdentifier": { "productId": "1234567890" }
|
|
95
|
+
"productPayload": { "productIdentifier": { "productId": "1234567890" } }
|
|
87
96
|
}'
|
|
88
97
|
|
|
89
98
|
curl -X POST "http://localhost:8000/shopifyProductUpdateTrigger" \
|
|
90
99
|
-H "Content-Type: application/json" \
|
|
91
100
|
-d '{
|
|
92
101
|
"credsPayload": { "credsPath": "shopify.au" },
|
|
93
|
-
"productIdentifier": { "productId": "1234567890" },
|
|
94
|
-
"options": { "productTitle": "Ultra Strength Freeze Ray" }
|
|
102
|
+
"productPayload": { "productIdentifier": { "productId": "1234567890" }, "productTitle": "Ultra Strength Freeze Ray" },
|
|
95
103
|
}'
|
|
96
104
|
*/
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// https://shopify.dev/docs/api/admin-graphql/latest/objects/OnlineStoreTheme#connection-files
|
|
2
|
+
|
|
3
|
+
const { ArgsWarden, Getter, ensureArray, getWithLocalCachedFile } = require('../utils');
|
|
4
|
+
const { credsValidator } = require('../validators');
|
|
5
|
+
const { shopifyClient } = require('./shopify.utils');
|
|
6
|
+
const { shopifyGetPaginator, shopifyGetDigester } = require('./shopifyGet');
|
|
7
|
+
const { MAX_PER_PAGE } = require('./shopify.constants');
|
|
8
|
+
|
|
9
|
+
const defaultAttrs = 'filename';
|
|
10
|
+
|
|
11
|
+
const argsWarden = new ArgsWarden([
|
|
12
|
+
['credsPayload', credsValidator],
|
|
13
|
+
['themeId'],
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const shopifyThemeFilesGetPacket = async (
|
|
17
|
+
credsPayload,
|
|
18
|
+
themeId,
|
|
19
|
+
attrs,
|
|
20
|
+
{
|
|
21
|
+
apiVersion,
|
|
22
|
+
|
|
23
|
+
perPage = MAX_PER_PAGE,
|
|
24
|
+
cursor,
|
|
25
|
+
// Supports globs, e.g. templates/page.*
|
|
26
|
+
filenames,
|
|
27
|
+
} = {},
|
|
28
|
+
) => {
|
|
29
|
+
|
|
30
|
+
return await shopifyClient.fetch({
|
|
31
|
+
requestPayload: {
|
|
32
|
+
method: 'post',
|
|
33
|
+
body: {
|
|
34
|
+
query: `
|
|
35
|
+
query GetThemeFiles (
|
|
36
|
+
$themeId: ID!
|
|
37
|
+
$first: Int!
|
|
38
|
+
$cursor: String
|
|
39
|
+
${ filenames ? '$filenames: [String!]' : '' }
|
|
40
|
+
) {
|
|
41
|
+
theme(id: $themeId) {
|
|
42
|
+
files(
|
|
43
|
+
first: $first
|
|
44
|
+
after: $cursor
|
|
45
|
+
${ filenames ? 'filenames: $filenames' : '' }
|
|
46
|
+
) {
|
|
47
|
+
nodes {
|
|
48
|
+
${ attrs }
|
|
49
|
+
}
|
|
50
|
+
pageInfo {
|
|
51
|
+
hasNextPage
|
|
52
|
+
endCursor
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
`,
|
|
58
|
+
variables: {
|
|
59
|
+
themeId: `gid://shopify/OnlineStoreTheme/${ themeId }`,
|
|
60
|
+
first: perPage,
|
|
61
|
+
cursor,
|
|
62
|
+
...filenames && { filenames: ensureArray(filenames) },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
context: {
|
|
67
|
+
credsPayload,
|
|
68
|
+
apiVersion,
|
|
69
|
+
resultPath: 'data.theme.files',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const shopifyThemeFilesGet = async (
|
|
75
|
+
returnGetter, // Always bound
|
|
76
|
+
|
|
77
|
+
credsPayload,
|
|
78
|
+
themeId,
|
|
79
|
+
{
|
|
80
|
+
apiVersion,
|
|
81
|
+
|
|
82
|
+
attrs = defaultAttrs,
|
|
83
|
+
perPage = MAX_PER_PAGE,
|
|
84
|
+
cursor,
|
|
85
|
+
filenames,
|
|
86
|
+
|
|
87
|
+
useLocalCachedFile,
|
|
88
|
+
...getterOptions // e.g. limit
|
|
89
|
+
} = {},
|
|
90
|
+
) => {
|
|
91
|
+
|
|
92
|
+
const rejectResponse = await argsWarden.responseIfRejectingArgs({
|
|
93
|
+
credsPayload,
|
|
94
|
+
themeId,
|
|
95
|
+
});
|
|
96
|
+
if (rejectResponse) {
|
|
97
|
+
return rejectResponse;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const getter = new Getter(
|
|
101
|
+
{
|
|
102
|
+
args: [
|
|
103
|
+
credsPayload,
|
|
104
|
+
themeId,
|
|
105
|
+
attrs,
|
|
106
|
+
],
|
|
107
|
+
options: {
|
|
108
|
+
apiVersion,
|
|
109
|
+
perPage,
|
|
110
|
+
cursor,
|
|
111
|
+
filenames,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
func: shopifyThemeFilesGetPacket,
|
|
116
|
+
digester: shopifyGetDigester,
|
|
117
|
+
paginator: shopifyGetPaginator,
|
|
118
|
+
...getterOptions,
|
|
119
|
+
},
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if (returnGetter) {
|
|
123
|
+
return getter;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return getWithLocalCachedFile(
|
|
127
|
+
useLocalCachedFile,
|
|
128
|
+
async () => {
|
|
129
|
+
const data = await getter.run({ returnAll: true });
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
ok: true,
|
|
133
|
+
data,
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const funcApiConfig = {
|
|
140
|
+
argsWarden,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
module.exports = {
|
|
144
|
+
shopifyThemeFilesGet: (...args) => shopifyThemeFilesGet(false, ...args),
|
|
145
|
+
shopifyThemeFilesGetter: (...args) => shopifyThemeFilesGet(true, ...args),
|
|
146
|
+
funcApiConfig,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/*
|
|
150
|
+
curl -X POST "http://localhost:8000/shopifyThemeFilesGet" \
|
|
151
|
+
-H "Content-Type: application/json" \
|
|
152
|
+
-d '{
|
|
153
|
+
"credsPayload": { "credsPath": "shopify.au" },
|
|
154
|
+
"themeId": "130370109512"
|
|
155
|
+
}'
|
|
156
|
+
|
|
157
|
+
curl -X POST "http://localhost:8000/shopifyThemeFilesGet" \
|
|
158
|
+
-H "Content-Type: application/json" \
|
|
159
|
+
-d '{
|
|
160
|
+
"credsPayload": { "credsPath": "shopify.au" },
|
|
161
|
+
"themeId": "130370109512",
|
|
162
|
+
"options": {
|
|
163
|
+
"filenames": "templates/*"
|
|
164
|
+
}
|
|
165
|
+
}'
|
|
166
|
+
*/
|