zapier-platform-core 17.0.1 → 17.0.3
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/index.mjs +21 -0
- package/package.json +13 -3
- package/src/constants.js +7 -2
- package/src/execute-request.js +0 -1
- package/src/execute.js +9 -13
- package/src/http-middlewares/before/inject-input.js +3 -3
- package/src/http-middlewares/before/prepare-request.js +40 -25
- package/src/tools/cleaner.js +80 -50
- package/src/tools/create-http-patch.js +1 -1
- package/src/tools/request-merge.js +7 -0
- package/types/schemas.generated.d.ts +1 -1
package/index.mjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import zapier from './src/index.js';
|
|
2
|
+
import packageJson from './package.json' with { type: 'json' };
|
|
3
|
+
import _tools from './src/tools/exported.js';
|
|
4
|
+
zapier.version = packageJson.version;
|
|
5
|
+
zapier.tools = _tools;
|
|
6
|
+
// Allows `import { ... } from 'zapier-platform-core'`
|
|
7
|
+
export const {
|
|
8
|
+
createAppHandler,
|
|
9
|
+
createAppTester,
|
|
10
|
+
defineApp,
|
|
11
|
+
defineCreate,
|
|
12
|
+
defineInputField,
|
|
13
|
+
defineInputFields,
|
|
14
|
+
defineSearch,
|
|
15
|
+
defineTrigger,
|
|
16
|
+
integrationTestHandler,
|
|
17
|
+
tools,
|
|
18
|
+
version,
|
|
19
|
+
} = zapier;
|
|
20
|
+
// Allows `import zapier from 'zapier-platform-core'`
|
|
21
|
+
export default zapier;
|
package/package.json
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zapier-platform-core",
|
|
3
|
-
"version": "17.0.
|
|
3
|
+
"version": "17.0.3",
|
|
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/",
|
|
7
7
|
"author": "Zapier Engineering <contact@zapier.com>",
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE",
|
|
9
|
-
"main": "index.js",
|
|
10
9
|
"types": "types/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"require": "./index.js",
|
|
13
|
+
"import": "./index.mjs",
|
|
14
|
+
"types": "./types/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./src/*": {
|
|
17
|
+
"require": "./src/*.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
11
20
|
"files": [
|
|
12
21
|
"/include/",
|
|
13
22
|
"/index.js",
|
|
23
|
+
"/index.mjs",
|
|
14
24
|
"/src/",
|
|
15
25
|
"/types/"
|
|
16
26
|
],
|
|
@@ -52,7 +62,7 @@
|
|
|
52
62
|
"node-fetch": "2.7.0",
|
|
53
63
|
"oauth-sign": "0.9.0",
|
|
54
64
|
"semver": "7.7.1",
|
|
55
|
-
"zapier-platform-schema": "17.0.
|
|
65
|
+
"zapier-platform-schema": "17.0.3"
|
|
56
66
|
},
|
|
57
67
|
"devDependencies": {
|
|
58
68
|
"@types/node-fetch": "^2.6.11",
|
package/src/constants.js
CHANGED
|
@@ -23,7 +23,11 @@ const RENDER_ONLY_METHODS = [
|
|
|
23
23
|
'authentication.oauth1Config.authorizeUrl',
|
|
24
24
|
];
|
|
25
25
|
|
|
26
|
-
const
|
|
26
|
+
const REPLACE_CURLIES = Symbol('replaceCurlies');
|
|
27
|
+
|
|
28
|
+
const REQUEST_OBJECT_SHORTHAND_OPTIONS = {
|
|
29
|
+
[REPLACE_CURLIES]: true,
|
|
30
|
+
};
|
|
27
31
|
|
|
28
32
|
const DEFAULT_LOGGING_HTTP_ENDPOINT = 'https://httplogger.zapier.com/input';
|
|
29
33
|
const DEFAULT_LOGGING_HTTP_API_KEY = 'R24hzu86v3jntwtX2DtYECeWAB'; // It's ok, this isn't PROD
|
|
@@ -65,13 +69,14 @@ module.exports = {
|
|
|
65
69
|
IS_TESTING,
|
|
66
70
|
KILL_MAX_LIMIT,
|
|
67
71
|
KILL_MIN_LIMIT,
|
|
72
|
+
NON_STREAM_UPLOAD_MAX_SIZE,
|
|
68
73
|
PACKAGE_NAME,
|
|
69
74
|
PACKAGE_VERSION,
|
|
70
75
|
RENDER_ONLY_METHODS,
|
|
76
|
+
REPLACE_CURLIES,
|
|
71
77
|
REQUEST_OBJECT_SHORTHAND_OPTIONS,
|
|
72
78
|
RESPONSE_SIZE_LIMIT,
|
|
73
79
|
SAFE_LOG_KEYS,
|
|
74
80
|
STATUSES,
|
|
75
81
|
UPLOAD_MAX_SIZE,
|
|
76
|
-
NON_STREAM_UPLOAD_MAX_SIZE,
|
|
77
82
|
};
|
package/src/execute-request.js
CHANGED
package/src/execute.js
CHANGED
|
@@ -10,18 +10,15 @@ const prepareRequest = require('./http-middlewares/before/prepare-request');
|
|
|
10
10
|
const constants = require('./constants');
|
|
11
11
|
|
|
12
12
|
const executeHttpRequest = (input, options) => {
|
|
13
|
-
options =
|
|
14
|
-
{},
|
|
13
|
+
options = {
|
|
15
14
|
// shorthand requests should always throw _unless_ the object specifically opts out
|
|
16
15
|
// this covers godzilla devs who use shorthand requests (most of them) that rely on the throwing behavior
|
|
17
16
|
// when we set the app-wide skip for everyone, we don't want their behavior to change
|
|
18
17
|
// so, this line takes precedence over the global setting, but not the local one (`options`)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
constants.REQUEST_OBJECT_SHORTHAND_OPTIONS,
|
|
24
|
-
);
|
|
18
|
+
skipThrowForStatus: false,
|
|
19
|
+
...options,
|
|
20
|
+
...constants.REQUEST_OBJECT_SHORTHAND_OPTIONS,
|
|
21
|
+
};
|
|
25
22
|
return input.z.request(options).then((response) => {
|
|
26
23
|
if (response.data === undefined) {
|
|
27
24
|
throw new Error(
|
|
@@ -83,11 +80,10 @@ const execute = (app, input) => {
|
|
|
83
80
|
} else if (_.isObject(method) && method.url) {
|
|
84
81
|
const options = method;
|
|
85
82
|
if (isRenderOnly(methodName)) {
|
|
86
|
-
const requestWithInput =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
);
|
|
83
|
+
const requestWithInput = {
|
|
84
|
+
...injectInput(input)(options),
|
|
85
|
+
...constants.REQUEST_OBJECT_SHORTHAND_OPTIONS,
|
|
86
|
+
};
|
|
91
87
|
const preparedRequest = addQueryParams(prepareRequest(requestWithInput));
|
|
92
88
|
return preparedRequest.url;
|
|
93
89
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
|
|
5
3
|
/*
|
|
6
4
|
Creates HTTP before middleware that adds some app context
|
|
7
5
|
to HTTP request options, including the app and event.
|
|
@@ -9,7 +7,9 @@ const _ = require('lodash');
|
|
|
9
7
|
Useful for HTTP middlewares that need stuff from the app or event.
|
|
10
8
|
*/
|
|
11
9
|
const injectInput = (input) => {
|
|
12
|
-
return (req) =>
|
|
10
|
+
return (req) => {
|
|
11
|
+
return { ...req, input };
|
|
12
|
+
};
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
module.exports = injectInput;
|
|
@@ -16,6 +16,8 @@ const {
|
|
|
16
16
|
JSON_TYPE_UTF8,
|
|
17
17
|
} = require('../../tools/http');
|
|
18
18
|
|
|
19
|
+
const { REPLACE_CURLIES } = require('../../constants');
|
|
20
|
+
|
|
19
21
|
const isStream = (obj) => obj instanceof stream.Stream;
|
|
20
22
|
const isPromise = (obj) => obj && typeof obj.then === 'function';
|
|
21
23
|
|
|
@@ -50,6 +52,7 @@ const coerceBody = (req) => {
|
|
|
50
52
|
|
|
51
53
|
// auto coerce form if header says so
|
|
52
54
|
if (contentType === FORM_TYPE && req.body && !_.isString(req.body)) {
|
|
55
|
+
normalizeEmptyBodyFields(req);
|
|
53
56
|
req.body = querystring.stringify(req.body).replace(/%20/g, '+');
|
|
54
57
|
}
|
|
55
58
|
|
|
@@ -108,8 +111,9 @@ const throwForCurlies = (value, path) => {
|
|
|
108
111
|
if (/{{\s*(bundle|process)\.[^}]*}}/.test(value)) {
|
|
109
112
|
throw new Error(
|
|
110
113
|
'z.request() no longer supports {{bundle.*}} or {{process.*}} as of v17 ' +
|
|
111
|
-
"unless it's used in a shorthand request. " +
|
|
112
|
-
'
|
|
114
|
+
"unless it's used in a shorthand request defined by the integration. " +
|
|
115
|
+
'Zapier Customers: Remove "{{curly braces}}" from your request. ' +
|
|
116
|
+
'Developers: Use JavaScript template literals instead. ' +
|
|
113
117
|
`Value in violation: "${value}" in attribute "${path.join('.')}".`,
|
|
114
118
|
);
|
|
115
119
|
}
|
|
@@ -145,33 +149,44 @@ const prepareRequest = function (req) {
|
|
|
145
149
|
|
|
146
150
|
req = sugarBody(req);
|
|
147
151
|
|
|
148
|
-
|
|
149
|
-
if (req.merge) {
|
|
150
|
-
const requestTemplate = (input._zapier?.app || {}).requestTemplate;
|
|
151
|
-
req = requestMerge(requestTemplate, req);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const replaceable = {
|
|
155
|
-
url: req.url,
|
|
156
|
-
headers: req.headers,
|
|
157
|
-
params: req.params,
|
|
158
|
-
body: req.body,
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
if (req.replace) {
|
|
162
|
-
// replace {{curlies}} in the request
|
|
152
|
+
if (req[REPLACE_CURLIES] || req.merge) {
|
|
163
153
|
const bank = createBundleBank(
|
|
164
|
-
input
|
|
165
|
-
input._zapier.event,
|
|
154
|
+
input?._zapier?.event || {},
|
|
166
155
|
req.serializeValueForCurlies,
|
|
167
156
|
);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
157
|
+
|
|
158
|
+
const requestReplaceable = {
|
|
159
|
+
url: req.url,
|
|
160
|
+
headers: req.headers,
|
|
161
|
+
params: req.params,
|
|
162
|
+
body: req.body,
|
|
171
163
|
};
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
164
|
+
if (req[REPLACE_CURLIES]) {
|
|
165
|
+
// replace {{curlies}} in the request
|
|
166
|
+
req = {
|
|
167
|
+
...req,
|
|
168
|
+
...recurseReplaceBank(requestReplaceable, bank),
|
|
169
|
+
};
|
|
170
|
+
} else {
|
|
171
|
+
// throw if there's {{curlies}} in the request
|
|
172
|
+
throwForCurlies(requestReplaceable);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (req.merge) {
|
|
176
|
+
// Always replace {{curlies}} in reqeustTemplate regardless of
|
|
177
|
+
// req[REPLACE_CURLIES]
|
|
178
|
+
const requestTemplate = input._zapier?.app?.requestTemplate || {};
|
|
179
|
+
const templateReplaceable = {
|
|
180
|
+
url: requestTemplate.url,
|
|
181
|
+
headers: requestTemplate.headers,
|
|
182
|
+
params: requestTemplate.params,
|
|
183
|
+
body: requestTemplate.body,
|
|
184
|
+
};
|
|
185
|
+
const renderedTemplate = recurseReplaceBank(templateReplaceable, bank);
|
|
186
|
+
|
|
187
|
+
// Apply app.requestTemplate to request
|
|
188
|
+
req = requestMerge(renderedTemplate, req);
|
|
189
|
+
}
|
|
175
190
|
}
|
|
176
191
|
|
|
177
192
|
req = coerceBody(req);
|
package/src/tools/cleaner.js
CHANGED
|
@@ -43,66 +43,96 @@ const recurseCleanFuncs = (obj, path) => {
|
|
|
43
43
|
return obj;
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
+
const findNextCurlies = (str) => {
|
|
47
|
+
const start = str.indexOf('{{');
|
|
48
|
+
if (start < 0) {
|
|
49
|
+
return {
|
|
50
|
+
start: -1,
|
|
51
|
+
end: -1,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const end = str.indexOf('}}', start + 2);
|
|
56
|
+
if (end < 0) {
|
|
57
|
+
return {
|
|
58
|
+
start: -1,
|
|
59
|
+
end: -1,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
start,
|
|
65
|
+
end: end + 2,
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
46
69
|
// Recurse a nested object replace all instances of keys->vals in the bank.
|
|
47
70
|
const recurseReplaceBank = (obj, bank = {}) => {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
acc[key] = new RegExp(key.replace(/[-[\]/{}()\\*+?.^$|]/g, '\\$&'), 'g');
|
|
52
|
-
return acc;
|
|
53
|
-
}, {});
|
|
54
|
-
const replacer = (out) => {
|
|
55
|
-
if (!['string', 'number'].includes(typeof out)) {
|
|
56
|
-
return out;
|
|
71
|
+
const replacer = (input) => {
|
|
72
|
+
if (typeof input !== 'string') {
|
|
73
|
+
return input;
|
|
57
74
|
}
|
|
58
75
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
const outputItems = [];
|
|
77
|
+
let inputString = input;
|
|
78
|
+
|
|
79
|
+
// 1000 iterations is just a static upper bound to make infinite loops
|
|
80
|
+
// impossible. Who would have 1000 {{curlies}} in a string... right?
|
|
81
|
+
const MAX_ITERATIONS = 1000;
|
|
82
|
+
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
|
83
|
+
const { start, end } = findNextCurlies(inputString);
|
|
84
|
+
if (start < 0) {
|
|
85
|
+
outputItems.push(inputString);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const head = inputString.slice(0, start);
|
|
90
|
+
const key = inputString.slice(start, end);
|
|
91
|
+
const tail = inputString.slice(end);
|
|
92
|
+
|
|
93
|
+
if (head) {
|
|
94
|
+
outputItems.push(head);
|
|
74
95
|
}
|
|
75
96
|
|
|
76
|
-
const valueParts = maybeChangedString
|
|
77
|
-
.split(matchesCurlies)
|
|
78
|
-
.filter(Boolean);
|
|
79
97
|
const replacementValue = bank[key];
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
98
|
+
if (replacementValue == null) {
|
|
99
|
+
// No match in the bank
|
|
100
|
+
outputItems.push(key);
|
|
101
|
+
} else {
|
|
102
|
+
const isPartOfString = head || tail;
|
|
103
|
+
if (
|
|
104
|
+
isPartOfString &&
|
|
105
|
+
(Array.isArray(replacementValue) || _.isPlainObject(replacementValue))
|
|
106
|
+
) {
|
|
107
|
+
const bareKey = key.slice(2, -2); // '{{key}}' -> 'key'
|
|
108
|
+
throw new TypeError(
|
|
109
|
+
'Cannot reliably interpolate objects or arrays into a string. ' +
|
|
110
|
+
`Variable \`${bareKey}\` is an ${getObjectType(
|
|
111
|
+
replacementValue,
|
|
112
|
+
)}:\n"${replacementValue}"`,
|
|
113
|
+
);
|
|
114
|
+
} else {
|
|
115
|
+
outputItems.push(replacementValue);
|
|
116
|
+
}
|
|
94
117
|
}
|
|
95
118
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
119
|
+
inputString = tail;
|
|
120
|
+
if (!inputString) {
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (i === MAX_ITERATIONS - 1) {
|
|
125
|
+
// The input string does have more than 1000 {{curlies}}, just return
|
|
126
|
+
// the rest of the string without replacing.
|
|
127
|
+
outputItems.push(inputString);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
100
130
|
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
131
|
+
if (outputItems.length === 1 && typeof outputItems[0] !== 'string') {
|
|
132
|
+
return outputItems[0];
|
|
133
|
+
} else {
|
|
134
|
+
return outputItems.join('');
|
|
104
135
|
}
|
|
105
|
-
return maybeChangedString;
|
|
106
136
|
};
|
|
107
137
|
return recurseReplace(obj, replacer);
|
|
108
138
|
};
|
|
@@ -113,7 +143,7 @@ const finalizeBundle = pipe(
|
|
|
113
143
|
);
|
|
114
144
|
|
|
115
145
|
// Takes a raw app and bundle and composes a bank of {{key}}->val
|
|
116
|
-
const createBundleBank = (
|
|
146
|
+
const createBundleBank = (event = {}, serializeFunc = (x) => x) => {
|
|
117
147
|
const bank = {
|
|
118
148
|
bundle: finalizeBundle(event.bundle),
|
|
119
149
|
process: {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
|
|
5
5
|
const requestClean = require('./request-clean');
|
|
6
|
+
const { REPLACE_CURLIES } = require('../constants');
|
|
6
7
|
|
|
7
8
|
// Do a merge with case-insensitive keys in the .header, and drop empty .header keys
|
|
8
9
|
const caseInsensitiveMerge = (requestOne, requestTwo, requestThree) => {
|
|
@@ -14,6 +15,12 @@ const caseInsensitiveMerge = (requestOne, requestTwo, requestThree) => {
|
|
|
14
15
|
// This is a very quick & efficient merge for all of request's properties
|
|
15
16
|
const mergedRequest = _.merge(requestOne, requestTwo, requestThree);
|
|
16
17
|
|
|
18
|
+
// _.merge() ignores symbols. REPLACE_CURLIES is a symbol, so we need to add
|
|
19
|
+
// it back
|
|
20
|
+
if (requestThree[REPLACE_CURLIES]) {
|
|
21
|
+
mergedRequest[REPLACE_CURLIES] = requestThree[REPLACE_CURLIES];
|
|
22
|
+
}
|
|
23
|
+
|
|
17
24
|
// Now to cleanup headers, we start on the last request (the one with priority) and work backwards to add the keys that don't already exist
|
|
18
25
|
// NOTE: This is done "manually" instead of a _.merge or Object.assign() because we need case-insensitivity
|
|
19
26
|
const mergedRequestHeaders = requestThree.headers || {};
|