zapier-platform-core 11.0.1 → 11.3.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/LICENSE +1 -2
- package/index.d.ts +12 -6
- package/package.json +10 -6
- package/src/constants.js +0 -12
- package/src/create-command-handler.js +2 -2
- package/src/errors.js +15 -0
- package/src/tools/cleaner.js +9 -7
- package/src/tools/create-app-tester.js +21 -27
- package/src/tools/create-file-stasher.js +244 -113
- package/src/tools/create-http-patch.js +6 -8
- package/src/tools/create-lambda-handler.js +24 -28
- package/src/tools/create-legacy-scripting-runner.js +7 -4
- package/src/tools/create-logger.js +140 -93
- package/src/tools/data.js +1 -18
- package/src/tools/fetch.js +27 -3
- package/src/tools/resolve-method-path.js +10 -23
- package/src/tools/schema-tools.js +5 -1
- package/src/tools/should-paginate.js +47 -0
package/LICENSE
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
Copyright (c) Zapier, Inc.
|
|
2
2
|
|
|
3
|
-
This repository is part of Zapier Platform, of
|
|
4
|
-
https://zapier.com/platform/tos.
|
|
3
|
+
This repository is part of Zapier Platform. By downloading, installing, accessing, or using any part of the Zapier Platform, including this repository, you agree to the Zapier Platform Agreement, which can be found at: https://zapier.com/platform/tos. If you do not agree to the Zapier Platform Agreement, you may not download, install, access, or use any part of the Zapier Platform, including this repository.
|
package/index.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export interface Bundle<InputData = { [x: string]: any }> {
|
|
|
37
37
|
inputData: InputData;
|
|
38
38
|
inputDataRaw: { [x: string]: string };
|
|
39
39
|
meta: {
|
|
40
|
+
isBulkRead: boolean;
|
|
40
41
|
isFillingDynamicDropdown: boolean;
|
|
41
42
|
isLoadingSample: boolean;
|
|
42
43
|
isPopulatingDedupe: boolean;
|
|
@@ -70,6 +71,9 @@ declare class AppError extends Error {
|
|
|
70
71
|
declare class HaltedError extends Error {}
|
|
71
72
|
declare class ExpiredAuthError extends Error {}
|
|
72
73
|
declare class RefreshAuthError extends Error {}
|
|
74
|
+
declare class ThrottledError extends Error {
|
|
75
|
+
constructor(message: string, delay?: number);
|
|
76
|
+
}
|
|
73
77
|
|
|
74
78
|
// copied http stuff from external typings
|
|
75
79
|
export interface HttpRequestOptions {
|
|
@@ -123,12 +127,13 @@ type DehydrateFunc = <T>(
|
|
|
123
127
|
export interface ZObject {
|
|
124
128
|
request: {
|
|
125
129
|
// most specific overloads go first
|
|
126
|
-
(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
(
|
|
131
|
+
url: string,
|
|
132
|
+
options: HttpRequestOptions & { raw: true }
|
|
133
|
+
): Promise<RawHttpResponse>;
|
|
134
|
+
(
|
|
135
|
+
options: HttpRequestOptions & { raw: true; url: string }
|
|
136
|
+
): Promise<RawHttpResponse>;
|
|
132
137
|
|
|
133
138
|
(url: string, options?: HttpRequestOptions): Promise<HttpResponse>;
|
|
134
139
|
(options: HttpRequestOptions & { url: string }): Promise<HttpResponse>;
|
|
@@ -186,5 +191,6 @@ export interface ZObject {
|
|
|
186
191
|
HaltedError: typeof HaltedError;
|
|
187
192
|
ExpiredAuthError: typeof ExpiredAuthError;
|
|
188
193
|
RefreshAuthError: typeof RefreshAuthError;
|
|
194
|
+
ThrottledError: typeof ThrottledError;
|
|
189
195
|
};
|
|
190
196
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zapier-platform-core",
|
|
3
|
-
"version": "11.0
|
|
3
|
+
"version": "11.3.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/",
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
"preversion": "git pull && yarn test",
|
|
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 20000 --recursive test",
|
|
22
|
+
"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!'",
|
|
23
|
+
"test": "yarn main-tests && yarn solo-test",
|
|
22
24
|
"debug": "mocha -t 10000 --inspect-brk --recursive test",
|
|
23
25
|
"test:w": "mocha -t 10000 --recursive test --watch",
|
|
24
|
-
"plain-test": "mocha -t 5000 --recursive test",
|
|
25
26
|
"integration-test": "mocha -t 20000 integration-test",
|
|
26
27
|
"local-integration-test": "mocha -t 10000 integration-test --local",
|
|
27
28
|
"lambda-integration-test": "mocha -t 10000 integration-test --lambda",
|
|
@@ -34,24 +35,27 @@
|
|
|
34
35
|
"validate": "yarn test && yarn smoke-test && yarn lint"
|
|
35
36
|
},
|
|
36
37
|
"engines": {
|
|
37
|
-
"node": ">=
|
|
38
|
+
"node": ">=12",
|
|
38
39
|
"npm": ">=5.6.0"
|
|
39
40
|
},
|
|
40
41
|
"engineStrict": true,
|
|
41
42
|
"dependencies": {
|
|
43
|
+
"@zapier/secret-scrubber": "^1.0.3",
|
|
42
44
|
"bluebird": "3.7.2",
|
|
43
45
|
"content-disposition": "0.5.3",
|
|
44
46
|
"dotenv": "9.0.2",
|
|
45
47
|
"form-data": "4.0.0",
|
|
46
48
|
"lodash": "4.17.21",
|
|
47
|
-
"
|
|
49
|
+
"mime-types": "2.1.34",
|
|
50
|
+
"node-fetch": "2.6.6",
|
|
48
51
|
"oauth-sign": "0.9.0",
|
|
49
52
|
"semver": "7.3.5",
|
|
50
|
-
"zapier-platform-schema": "11.0
|
|
53
|
+
"zapier-platform-schema": "11.3.0"
|
|
51
54
|
},
|
|
52
55
|
"devDependencies": {
|
|
53
56
|
"adm-zip": "0.5.5",
|
|
54
57
|
"aws-sdk": "^2.905.0",
|
|
58
|
+
"dicer": "^0.3.0",
|
|
55
59
|
"fs-extra": "^10.0.0",
|
|
56
60
|
"mock-fs": "^4.14.0"
|
|
57
61
|
},
|
package/src/constants.js
CHANGED
|
@@ -23,17 +23,6 @@ const REQUEST_OBJECT_SHORTHAND_OPTIONS = { isShorthand: true, replace: true };
|
|
|
23
23
|
const DEFAULT_LOGGING_HTTP_ENDPOINT = 'https://httplogger.zapier.com/input';
|
|
24
24
|
const DEFAULT_LOGGING_HTTP_API_KEY = 'R24hzu86v3jntwtX2DtYECeWAB'; // It's ok, this isn't PROD
|
|
25
25
|
|
|
26
|
-
const SENSITIVE_KEYS = [
|
|
27
|
-
'api_key',
|
|
28
|
-
'apikey',
|
|
29
|
-
'auth',
|
|
30
|
-
'passwd',
|
|
31
|
-
'password',
|
|
32
|
-
'secret',
|
|
33
|
-
'signature',
|
|
34
|
-
'token',
|
|
35
|
-
];
|
|
36
|
-
|
|
37
26
|
const SAFE_LOG_KEYS = [
|
|
38
27
|
'account_id',
|
|
39
28
|
'api_title',
|
|
@@ -76,6 +65,5 @@ module.exports = {
|
|
|
76
65
|
REQUEST_OBJECT_SHORTHAND_OPTIONS,
|
|
77
66
|
RESPONSE_SIZE_LIMIT,
|
|
78
67
|
SAFE_LOG_KEYS,
|
|
79
|
-
SENSITIVE_KEYS,
|
|
80
68
|
STATUSES,
|
|
81
69
|
};
|
|
@@ -18,7 +18,7 @@ const commandHandlers = {
|
|
|
18
18
|
commands like 'execute', 'validate', 'definition', 'request'.
|
|
19
19
|
*/
|
|
20
20
|
const createCommandHandler = (compiledApp) => {
|
|
21
|
-
return (input) => {
|
|
21
|
+
return async (input) => {
|
|
22
22
|
const command = input._zapier.event.command || 'execute'; // validate || definition || request
|
|
23
23
|
const handler = commandHandlers[command];
|
|
24
24
|
if (!handler) {
|
|
@@ -26,7 +26,7 @@ const createCommandHandler = (compiledApp) => {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
|
-
return handler(compiledApp, input);
|
|
29
|
+
return await handler(compiledApp, input);
|
|
30
30
|
} catch (err) {
|
|
31
31
|
return handleError(err);
|
|
32
32
|
}
|
package/src/errors.js
CHANGED
|
@@ -32,6 +32,7 @@ class ResponseError extends Error {
|
|
|
32
32
|
status: response.status,
|
|
33
33
|
headers: {
|
|
34
34
|
'content-type': response.headers.get('content-type'),
|
|
35
|
+
'retry-after': response.headers.get('retry-after'),
|
|
35
36
|
},
|
|
36
37
|
content,
|
|
37
38
|
request: {
|
|
@@ -44,6 +45,19 @@ class ResponseError extends Error {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
class ThrottledError extends Error {
|
|
49
|
+
constructor(message, delay) {
|
|
50
|
+
super(
|
|
51
|
+
JSON.stringify({
|
|
52
|
+
message,
|
|
53
|
+
delay,
|
|
54
|
+
})
|
|
55
|
+
);
|
|
56
|
+
this.name = 'ThrottledError';
|
|
57
|
+
this.doNotContextify = true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
47
61
|
// Make some of the errors we'll use!
|
|
48
62
|
const createError = (name) => {
|
|
49
63
|
const NewError = function (message = '') {
|
|
@@ -77,6 +91,7 @@ const exceptions = _.reduce(
|
|
|
77
91
|
{
|
|
78
92
|
Error: AppError,
|
|
79
93
|
ResponseError,
|
|
94
|
+
ThrottledError,
|
|
80
95
|
}
|
|
81
96
|
);
|
|
82
97
|
|
package/src/tools/cleaner.js
CHANGED
|
@@ -45,6 +45,12 @@ const recurseCleanFuncs = (obj, path) => {
|
|
|
45
45
|
|
|
46
46
|
// Recurse a nested object replace all instances of keys->vals in the bank.
|
|
47
47
|
const recurseReplaceBank = (obj, bank = {}) => {
|
|
48
|
+
const matchesCurlies = /({{.*?}})/;
|
|
49
|
+
const matchesKeyRegexMap = Object.keys(bank).reduce((acc, key) => {
|
|
50
|
+
// Escape characters (ex. {{foo}} => \\{\\{foo\\}\\} )
|
|
51
|
+
acc[key] = new RegExp(key.replace(/[-[\]/{}()\\*+?.^$|]/g, '\\$&'), 'g');
|
|
52
|
+
return acc;
|
|
53
|
+
}, {});
|
|
48
54
|
const replacer = (out) => {
|
|
49
55
|
if (!['string', 'number'].includes(typeof out)) {
|
|
50
56
|
return out;
|
|
@@ -57,15 +63,11 @@ const recurseReplaceBank = (obj, bank = {}) => {
|
|
|
57
63
|
let maybeChangedString = originalValueStr;
|
|
58
64
|
|
|
59
65
|
Object.keys(bank).forEach((key) => {
|
|
60
|
-
|
|
61
|
-
const escapedKey = key.replace(/[-[\]/{}()\\*+?.^$|]/g, '\\$&');
|
|
62
|
-
const matchesKey = new RegExp(escapedKey, 'g');
|
|
63
|
-
|
|
66
|
+
const matchesKey = matchesKeyRegexMap[key];
|
|
64
67
|
if (!matchesKey.test(maybeChangedString)) {
|
|
65
68
|
return;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
const matchesCurlies = /({{.*?}})/;
|
|
69
71
|
const valueParts = maybeChangedString
|
|
70
72
|
.split(matchesCurlies)
|
|
71
73
|
.filter(Boolean);
|
|
@@ -156,7 +158,7 @@ const isEmptyQueryParam = (value) =>
|
|
|
156
158
|
value === '' ||
|
|
157
159
|
value === null ||
|
|
158
160
|
value === undefined ||
|
|
159
|
-
|
|
161
|
+
(typeof value === 'string' && value.search(isCurlies) >= 0);
|
|
160
162
|
|
|
161
163
|
const normalizeEmptyParamFields = normalizeEmptyRequestFields.bind(
|
|
162
164
|
null,
|
|
@@ -165,7 +167,7 @@ const normalizeEmptyParamFields = normalizeEmptyRequestFields.bind(
|
|
|
165
167
|
);
|
|
166
168
|
const normalizeEmptyBodyFields = normalizeEmptyRequestFields.bind(
|
|
167
169
|
null,
|
|
168
|
-
(v) =>
|
|
170
|
+
(v) => typeof v === 'string' && v.search(isCurlies) >= 0,
|
|
169
171
|
'body'
|
|
170
172
|
);
|
|
171
173
|
|
|
@@ -3,32 +3,9 @@
|
|
|
3
3
|
const createLambdaHandler = require('./create-lambda-handler');
|
|
4
4
|
const resolveMethodPath = require('./resolve-method-path');
|
|
5
5
|
const ZapierPromise = require('./promise');
|
|
6
|
-
const {
|
|
6
|
+
const { isFunction } = require('lodash');
|
|
7
7
|
const { genId } = require('./data');
|
|
8
|
-
|
|
9
|
-
// this is (annoyingly) mirrored in cli/api_base, so that test functions only
|
|
10
|
-
// have a storeKey when canPaginate is true. otherwise, a test would work but a
|
|
11
|
-
// poll on site would fail. this is only used in test handlers
|
|
12
|
-
|
|
13
|
-
// there are 2 places you can put a method that can interact with cursors:
|
|
14
|
-
// triggers.contact.operation.perform, if it's a poll trigger
|
|
15
|
-
// resources.contact.list.operation.perform if it's a resource
|
|
16
|
-
// schema doesn't currently allow cursor use on hook trigger `performList`, so we don't need to account for it
|
|
17
|
-
const shouldPaginate = (appRaw, method) => {
|
|
18
|
-
const methodParts = method.split('.');
|
|
19
|
-
|
|
20
|
-
if (
|
|
21
|
-
method.endsWith('perform') &&
|
|
22
|
-
((methodParts[0] === 'resources' && methodParts[2] === 'list') ||
|
|
23
|
-
methodParts[0] === 'triggers' ||
|
|
24
|
-
methodParts[0] === 'bulkReads')
|
|
25
|
-
) {
|
|
26
|
-
methodParts.pop();
|
|
27
|
-
return get(appRaw, [...methodParts, 'canPaginate']);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return false;
|
|
31
|
-
};
|
|
8
|
+
const { shouldPaginate } = require('./should-paginate');
|
|
32
9
|
|
|
33
10
|
// Convert a app handler to promise for convenience.
|
|
34
11
|
const promisifyHandler = (handler) => {
|
|
@@ -55,7 +32,21 @@ const createAppTester = (appRaw, { customStoreKey } = {}) => {
|
|
|
55
32
|
return (methodOrFunc, bundle) => {
|
|
56
33
|
bundle = bundle || {};
|
|
57
34
|
|
|
58
|
-
|
|
35
|
+
let method = resolveMethodPath(appRaw, methodOrFunc, false);
|
|
36
|
+
if (!method) {
|
|
37
|
+
if (isFunction(methodOrFunc)) {
|
|
38
|
+
// definitely have a function but didn't find it on the app; it's an adhoc
|
|
39
|
+
appRaw._testRequest = (z, bundle) => methodOrFunc(z, bundle);
|
|
40
|
+
method = resolveMethodPath(appRaw, appRaw._testRequest);
|
|
41
|
+
} else {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Unable to find the following on your App instance: ${JSON.stringify(
|
|
44
|
+
methodOrFunc
|
|
45
|
+
)}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
59
50
|
const storeKey = shouldPaginate(appRaw, method)
|
|
60
51
|
? customStoreKey
|
|
61
52
|
? `testKey-${customStoreKey}`
|
|
@@ -77,7 +68,10 @@ const createAppTester = (appRaw, { customStoreKey } = {}) => {
|
|
|
77
68
|
event.detailedLogToStdout = true;
|
|
78
69
|
}
|
|
79
70
|
|
|
80
|
-
return createHandlerPromise(event).then((resp) =>
|
|
71
|
+
return createHandlerPromise(event).then((resp) => {
|
|
72
|
+
delete appRaw._testRequest; // clear adHocFunc so tests can't affect each other
|
|
73
|
+
return resp.results;
|
|
74
|
+
});
|
|
81
75
|
};
|
|
82
76
|
};
|
|
83
77
|
|