zapier-platform-core 16.5.0 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/include/zapierwrapper.mjs +21 -0
- package/index.js +0 -1
- package/package.json +7 -8
- package/src/app-middlewares/after/wait-for-promises.js +1 -3
- package/src/app-middlewares/before/add-app-context.js +5 -4
- package/src/execute.js +2 -3
- package/src/http-middlewares/after/log-response.js +3 -2
- package/src/http-middlewares/before/add-query-params.js +3 -1
- package/src/http-middlewares/before/prepare-request.js +43 -6
- package/src/http-middlewares/before/sanatize-headers.js +14 -0
- package/src/index.js +1 -0
- package/src/middleware.js +46 -37
- package/src/tools/create-app-request-client.js +2 -1
- package/src/tools/create-app-tester.js +1 -18
- package/src/tools/create-http-patch.js +3 -0
- package/src/tools/create-input.js +2 -0
- package/src/tools/create-lambda-handler.js +103 -122
- package/src/tools/create-rpc-client.js +1 -1
- package/src/tools/create-storekey-tool.js +3 -6
- package/src/tools/fetch.js +0 -2
- package/src/tools/http.js +9 -11
- package/src/typeHelpers.js +8 -0
- package/types/app.d.ts +24 -0
- package/types/{zapier.custom.d.ts → custom.d.ts} +41 -27
- package/types/functions.d.ts +206 -0
- package/types/index.d.ts +6 -2
- package/types/index.test-d.ts +75 -61
- package/types/inputs.d.ts +336 -0
- package/types/inputs.test-d.ts +266 -0
- package/types/{zapier.generated.d.ts → schemas.generated.d.ts} +958 -1094
- package/types/typeHelpers.d.ts +35 -0
- package/types/typeHelpers.test-d.ts +218 -0
- package/src/tools/promise.js +0 -106
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// not intended to be loaded via require() or import() - copied during build step
|
|
2
|
+
import zapier from 'zapier-platform-core';
|
|
3
|
+
|
|
4
|
+
let _appRaw;
|
|
5
|
+
try {
|
|
6
|
+
_appRaw = await import('{REPLACE_ME_PACKAGE_NAME}');
|
|
7
|
+
} catch (err) {
|
|
8
|
+
if (err.code === 'ERR_MODULE_NOT_FOUND') {
|
|
9
|
+
err.message +=
|
|
10
|
+
'\nMake sure you specify a valid entry point using `exports` in package.json.';
|
|
11
|
+
}
|
|
12
|
+
throw err;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Allows a developer to use named exports or default export in entry point
|
|
16
|
+
if (_appRaw && _appRaw.default) {
|
|
17
|
+
_appRaw = _appRaw.default;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const appRaw = _appRaw;
|
|
21
|
+
export const handler = zapier.createAppHandler(_appRaw);
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zapier-platform-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.0",
|
|
4
4
|
"description": "The core SDK for CLI apps in the Zapier Developer Platform.",
|
|
5
5
|
"repository": "zapier/zapier-platform",
|
|
6
6
|
"homepage": "https://platform.zapier.com/",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"version": "node bin/bump-dependencies.js && yarn && git add package.json yarn.lock",
|
|
20
20
|
"postversion": "git push && git push --tags",
|
|
21
21
|
"main-tests": "mocha -t 20s --recursive test --exit",
|
|
22
|
-
"type-tests": "tsd",
|
|
22
|
+
"type-tests": "tsd --files types/**/*.test-d.ts",
|
|
23
23
|
"solo-test": "test $(OPT_OUT_PATCH_TEST_ONLY=yes mocha --recursive test -g 'should be able to opt out of patch' -R json | jq '.stats.passes') -eq 1 && echo 'Ran 1 test and it passed!'",
|
|
24
24
|
"test": "yarn main-tests && yarn solo-test && yarn type-tests",
|
|
25
25
|
"test:debug": "mocha inspect -t 10s --recursive test",
|
|
@@ -43,25 +43,24 @@
|
|
|
43
43
|
"engineStrict": true,
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@zapier/secret-scrubber": "^1.1.2",
|
|
46
|
-
"bluebird": "3.7.2",
|
|
47
46
|
"content-disposition": "0.5.4",
|
|
48
|
-
"dotenv": "16.
|
|
47
|
+
"dotenv": "16.5.0",
|
|
49
48
|
"form-data": "4.0.1",
|
|
50
49
|
"lodash": "4.17.21",
|
|
51
50
|
"mime-types": "2.1.35",
|
|
52
51
|
"node-abort-controller": "3.1.1",
|
|
53
52
|
"node-fetch": "2.7.0",
|
|
54
53
|
"oauth-sign": "0.9.0",
|
|
55
|
-
"semver": "7.
|
|
56
|
-
"zapier-platform-schema": "
|
|
54
|
+
"semver": "7.7.1",
|
|
55
|
+
"zapier-platform-schema": "17.0.0"
|
|
57
56
|
},
|
|
58
57
|
"devDependencies": {
|
|
59
58
|
"@types/node-fetch": "^2.6.11",
|
|
60
59
|
"adm-zip": "0.5.16",
|
|
61
60
|
"aws-sdk": "^2.1397.0",
|
|
62
61
|
"dicer": "^0.3.1",
|
|
63
|
-
"fs-extra": "^11.
|
|
64
|
-
"mock-fs": "^5.
|
|
62
|
+
"fs-extra": "^11.3.0",
|
|
63
|
+
"mock-fs": "^5.5.0",
|
|
65
64
|
"nock": "^13.5.4",
|
|
66
65
|
"tsd": "^0.31.1"
|
|
67
66
|
},
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const ZapierPromise = require('../../tools/promise');
|
|
4
|
-
|
|
5
3
|
/*
|
|
6
4
|
After app middlewares that waits for all pending promises to resolve.
|
|
7
5
|
*/
|
|
8
6
|
const waitForPromises = (output) => {
|
|
9
|
-
return
|
|
7
|
+
return Promise.all(output.input._zapier.promises || [])
|
|
10
8
|
.catch(() => {}) // drop any errors in waiting promises
|
|
11
9
|
.then(() => output);
|
|
12
10
|
};
|
|
@@ -12,12 +12,13 @@ const logSafeBundle = (bundle) => {
|
|
|
12
12
|
*/
|
|
13
13
|
const addAppContext = (input) => {
|
|
14
14
|
const methodName = _.get(input, '_zapier.event.method');
|
|
15
|
+
input._zapier.whatHappened.push(`Executing ${methodName} with bundle`);
|
|
16
|
+
|
|
15
17
|
const bundle = _.get(input, '_zapier.event.bundle', {});
|
|
18
|
+
if (Object.keys(bundle).length > 0) {
|
|
19
|
+
input._zapier.whatHappened.push(JSON.stringify(logSafeBundle(bundle)));
|
|
20
|
+
}
|
|
16
21
|
|
|
17
|
-
input._addContext(
|
|
18
|
-
`Executing ${methodName} with bundle`,
|
|
19
|
-
JSON.stringify(logSafeBundle(bundle)),
|
|
20
|
-
);
|
|
21
22
|
return input;
|
|
22
23
|
};
|
|
23
24
|
|
package/src/execute.js
CHANGED
|
@@ -6,7 +6,6 @@ const addQueryParams = require('./http-middlewares/before/add-query-params');
|
|
|
6
6
|
const ensureArray = require('./tools/ensure-array');
|
|
7
7
|
const injectInput = require('./http-middlewares/before/inject-input');
|
|
8
8
|
const prepareRequest = require('./http-middlewares/before/prepare-request');
|
|
9
|
-
const ZapierPromise = require('./tools/promise');
|
|
10
9
|
|
|
11
10
|
const constants = require('./constants');
|
|
12
11
|
|
|
@@ -36,7 +35,7 @@ const executeHttpRequest = (input, options) => {
|
|
|
36
35
|
const executeInputOutputFields = (inputOutputFields, input) => {
|
|
37
36
|
inputOutputFields = ensureArray(inputOutputFields);
|
|
38
37
|
|
|
39
|
-
return
|
|
38
|
+
return Promise.all(
|
|
40
39
|
inputOutputFields.map((field) =>
|
|
41
40
|
_.isFunction(field) ? field(input.z, input.bundle) : field,
|
|
42
41
|
),
|
|
@@ -44,7 +43,7 @@ const executeInputOutputFields = (inputOutputFields, input) => {
|
|
|
44
43
|
};
|
|
45
44
|
|
|
46
45
|
const executeCallbackMethod = (z, bundle, method) => {
|
|
47
|
-
return new
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
48
47
|
const callback = (err, output) => {
|
|
49
48
|
if (err) {
|
|
50
49
|
reject(err);
|
|
@@ -65,8 +65,9 @@ const logResponse = (resp) => {
|
|
|
65
65
|
infoMsg += ` after ${logs.data.request_duration_ms}ms`;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
resp.
|
|
69
|
-
|
|
68
|
+
const whatHappened = resp.request.input._zapier.whatHappened;
|
|
69
|
+
whatHappened.push(infoMsg);
|
|
70
|
+
whatHappened.push(
|
|
70
71
|
`Received content "${String(logs.data.response_content).substr(0, 100)}"`,
|
|
71
72
|
);
|
|
72
73
|
|
|
@@ -14,7 +14,9 @@ const addQueryParams = (req) => {
|
|
|
14
14
|
|
|
15
15
|
normalizeEmptyParamFields(req);
|
|
16
16
|
|
|
17
|
-
let stringifiedParams = querystring.stringify(req.params
|
|
17
|
+
let stringifiedParams = querystring.stringify(req.params, '&', '=', {
|
|
18
|
+
encodeURIComponent: req.encodeURIComponent,
|
|
19
|
+
});
|
|
18
20
|
|
|
19
21
|
// it goes against spec, but for compatibility, some APIs want certain
|
|
20
22
|
// characters (mostly $) unencoded
|
|
@@ -102,6 +102,29 @@ const finalRequest = (req) => {
|
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
const throwForCurlies = (value, path) => {
|
|
106
|
+
path = path || [];
|
|
107
|
+
if (typeof value === 'string') {
|
|
108
|
+
if (/{{\s*(bundle|process)\.[^}]*}}/.test(value)) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
'z.request() no longer supports {{bundle.*}} or {{process.*}} as of v17 ' +
|
|
111
|
+
"unless it's used in a shorthand request. " +
|
|
112
|
+
'Use JavaScript template literals instead. ' +
|
|
113
|
+
`Value in violation: "${value}" in attribute "${path.join('.')}".`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
} else if (Array.isArray(value)) {
|
|
117
|
+
for (let i = 0; i < value.length; i++) {
|
|
118
|
+
const item = value[i];
|
|
119
|
+
throwForCurlies(item, [...path, String(i)]);
|
|
120
|
+
}
|
|
121
|
+
} else if (_.isPlainObject(value)) {
|
|
122
|
+
for (const [k, v] of Object.entries(value)) {
|
|
123
|
+
throwForCurlies(v, [...path, k]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
105
128
|
const prepareRequest = function (req) {
|
|
106
129
|
const input = req.input || {};
|
|
107
130
|
|
|
@@ -112,39 +135,53 @@ const prepareRequest = function (req) {
|
|
|
112
135
|
params: false,
|
|
113
136
|
body: false,
|
|
114
137
|
},
|
|
115
|
-
replace: true, // always replace curlies
|
|
116
138
|
// read default from app flags, but always defer to the request object if the value was set
|
|
117
139
|
skipThrowForStatus: _.get(
|
|
118
140
|
input,
|
|
119
141
|
['_zapier', 'app', 'flags', 'skipThrowForStatus'],
|
|
120
142
|
false,
|
|
121
143
|
),
|
|
122
|
-
_addContext: () => {},
|
|
123
144
|
});
|
|
124
145
|
|
|
125
146
|
req = sugarBody(req);
|
|
126
147
|
|
|
127
148
|
// apply app requestTemplate to request
|
|
128
149
|
if (req.merge) {
|
|
129
|
-
const requestTemplate = (input._zapier
|
|
150
|
+
const requestTemplate = (input._zapier?.app || {}).requestTemplate;
|
|
130
151
|
req = requestMerge(requestTemplate, req);
|
|
131
152
|
}
|
|
132
153
|
|
|
133
|
-
|
|
154
|
+
const replaceable = {
|
|
155
|
+
url: req.url,
|
|
156
|
+
headers: req.headers,
|
|
157
|
+
params: req.params,
|
|
158
|
+
body: req.body,
|
|
159
|
+
};
|
|
160
|
+
|
|
134
161
|
if (req.replace) {
|
|
162
|
+
// replace {{curlies}} in the request
|
|
135
163
|
const bank = createBundleBank(
|
|
136
164
|
input._zapier.app,
|
|
137
165
|
input._zapier.event,
|
|
138
166
|
req.serializeValueForCurlies,
|
|
139
167
|
);
|
|
140
|
-
req =
|
|
168
|
+
req = {
|
|
169
|
+
...req,
|
|
170
|
+
...recurseReplaceBank(replaceable, bank),
|
|
171
|
+
};
|
|
172
|
+
} else {
|
|
173
|
+
// throw if there's {{curlies}} in the request
|
|
174
|
+
throwForCurlies(replaceable);
|
|
141
175
|
}
|
|
142
176
|
|
|
143
177
|
req = coerceBody(req);
|
|
144
178
|
|
|
145
179
|
req._requestStart = new Date();
|
|
146
180
|
|
|
147
|
-
|
|
181
|
+
const whatHappened = req.input._zapier.whatHappened;
|
|
182
|
+
if (whatHappened) {
|
|
183
|
+
whatHappened.push(`Starting ${req.method} request to ${req.url}`);
|
|
184
|
+
}
|
|
148
185
|
|
|
149
186
|
return finalRequest(req);
|
|
150
187
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Middleware to trim whitespace from header keys and values
|
|
4
|
+
const sanitizeHeaders = (req) => {
|
|
5
|
+
req.headers = Object.fromEntries(
|
|
6
|
+
Object.entries(req.headers || {}).map(([key, value]) => [
|
|
7
|
+
key.trim(),
|
|
8
|
+
typeof value === 'string' ? value.trim() : value,
|
|
9
|
+
]),
|
|
10
|
+
);
|
|
11
|
+
return req;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
module.exports = sanitizeHeaders;
|
package/src/index.js
CHANGED
package/src/middleware.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
|
|
5
5
|
const envelope = require('./tools/envelope');
|
|
6
|
-
const ZapierPromise = require('./tools/promise');
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
Applies before and after middleware functions, returning
|
|
@@ -40,6 +39,17 @@ const ZapierPromise = require('./tools/promise');
|
|
|
40
39
|
output. The default is false.
|
|
41
40
|
*/
|
|
42
41
|
|
|
42
|
+
const enrichErrorMessages = (error, input) => {
|
|
43
|
+
if (error.doNotContextify) {
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
if (input._zapier && input._zapier.whatHappened) {
|
|
47
|
+
const details = input._zapier.whatHappened.map((f) => ` ${f}`).join('\n');
|
|
48
|
+
error.message = `${error.message}\nWhat happened:\n${details}\n ${error.message}`;
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
};
|
|
52
|
+
|
|
43
53
|
const applyMiddleware = (befores, afters, app, options) => {
|
|
44
54
|
options = _.defaults({}, options, {
|
|
45
55
|
skipEnvelope: false,
|
|
@@ -55,47 +65,46 @@ const applyMiddleware = (befores, afters, app, options) => {
|
|
|
55
65
|
};
|
|
56
66
|
|
|
57
67
|
return (input) => {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return result;
|
|
71
|
-
});
|
|
72
|
-
}, resolve(beforeInput));
|
|
68
|
+
const beforeMiddleware = async (beforeInput) => {
|
|
69
|
+
let newInput = beforeInput;
|
|
70
|
+
for (const func of befores) {
|
|
71
|
+
const args = [newInput].concat(options.extraArgs);
|
|
72
|
+
const maybePromise = func.apply(undefined, args);
|
|
73
|
+
// legacy scripting runner returns a Promise for beforeRequest
|
|
74
|
+
if (typeof maybePromise !== 'object') {
|
|
75
|
+
throw new Error('Middleware should return an object.');
|
|
76
|
+
}
|
|
77
|
+
newInput = await Promise.resolve(maybePromise);
|
|
78
|
+
}
|
|
79
|
+
return newInput;
|
|
73
80
|
};
|
|
74
81
|
|
|
75
|
-
const afterMiddleware = (output) => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}, resolve(output));
|
|
82
|
+
const afterMiddleware = async (output) => {
|
|
83
|
+
for (const func of afters) {
|
|
84
|
+
const args = [output].concat(options.extraArgs);
|
|
85
|
+
const maybePromise = func.apply(undefined, args);
|
|
86
|
+
if (typeof maybePromise !== 'object') {
|
|
87
|
+
throw new Error('Middleware should return an object.');
|
|
88
|
+
}
|
|
89
|
+
output = await Promise.resolve(maybePromise);
|
|
90
|
+
output = ensureEnvelope(output);
|
|
91
|
+
}
|
|
92
|
+
return output;
|
|
87
93
|
};
|
|
88
94
|
|
|
89
|
-
const promise =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
const promise = async (input) => {
|
|
96
|
+
const newInput = await beforeMiddleware(input);
|
|
97
|
+
try {
|
|
98
|
+
let output = await app(newInput);
|
|
99
|
+
output = await ensureEnvelope(output);
|
|
100
|
+
output.input = newInput;
|
|
101
|
+
return afterMiddleware(output);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return enrichErrorMessages(error, newInput);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
97
106
|
|
|
98
|
-
return promise;
|
|
107
|
+
return promise(input);
|
|
99
108
|
};
|
|
100
109
|
};
|
|
101
110
|
|
|
@@ -14,6 +14,7 @@ const createInjectInputMiddleware = require('../http-middlewares/before/inject-i
|
|
|
14
14
|
const disableSSLCertCheck = require('../http-middlewares/before/disable-ssl-cert-check');
|
|
15
15
|
const oauth1SignRequest = require('../http-middlewares/before/oauth1-sign-request');
|
|
16
16
|
const prepareRequest = require('../http-middlewares/before/prepare-request');
|
|
17
|
+
const sanitizeHeaders = require('../http-middlewares/before/sanatize-headers');
|
|
17
18
|
|
|
18
19
|
// after middles
|
|
19
20
|
const { logResponse } = require('../http-middlewares/after/log-response');
|
|
@@ -45,7 +46,7 @@ const createAppRequestClient = (input, options) => {
|
|
|
45
46
|
httpBefores.push(oauth1SignRequest);
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
+
httpBefores.push(sanitizeHeaders);
|
|
49
50
|
httpBefores.push(addQueryParams);
|
|
50
51
|
|
|
51
52
|
const verifySSL = _.get(input, '_zapier.event.verifySSL');
|
|
@@ -2,30 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const createLambdaHandler = require('./create-lambda-handler');
|
|
4
4
|
const resolveMethodPath = require('./resolve-method-path');
|
|
5
|
-
const ZapierPromise = require('./promise');
|
|
6
5
|
const { isFunction } = require('lodash');
|
|
7
6
|
const { genId } = require('./data');
|
|
8
7
|
const { shouldPaginate } = require('./should-paginate');
|
|
9
8
|
|
|
10
|
-
// Convert a app handler to promise for convenience.
|
|
11
|
-
const promisifyHandler = (handler) => {
|
|
12
|
-
return (event) => {
|
|
13
|
-
return new ZapierPromise((resolve, reject) => {
|
|
14
|
-
handler(event, {}, (err, resp) => {
|
|
15
|
-
if (err) {
|
|
16
|
-
reject(err);
|
|
17
|
-
} else {
|
|
18
|
-
resolve(resp);
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
|
|
25
9
|
// A shorthand compatible wrapper for testing.
|
|
26
10
|
const createAppTester = (appRaw, { customStoreKey } = {}) => {
|
|
27
11
|
const handler = createLambdaHandler(appRaw);
|
|
28
|
-
const createHandlerPromise = promisifyHandler(handler);
|
|
29
12
|
|
|
30
13
|
const randomSeed = genId();
|
|
31
14
|
|
|
@@ -73,7 +56,7 @@ const createAppTester = (appRaw, { customStoreKey } = {}) => {
|
|
|
73
56
|
event.detailedLogToStdout = true;
|
|
74
57
|
}
|
|
75
58
|
|
|
76
|
-
return
|
|
59
|
+
return handler(event).then((resp) => {
|
|
77
60
|
delete appRaw._testRequest; // clear adHocFunc so tests can't affect each other
|
|
78
61
|
return resp.results;
|
|
79
62
|
});
|
|
@@ -59,6 +59,9 @@ const createHttpPatch = (event) => {
|
|
|
59
59
|
|
|
60
60
|
// Only include request or response data for specific content types
|
|
61
61
|
// which we are able to read in logs and which are not typically too large
|
|
62
|
+
// Limitation: This doesn't capture the request content-type if it's set afterwards, like:
|
|
63
|
+
// const req = https.request(options, callback);
|
|
64
|
+
// req.setHeader('Content-Type', 'text/plain');
|
|
62
65
|
const requestContentType = getContentType(options.headers || {});
|
|
63
66
|
const responseContentType = getContentType(response.headers || {});
|
|
64
67
|
|