zapier-platform-core 17.7.0 → 17.7.2
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/package.json +2 -2
- package/src/http-middlewares/after/prepare-response.js +32 -7
- package/src/tools/create-logger.js +3 -96
- package/src/tools/secret-scrubber.js +98 -0
- package/types/custom.d.ts +13 -2
- package/types/functions.d.ts +84 -29
- package/types/functions.test-d.ts +47 -1
- package/types/inputs.d.ts +142 -11
- package/types/inputs.plainfields.test-d.ts +484 -0
- package/types/inputs.test-d.ts +103 -39
- package/types/schemas.generated.d.ts +58 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zapier-platform-core",
|
|
3
|
-
"version": "17.7.
|
|
3
|
+
"version": "17.7.2",
|
|
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/",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"node-fetch": "2.7.0",
|
|
64
64
|
"oauth-sign": "0.9.0",
|
|
65
65
|
"semver": "7.7.1",
|
|
66
|
-
"zapier-platform-schema": "17.7.
|
|
66
|
+
"zapier-platform-schema": "17.7.2"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@types/node-fetch": "^2.6.11",
|
|
@@ -2,19 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
const querystring = require('querystring');
|
|
5
|
+
|
|
6
|
+
const { scrub, findSensitiveValues } = require('@zapier/secret-scrubber');
|
|
7
|
+
|
|
5
8
|
const { replaceHeaders } = require('./middleware-utils');
|
|
6
9
|
const { FORM_TYPE } = require('../../tools/http');
|
|
7
10
|
const errors = require('../../errors');
|
|
11
|
+
const {
|
|
12
|
+
findSensitiveValuesFromAuthData,
|
|
13
|
+
} = require('../../tools/secret-scrubber');
|
|
8
14
|
|
|
9
|
-
const
|
|
15
|
+
const buildSensitiveValues = (bundle) => {
|
|
16
|
+
const authData = bundle?.authData || {};
|
|
17
|
+
const result = [
|
|
18
|
+
...findSensitiveValuesFromAuthData(authData),
|
|
19
|
+
...findSensitiveValues(process.env),
|
|
20
|
+
];
|
|
21
|
+
return [...new Set(result)];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const _throwForStatus = (response, bundle) => {
|
|
10
25
|
// calling this always throws, regardless of the skipThrowForStatus value
|
|
11
26
|
// eslint-disable-next-line yoda
|
|
12
27
|
if (400 <= response.status && response.status < 600) {
|
|
28
|
+
// Create a cleaned version of the response to avoid sensitive data leaks
|
|
29
|
+
try {
|
|
30
|
+
// Find sensitive values from environment variables and bundle authData
|
|
31
|
+
const sensitiveValues = buildSensitiveValues(bundle);
|
|
32
|
+
if (sensitiveValues.length > 0 && response?.request?.url) {
|
|
33
|
+
response.request.url = scrub(response.request.url, sensitiveValues);
|
|
34
|
+
}
|
|
35
|
+
} catch (err) {
|
|
36
|
+
// don't fail the whole request if we can't scrub for some reason
|
|
37
|
+
}
|
|
13
38
|
throw new errors.ResponseError(response);
|
|
14
39
|
}
|
|
15
40
|
};
|
|
16
41
|
|
|
17
|
-
const prepareRawResponse = (resp, request) => {
|
|
42
|
+
const prepareRawResponse = (resp, request, bundle) => {
|
|
18
43
|
// TODO: if !2xx should we go ahead and get response.content for them?
|
|
19
44
|
// retain the response signature for raw control
|
|
20
45
|
const extendedResp = {
|
|
@@ -24,7 +49,7 @@ const prepareRawResponse = (resp, request) => {
|
|
|
24
49
|
const outResp = _.extend(resp, extendedResp, replaceHeaders(resp));
|
|
25
50
|
|
|
26
51
|
outResp.throwForStatus = () => {
|
|
27
|
-
_throwForStatus(outResp);
|
|
52
|
+
_throwForStatus(outResp, bundle);
|
|
28
53
|
};
|
|
29
54
|
|
|
30
55
|
Object.defineProperty(outResp, 'content', {
|
|
@@ -39,7 +64,7 @@ const prepareRawResponse = (resp, request) => {
|
|
|
39
64
|
return outResp;
|
|
40
65
|
};
|
|
41
66
|
|
|
42
|
-
const prepareContentResponse = async (resp, request) => {
|
|
67
|
+
const prepareContentResponse = async (resp, request, bundle) => {
|
|
43
68
|
// TODO: does it make sense to not trim the signature? more equivalence to raw...
|
|
44
69
|
const content = await resp.text();
|
|
45
70
|
|
|
@@ -67,14 +92,14 @@ const prepareContentResponse = async (resp, request) => {
|
|
|
67
92
|
} catch (_e) {}
|
|
68
93
|
|
|
69
94
|
outResp.throwForStatus = () => {
|
|
70
|
-
_throwForStatus(outResp);
|
|
95
|
+
_throwForStatus(outResp, bundle);
|
|
71
96
|
};
|
|
72
97
|
|
|
73
98
|
return outResp;
|
|
74
99
|
};
|
|
75
100
|
|
|
76
101
|
// Provide a standardized plain JS responseObj for common consumption, or raw response for streaming.
|
|
77
|
-
const prepareResponse = (resp) => {
|
|
102
|
+
const prepareResponse = (resp, z, bundle) => {
|
|
78
103
|
const request = resp.input;
|
|
79
104
|
delete resp.input;
|
|
80
105
|
|
|
@@ -82,7 +107,7 @@ const prepareResponse = (resp) => {
|
|
|
82
107
|
? prepareRawResponse
|
|
83
108
|
: prepareContentResponse;
|
|
84
109
|
|
|
85
|
-
return responseFunc(resp, request);
|
|
110
|
+
return responseFunc(resp, request, bundle);
|
|
86
111
|
};
|
|
87
112
|
|
|
88
113
|
module.exports = prepareResponse;
|
|
@@ -15,16 +15,8 @@ const {
|
|
|
15
15
|
SAFE_LOG_KEYS,
|
|
16
16
|
} = require('../constants');
|
|
17
17
|
const { unheader } = require('./http');
|
|
18
|
-
const {
|
|
19
|
-
|
|
20
|
-
findSensitiveValues,
|
|
21
|
-
recurseExtract,
|
|
22
|
-
} = require('@zapier/secret-scrubber');
|
|
23
|
-
// not really a public function, but it came from here originally
|
|
24
|
-
const {
|
|
25
|
-
isUrlWithSecrets,
|
|
26
|
-
isSensitiveKey,
|
|
27
|
-
} = require('@zapier/secret-scrubber/lib/convenience');
|
|
18
|
+
const { scrub, findSensitiveValues } = require('@zapier/secret-scrubber');
|
|
19
|
+
const { findSensitiveValuesFromAuthData } = require('./secret-scrubber');
|
|
28
20
|
|
|
29
21
|
// The payload size per request to stream logs. This should be slighly lower
|
|
30
22
|
// than the limit (16 MB) on the server side.
|
|
@@ -32,70 +24,8 @@ const LOG_STREAM_BYTES_LIMIT = 15 * 1024 * 1024;
|
|
|
32
24
|
|
|
33
25
|
const DEFAULT_LOGGER_TIMEOUT = 200;
|
|
34
26
|
|
|
35
|
-
// Will be initialized lazily
|
|
36
|
-
const SAFE_AUTH_DATA_KEYS = new Set();
|
|
37
|
-
|
|
38
27
|
const sleep = promisify(setTimeout);
|
|
39
28
|
|
|
40
|
-
const initSafeAuthDataKeys = () => {
|
|
41
|
-
// An authData key in (safePrefixes x safeSuffixes) is considered safe to log
|
|
42
|
-
// uncensored
|
|
43
|
-
const safePrefixes = [
|
|
44
|
-
'account',
|
|
45
|
-
'bot_user',
|
|
46
|
-
'cloud',
|
|
47
|
-
'cloud_site',
|
|
48
|
-
'company',
|
|
49
|
-
'domain',
|
|
50
|
-
'email',
|
|
51
|
-
'environment',
|
|
52
|
-
'location',
|
|
53
|
-
'org',
|
|
54
|
-
'organization',
|
|
55
|
-
'project',
|
|
56
|
-
'region',
|
|
57
|
-
'scope',
|
|
58
|
-
'scopes',
|
|
59
|
-
'site',
|
|
60
|
-
'subdomain',
|
|
61
|
-
'team',
|
|
62
|
-
'token_type',
|
|
63
|
-
'user',
|
|
64
|
-
'workspace',
|
|
65
|
-
];
|
|
66
|
-
const safeSuffixes = ['', '_id', '_name', 'id', 'name'];
|
|
67
|
-
for (const prefix of safePrefixes) {
|
|
68
|
-
for (const suffix of safeSuffixes) {
|
|
69
|
-
SAFE_AUTH_DATA_KEYS.add(prefix + suffix);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const isUrl = (value) => {
|
|
75
|
-
if (!value || typeof value !== 'string') {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
const commonProtocols = [
|
|
79
|
-
'https://',
|
|
80
|
-
'http://',
|
|
81
|
-
'ftp://',
|
|
82
|
-
'ftps://',
|
|
83
|
-
'file://',
|
|
84
|
-
];
|
|
85
|
-
for (const protocol of commonProtocols) {
|
|
86
|
-
if (value.startsWith(protocol)) {
|
|
87
|
-
try {
|
|
88
|
-
// eslint-disable-next-line no-new
|
|
89
|
-
new URL(value);
|
|
90
|
-
return true;
|
|
91
|
-
} catch (e) {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return false;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
29
|
const MAX_LENGTH = 3500;
|
|
100
30
|
const truncateString = (str) => simpleTruncate(str, MAX_LENGTH, ' [...]');
|
|
101
31
|
|
|
@@ -188,35 +118,12 @@ const attemptFindSecretsInStr = (s, isGettingNewSecret) => {
|
|
|
188
118
|
return findSensitiveValues(parsedRespContent);
|
|
189
119
|
};
|
|
190
120
|
|
|
191
|
-
const isSafeAuthDataKey = (key) => {
|
|
192
|
-
if (SAFE_AUTH_DATA_KEYS.size === 0) {
|
|
193
|
-
initSafeAuthDataKeys();
|
|
194
|
-
}
|
|
195
|
-
return SAFE_AUTH_DATA_KEYS.has(key.toLowerCase());
|
|
196
|
-
};
|
|
197
|
-
|
|
198
121
|
const buildSensitiveValues = (event, data) => {
|
|
199
122
|
const bundle = event.bundle || {};
|
|
200
123
|
const authData = bundle.authData || {};
|
|
201
|
-
// for the most part, we should censor all the values from authData
|
|
202
|
-
// the exception is safe urls, which should be filtered out - we want those to be logged
|
|
203
|
-
// but, we _should_ censor-no-matter-what sensitive keys, even if their value is a safe url
|
|
204
|
-
// this covers the case where someone's password is a valid url ¯\_(ツ)_/¯
|
|
205
|
-
const sensitiveAuthData = recurseExtract(authData, (key, value) => {
|
|
206
|
-
if (isSensitiveKey(key)) {
|
|
207
|
-
return true;
|
|
208
|
-
}
|
|
209
|
-
if (isSafeAuthDataKey(key)) {
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
if (isUrl(value) && !isUrlWithSecrets(value)) {
|
|
213
|
-
return false;
|
|
214
|
-
}
|
|
215
|
-
return true;
|
|
216
|
-
});
|
|
217
124
|
|
|
218
125
|
const result = [
|
|
219
|
-
...
|
|
126
|
+
...findSensitiveValuesFromAuthData(authData),
|
|
220
127
|
...findSensitiveValues(process.env),
|
|
221
128
|
...findSensitiveValues(data),
|
|
222
129
|
];
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { recurseExtract } = require('@zapier/secret-scrubber');
|
|
4
|
+
const {
|
|
5
|
+
isUrlWithSecrets,
|
|
6
|
+
isSensitiveKey,
|
|
7
|
+
} = require('@zapier/secret-scrubber/lib/convenience');
|
|
8
|
+
|
|
9
|
+
// Will be initialized lazily
|
|
10
|
+
const SAFE_AUTH_DATA_KEYS = new Set();
|
|
11
|
+
|
|
12
|
+
const initSafeAuthDataKeys = () => {
|
|
13
|
+
// An authData key in (safePrefixes x safeSuffixes) is considered safe to log
|
|
14
|
+
// uncensored
|
|
15
|
+
const safePrefixes = [
|
|
16
|
+
'account',
|
|
17
|
+
'bot_user',
|
|
18
|
+
'cloud',
|
|
19
|
+
'cloud_site',
|
|
20
|
+
'company',
|
|
21
|
+
'domain',
|
|
22
|
+
'email',
|
|
23
|
+
'environment',
|
|
24
|
+
'location',
|
|
25
|
+
'org',
|
|
26
|
+
'organization',
|
|
27
|
+
'project',
|
|
28
|
+
'region',
|
|
29
|
+
'scope',
|
|
30
|
+
'scopes',
|
|
31
|
+
'site',
|
|
32
|
+
'subdomain',
|
|
33
|
+
'team',
|
|
34
|
+
'token_type',
|
|
35
|
+
'user',
|
|
36
|
+
'workspace',
|
|
37
|
+
];
|
|
38
|
+
const safeSuffixes = ['', '_id', '_name', 'id', 'name'];
|
|
39
|
+
for (const prefix of safePrefixes) {
|
|
40
|
+
for (const suffix of safeSuffixes) {
|
|
41
|
+
SAFE_AUTH_DATA_KEYS.add(prefix + suffix);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const isSafeAuthDataKey = (key) => {
|
|
47
|
+
if (SAFE_AUTH_DATA_KEYS.size === 0) {
|
|
48
|
+
initSafeAuthDataKeys();
|
|
49
|
+
}
|
|
50
|
+
return SAFE_AUTH_DATA_KEYS.has(key.toLowerCase());
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const isUrl = (value) => {
|
|
54
|
+
if (!value || typeof value !== 'string') {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const commonProtocols = [
|
|
58
|
+
'https://',
|
|
59
|
+
'http://',
|
|
60
|
+
'ftp://',
|
|
61
|
+
'ftps://',
|
|
62
|
+
'file://',
|
|
63
|
+
];
|
|
64
|
+
for (const protocol of commonProtocols) {
|
|
65
|
+
if (value.startsWith(protocol)) {
|
|
66
|
+
try {
|
|
67
|
+
// eslint-disable-next-line no-new
|
|
68
|
+
new URL(value);
|
|
69
|
+
return true;
|
|
70
|
+
} catch (e) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const findSensitiveValuesFromAuthData = (authData) =>
|
|
79
|
+
recurseExtract(authData, (key, value) => {
|
|
80
|
+
// for the most part, we should censor all the values from authData
|
|
81
|
+
// the exception is safe urls, which should be filtered out - we want those to be logged
|
|
82
|
+
// but, we _should_ censor-no-matter-what sensitive keys, even if their value is a safe url
|
|
83
|
+
// this covers the case where someone's password is a valid url ¯\_(ツ)_/¯
|
|
84
|
+
if (isSensitiveKey(key)) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
if (isSafeAuthDataKey(key)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
if (isUrl(value) && !isUrlWithSecrets(value)) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
module.exports = {
|
|
97
|
+
findSensitiveValuesFromAuthData,
|
|
98
|
+
};
|
package/types/custom.d.ts
CHANGED
|
@@ -112,6 +112,14 @@ export interface Bundle<
|
|
|
112
112
|
};
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
+
/**
|
|
116
|
+
* A token, such as a pagination cursor, that means this run should
|
|
117
|
+
* continue pulling results. Currently only used for search
|
|
118
|
+
* pagination. Set by previous invocations by returning `{results:
|
|
119
|
+
* […], paging_token: '…'}`
|
|
120
|
+
*/
|
|
121
|
+
paging_token?: string;
|
|
122
|
+
|
|
115
123
|
/**
|
|
116
124
|
* Contains metadata about the input fields, optionally provided
|
|
117
125
|
* by the inputField.meta property. Useful for storing extra data
|
|
@@ -142,7 +150,11 @@ export interface Bundle<
|
|
|
142
150
|
|
|
143
151
|
// Error class types that match the runtime structure from src/errors.js
|
|
144
152
|
type ErrorConstructor = new (message?: string) => Error;
|
|
145
|
-
type AppErrorConstructor = new (
|
|
153
|
+
type AppErrorConstructor = new (
|
|
154
|
+
message: string,
|
|
155
|
+
code?: string,
|
|
156
|
+
status?: number,
|
|
157
|
+
) => Error;
|
|
146
158
|
type ThrottledErrorConstructor = new (message: string, delay?: number) => Error;
|
|
147
159
|
type ResponseErrorConstructor = new (response: HttpResponse) => Error;
|
|
148
160
|
|
|
@@ -163,7 +175,6 @@ interface ErrorsModule {
|
|
|
163
175
|
handleError: (...args: any[]) => never;
|
|
164
176
|
}
|
|
165
177
|
|
|
166
|
-
|
|
167
178
|
// copied http stuff from external typings
|
|
168
179
|
export interface HttpRequestOptions {
|
|
169
180
|
agent?: Agent;
|
package/types/functions.d.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import type { Bundle, ZObject } from './custom';
|
|
2
|
+
import type { InferInputData, InputField } from './inputs';
|
|
2
3
|
|
|
3
4
|
type DefaultInputData = Record<string, unknown>;
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Automatically infer the type of the bundle based on if a raw
|
|
8
|
+
* inputData object shape is given, or an array of input fields that
|
|
9
|
+
* should be automatically inferred into an inputData object.
|
|
10
|
+
*/
|
|
11
|
+
type AutoBundle<$InputDataOrFields extends DefaultInputData | InputField[]> =
|
|
12
|
+
$InputDataOrFields extends InputField[]
|
|
13
|
+
? Bundle<InferInputData<$InputDataOrFields>>
|
|
14
|
+
: $InputDataOrFields extends DefaultInputData
|
|
15
|
+
? Bundle<$InputDataOrFields>
|
|
16
|
+
: never;
|
|
17
|
+
|
|
5
18
|
/**
|
|
6
19
|
* Wraps a `perform` function that is used to poll for data from an API.
|
|
7
20
|
* By default must return an array of objects with an `id` field, but
|
|
@@ -10,9 +23,12 @@ type DefaultInputData = Record<string, unknown>;
|
|
|
10
23
|
* those keys.
|
|
11
24
|
*/
|
|
12
25
|
export type PollingTriggerPerform<
|
|
13
|
-
$
|
|
26
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
14
27
|
$Return extends {} = { id: string },
|
|
15
|
-
> = (
|
|
28
|
+
> = (
|
|
29
|
+
z: ZObject,
|
|
30
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
31
|
+
) => $Return[] | Promise<$Return[]>;
|
|
16
32
|
|
|
17
33
|
/**
|
|
18
34
|
* Process the data of a webhook sent to Zapier.
|
|
@@ -21,62 +37,83 @@ export type PollingTriggerPerform<
|
|
|
21
37
|
* in `bundle.cleanedRequest` (and `bundle.rawRequest`).
|
|
22
38
|
*/
|
|
23
39
|
export type WebhookTriggerPerform<
|
|
24
|
-
$
|
|
40
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
25
41
|
$Return extends {} = {},
|
|
26
|
-
> = (
|
|
42
|
+
> = (
|
|
43
|
+
z: ZObject,
|
|
44
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
45
|
+
) => $Return[] | Promise<$Return[]>;
|
|
27
46
|
|
|
28
47
|
/**
|
|
29
48
|
* Pull sample data from the webhook trigger. Try to make these resemble
|
|
30
49
|
* to data of the hooks as closely as possible.
|
|
31
50
|
*/
|
|
32
51
|
export type WebhookTriggerPerformList<
|
|
33
|
-
$
|
|
52
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
34
53
|
$Return extends {} = {},
|
|
35
|
-
> = (
|
|
54
|
+
> = (
|
|
55
|
+
z: ZObject,
|
|
56
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
57
|
+
) => $Return[] | Promise<$Return[]>;
|
|
36
58
|
|
|
37
59
|
/**
|
|
38
60
|
* Return must be an object of subscription data. It will be passed as
|
|
39
61
|
* `bundle.subscribeData` in the performUnsubscribe function.
|
|
40
62
|
*/
|
|
41
63
|
export type WebhookTriggerPerformSubscribe<
|
|
42
|
-
$
|
|
64
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
43
65
|
$Return extends {} = {},
|
|
44
|
-
> = (
|
|
66
|
+
> = (
|
|
67
|
+
z: ZObject,
|
|
68
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
69
|
+
) => $Return | Promise<$Return>;
|
|
45
70
|
|
|
46
71
|
/**
|
|
47
72
|
* Unsubscribe from the webhook.
|
|
48
73
|
* Data from the subscribe function is provided in `bundle.subscribeData`.
|
|
49
74
|
*/
|
|
50
75
|
export type WebhookTriggerPerformUnsubscribe<
|
|
51
|
-
$
|
|
76
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
52
77
|
$Return extends {} = {},
|
|
53
|
-
> = (
|
|
78
|
+
> = (
|
|
79
|
+
z: ZObject,
|
|
80
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
81
|
+
) => $Return | Promise<$Return>;
|
|
54
82
|
|
|
55
83
|
/**
|
|
56
84
|
* Pull data from the API service, same as a polling trigger.
|
|
57
85
|
*/
|
|
58
86
|
export type HookToPollTriggerPerformList<
|
|
59
|
-
$
|
|
87
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
60
88
|
$Return extends {} = {},
|
|
61
|
-
> = (
|
|
89
|
+
> = (
|
|
90
|
+
z: ZObject,
|
|
91
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
92
|
+
) => $Return[] | Promise<$Return[]>;
|
|
62
93
|
|
|
63
94
|
/**
|
|
64
95
|
* Return must be an object of subscription data. It will be passed as
|
|
65
96
|
* `bundle.subscribeData` in the performUnsubscribe function.
|
|
66
97
|
*/
|
|
67
98
|
export type HookToPollTriggerPerformSubscribe<
|
|
68
|
-
$
|
|
99
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
69
100
|
$Return extends {} = {},
|
|
70
|
-
> = (
|
|
101
|
+
> = (
|
|
102
|
+
z: ZObject,
|
|
103
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
104
|
+
) => $Return | Promise<$Return>;
|
|
71
105
|
|
|
72
106
|
/**
|
|
73
107
|
* Unsubscribe from the HookToPoll.
|
|
74
108
|
* Data from the subscribe function is provided in `bundle.subscribeData`.
|
|
75
109
|
*/
|
|
76
110
|
export type HookToPollTriggerPerformUnsubscribe<
|
|
77
|
-
$
|
|
111
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
78
112
|
$Return extends {} = {},
|
|
79
|
-
> = (
|
|
113
|
+
> = (
|
|
114
|
+
z: ZObject,
|
|
115
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
116
|
+
) => $Return | Promise<$Return>;
|
|
80
117
|
|
|
81
118
|
/**
|
|
82
119
|
* Create an item on a partner API.
|
|
@@ -85,9 +122,12 @@ export type HookToPollTriggerPerformUnsubscribe<
|
|
|
85
122
|
* to populate more data.
|
|
86
123
|
*/
|
|
87
124
|
export type CreatePerform<
|
|
88
|
-
$
|
|
125
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
89
126
|
$Return extends {} = {},
|
|
90
|
-
> = (
|
|
127
|
+
> = (
|
|
128
|
+
z: ZObject,
|
|
129
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
130
|
+
) => $Return | Promise<$Return>;
|
|
91
131
|
|
|
92
132
|
/**
|
|
93
133
|
* A `perform` function can setup a partner API to call back to this
|
|
@@ -104,18 +144,24 @@ export type CreatePerform<
|
|
|
104
144
|
* - bundle.outputData: The output data from the original `perform`.
|
|
105
145
|
*/
|
|
106
146
|
export type CreatePerformResume<
|
|
107
|
-
$
|
|
147
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
108
148
|
// TODO: Type cleanedRequest & outputData on Bundle interface
|
|
109
149
|
$Return extends {} = {},
|
|
110
|
-
> = (
|
|
150
|
+
> = (
|
|
151
|
+
z: ZObject,
|
|
152
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
153
|
+
) => $Return | Promise<$Return>;
|
|
111
154
|
|
|
112
155
|
/**
|
|
113
156
|
* Look up an object to populate it after creation?
|
|
114
157
|
*/
|
|
115
158
|
export type CreatePerformGet<
|
|
116
|
-
$
|
|
159
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
117
160
|
$Return extends {} = {},
|
|
118
|
-
> = (
|
|
161
|
+
> = (
|
|
162
|
+
z: ZObject,
|
|
163
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
164
|
+
) => $Return | Promise<$Return>;
|
|
119
165
|
|
|
120
166
|
/**
|
|
121
167
|
* Helper type for search results that can optionally include pagination.
|
|
@@ -125,7 +171,7 @@ export type CreatePerformGet<
|
|
|
125
171
|
*
|
|
126
172
|
* When `canPaginate` is true for the search, the object shape is required.
|
|
127
173
|
*/
|
|
128
|
-
type SearchResult<T> = T[] | { results: T[]
|
|
174
|
+
type SearchResult<T> = T[] | { results: T[]; paging_token: string };
|
|
129
175
|
|
|
130
176
|
/**
|
|
131
177
|
* Search for objects on a partner API.
|
|
@@ -136,9 +182,12 @@ type SearchResult<T> = T[] | { results: T[], paging_token: string };
|
|
|
136
182
|
* revisit this?
|
|
137
183
|
*/
|
|
138
184
|
export type SearchPerform<
|
|
139
|
-
$
|
|
185
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
140
186
|
$Return extends {} = {},
|
|
141
|
-
> = (
|
|
187
|
+
> = (
|
|
188
|
+
z: ZObject,
|
|
189
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
190
|
+
) => SearchResult<$Return> | Promise<SearchResult<$Return>>;
|
|
142
191
|
|
|
143
192
|
/**
|
|
144
193
|
* Follow up a search's perform with additional data.
|
|
@@ -148,17 +197,23 @@ export type SearchPerform<
|
|
|
148
197
|
* -> PROBABLY: Just the result of searchPerform, no inputFields.
|
|
149
198
|
*/
|
|
150
199
|
export type SearchPerformGet<
|
|
151
|
-
$
|
|
200
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
152
201
|
$Return extends {} = {},
|
|
153
|
-
> = (
|
|
202
|
+
> = (
|
|
203
|
+
z: ZObject,
|
|
204
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
205
|
+
) => $Return | Promise<$Return>;
|
|
154
206
|
|
|
155
207
|
/**
|
|
156
208
|
*
|
|
157
209
|
*/
|
|
158
210
|
export type SearchPerformResume<
|
|
159
|
-
$
|
|
211
|
+
$InputDataOrFields extends DefaultInputData | InputField[] = DefaultInputData,
|
|
160
212
|
$Return extends {} = {},
|
|
161
|
-
> = (
|
|
213
|
+
> = (
|
|
214
|
+
z: ZObject,
|
|
215
|
+
bundle: AutoBundle<$InputDataOrFields>,
|
|
216
|
+
) => $Return | Promise<$Return>;
|
|
162
217
|
|
|
163
218
|
/**
|
|
164
219
|
* Produce the URL to send the user to authorise with the OAuth2 provider.
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { expectAssignable } from 'tsd';
|
|
1
|
+
import { expectAssignable, expectType } from 'tsd';
|
|
2
2
|
import type { PollingTriggerPerform } from './functions';
|
|
3
|
+
import { Bundle, ZObject } from './custom';
|
|
4
|
+
import { InferInputData } from './inputs';
|
|
5
|
+
import { defineInputFields } from './typeHelpers';
|
|
3
6
|
|
|
4
7
|
const simplePerform = (async (z, bundle) => {
|
|
5
8
|
return [{ id: '1', name: 'test' }];
|
|
@@ -16,3 +19,46 @@ const primaryKeyOverridePerform = (async (z, bundle) => {
|
|
|
16
19
|
expectAssignable<PollingTriggerPerform<{}, { itemId: number; name: string }>>(
|
|
17
20
|
primaryKeyOverridePerform,
|
|
18
21
|
);
|
|
22
|
+
|
|
23
|
+
//
|
|
24
|
+
// Automatic inputData inference
|
|
25
|
+
//
|
|
26
|
+
// Pass shape directly
|
|
27
|
+
const simplePerformWithInputFields = (async (z, bundle) => {
|
|
28
|
+
return [{ id: '1', name: 'test' }];
|
|
29
|
+
}) satisfies PollingTriggerPerform<{ key: 'string' }>;
|
|
30
|
+
expectType<
|
|
31
|
+
(
|
|
32
|
+
z: ZObject,
|
|
33
|
+
bundle: Bundle<{ key: 'string' }>,
|
|
34
|
+
) => Promise<{ id: string; name: string }[]>
|
|
35
|
+
>(simplePerformWithInputFields);
|
|
36
|
+
|
|
37
|
+
// Use InputFields and manually infer inputData
|
|
38
|
+
const inputFields = defineInputFields([
|
|
39
|
+
{ key: 'key1', type: 'string', required: true },
|
|
40
|
+
{ key: 'key2', type: 'number', required: false },
|
|
41
|
+
]);
|
|
42
|
+
const simplePerformWithInputFieldsAndManualInference = (async (z, bundle) => {
|
|
43
|
+
return [{ id: '1', name: 'test' }];
|
|
44
|
+
}) satisfies PollingTriggerPerform<InferInputData<typeof inputFields>>;
|
|
45
|
+
expectType<
|
|
46
|
+
(
|
|
47
|
+
z: ZObject,
|
|
48
|
+
bundle: Bundle<{ key1: string; key2?: number | undefined }>,
|
|
49
|
+
) => Promise<{ id: string; name: string }[]>
|
|
50
|
+
>(simplePerformWithInputFieldsAndManualInference);
|
|
51
|
+
|
|
52
|
+
// Use inputFields and automatically infer inputData
|
|
53
|
+
const simplePerformWithInputFieldsAndAutomaticInference = (async (
|
|
54
|
+
z,
|
|
55
|
+
bundle,
|
|
56
|
+
) => {
|
|
57
|
+
return [{ id: '1', name: 'test' }];
|
|
58
|
+
}) satisfies PollingTriggerPerform<typeof inputFields>;
|
|
59
|
+
expectType<
|
|
60
|
+
(
|
|
61
|
+
z: ZObject,
|
|
62
|
+
bundle: Bundle<{ key1: string; key2?: number | undefined }>,
|
|
63
|
+
) => Promise<{ id: string; name: string }[]>
|
|
64
|
+
>(simplePerformWithInputFieldsAndAutomaticInference);
|