zapier-platform-cli 12.2.1 → 14.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/README-source.md +113 -48
- package/README.md +201 -74
- package/oclif.manifest.json +1 -1
- package/package.json +29 -26
- package/scaffold/create.template.js +2 -2
- package/scaffold/resource.template.js +2 -2
- package/scaffold/search.template.js +2 -2
- package/scaffold/trigger.template.js +2 -2
- package/src/constants.js +4 -1
- package/src/generators/templates/README.template.md +1 -1
- package/src/generators/templates/callback/README.md +1 -1
- package/src/generators/templates/files/README.md +1 -1
- package/src/oclif/ZapierBaseCommand.js +44 -2
- package/src/oclif/commands/convert.js +27 -64
- package/src/oclif/commands/link.js +1 -1
- package/src/oclif/commands/promote.js +4 -1
- package/src/oclif/commands/register.js +274 -22
- package/src/oclif/hooks/getAppRegistrationFieldChoices.js +45 -0
- package/src/utils/api.js +7 -0
- package/src/utils/build.js +4 -0
- package/src/utils/check-missing-app-info.js +26 -0
- package/src/version-store.js +1 -1
|
@@ -5,72 +5,37 @@ const { callAPI } = require('../../utils/api');
|
|
|
5
5
|
const { convertApp } = require('../../utils/convert');
|
|
6
6
|
const { isExistingEmptyDir } = require('../../utils/files');
|
|
7
7
|
const { initApp } = require('../../utils/init');
|
|
8
|
-
const { BASE_ENDPOINT } = require('../../constants');
|
|
9
8
|
|
|
10
9
|
const { flags } = require('@oclif/command');
|
|
11
10
|
|
|
12
11
|
class ConvertCommand extends BaseCommand {
|
|
13
|
-
generateCreateFunc(
|
|
12
|
+
generateCreateFunc(appId, version) {
|
|
14
13
|
return async (tempAppDir) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
`Integration ${appId} @ ${version} is already a CLI integration and can't be converted. Instead, pick a version that was created using the Visual Builder.`
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
this.stopSpinner();
|
|
32
|
-
|
|
33
|
-
return convertApp(
|
|
34
|
-
appInfo,
|
|
35
|
-
versionInfo.definition_override,
|
|
36
|
-
tempAppDir
|
|
14
|
+
// has info about the app, such as title
|
|
15
|
+
// has a CLI version of the actual app implementation
|
|
16
|
+
this.throwForInvalidVersion(version);
|
|
17
|
+
this.startSpinner('Downloading integration from Zapier');
|
|
18
|
+
try {
|
|
19
|
+
const [appInfo, versionInfo] = await Promise.all([
|
|
20
|
+
callAPI(`/apps/${appId}`, undefined, true),
|
|
21
|
+
callAPI(`/apps/${appId}/versions/${version}`, undefined, true),
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
if (!versionInfo.definition_override) {
|
|
25
|
+
this.error(
|
|
26
|
+
`Integration ${appId} @ ${version} is already a CLI integration and can't be converted. Instead, pick a version that was created using the Visual Builder.`
|
|
37
27
|
);
|
|
38
|
-
} catch (e) {
|
|
39
|
-
if (e.status === 404) {
|
|
40
|
-
this.error(
|
|
41
|
-
`Visual Builder integration ${appId} @ ${version} not found. If you want to convert a Legacy Web Builder app, don't pass a \`--version\` option. Otherwise, double check the integration id and version.`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
this.error(e);
|
|
45
28
|
}
|
|
46
|
-
|
|
47
|
-
// has info about the app, such as title
|
|
48
|
-
const legacyDumpUrl = `${BASE_ENDPOINT}/api/developer/v1/apps/${appId}/dump`;
|
|
49
|
-
// has a CLI version of the actual app implementation
|
|
50
|
-
const cliDumpUrl = `${BASE_ENDPOINT}/api/developer/v1/apps/${appId}/cli-dump`;
|
|
51
|
-
|
|
52
|
-
this.startSpinner('Downloading integration from Zapier');
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const [legacyApp, appDefinition] = await Promise.all([
|
|
56
|
-
// these have weird call signatures because we're not calling the platform api
|
|
57
|
-
callAPI(null, { url: legacyDumpUrl }, true),
|
|
58
|
-
callAPI(null, { url: cliDumpUrl }, true),
|
|
59
|
-
]);
|
|
60
|
-
// The JSON dump of the app doesn't have app ID, let's add it here
|
|
61
|
-
legacyApp.general.app_id = appId;
|
|
29
|
+
this.stopSpinner();
|
|
62
30
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`Legacy Web Builder app ${appId} not found. If you want to convert a Visual Builder integration, make sure to pass a \`--version\` option.`
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
this.error(e);
|
|
31
|
+
return convertApp(appInfo, versionInfo.definition_override, tempAppDir);
|
|
32
|
+
} catch (e) {
|
|
33
|
+
if (e.status === 404) {
|
|
34
|
+
this.error(
|
|
35
|
+
`Visual Builder integration ${appId} @ ${version} not found. Double check the integration id and version.`
|
|
36
|
+
);
|
|
73
37
|
}
|
|
38
|
+
this.error(e.json.errors[0]);
|
|
74
39
|
}
|
|
75
40
|
};
|
|
76
41
|
}
|
|
@@ -83,7 +48,6 @@ class ConvertCommand extends BaseCommand {
|
|
|
83
48
|
'You must provide an integrationId. See zapier convert --help for more info.'
|
|
84
49
|
);
|
|
85
50
|
}
|
|
86
|
-
const isVisual = Boolean(this.flags.version);
|
|
87
51
|
|
|
88
52
|
if (
|
|
89
53
|
(await isExistingEmptyDir(path)) &&
|
|
@@ -92,7 +56,7 @@ class ConvertCommand extends BaseCommand {
|
|
|
92
56
|
this.exit();
|
|
93
57
|
}
|
|
94
58
|
|
|
95
|
-
await initApp(path, this.generateCreateFunc(
|
|
59
|
+
await initApp(path, this.generateCreateFunc(appId, version));
|
|
96
60
|
}
|
|
97
61
|
}
|
|
98
62
|
|
|
@@ -100,7 +64,7 @@ ConvertCommand.args = [
|
|
|
100
64
|
{
|
|
101
65
|
name: 'integrationId',
|
|
102
66
|
required: true,
|
|
103
|
-
description: `To get the integration/app ID, go to "
|
|
67
|
+
description: `To get the integration/app ID, go to "https://developer.zapier.com", click on an integration, and copy the number directly after "/app/" in the URL.`,
|
|
104
68
|
parse: (input) => Number(input),
|
|
105
69
|
},
|
|
106
70
|
{
|
|
@@ -116,14 +80,13 @@ ConvertCommand.flags = buildFlags({
|
|
|
116
80
|
char: 'v',
|
|
117
81
|
description:
|
|
118
82
|
'Convert a specific version. Required when converting a Visual Builder integration.',
|
|
83
|
+
required: true,
|
|
119
84
|
}),
|
|
120
85
|
},
|
|
121
86
|
});
|
|
122
|
-
ConvertCommand.description = `Convert a
|
|
123
|
-
|
|
124
|
-
If you're converting a **Legacy Web Builder** app: the new integration will have a dependency named zapier-platform-legacy-scripting-runner, a shim used to simulate behaviors that are specific to Legacy Web Builder. There could be differences on how the shim simulates and how Legacy Web Builder actually behaves on some edge cases, especially you have custom scripting code.
|
|
87
|
+
ConvertCommand.description = `Convert a Visual Builder integration to a CLI integration.
|
|
125
88
|
|
|
126
|
-
|
|
89
|
+
The resulting CLI integration will be identical to its Visual Builder version and ready to push and use immediately!
|
|
127
90
|
|
|
128
91
|
If you re-run this command on an existing directory it will leave existing files alone and not clobber them.
|
|
129
92
|
|
|
@@ -7,6 +7,7 @@ const { buildFlags } = require('../buildFlags');
|
|
|
7
7
|
const { callAPI } = require('../../utils/api');
|
|
8
8
|
const { flattenCheckResult } = require('../../utils/display');
|
|
9
9
|
const { getVersionChangelog } = require('../../utils/changelog');
|
|
10
|
+
const checkMissingAppInfo = require('../../utils/check-missing-app-info');
|
|
10
11
|
|
|
11
12
|
const serializeErrors = (errors) => {
|
|
12
13
|
const opener = 'Promotion failed for the following reasons:\n\n';
|
|
@@ -15,7 +16,7 @@ const serializeErrors = (errors) => {
|
|
|
15
16
|
return opener + errors.map((e) => `* ${e}`).join('\n');
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
const issues = flattenCheckResult({ errors
|
|
19
|
+
const issues = flattenCheckResult({ errors });
|
|
19
20
|
return (
|
|
20
21
|
opener +
|
|
21
22
|
issues
|
|
@@ -28,6 +29,8 @@ class PromoteCommand extends BaseCommand {
|
|
|
28
29
|
async perform() {
|
|
29
30
|
const app = await this.getWritableApp();
|
|
30
31
|
|
|
32
|
+
checkMissingAppInfo(app);
|
|
33
|
+
|
|
31
34
|
const version = this.args.version;
|
|
32
35
|
const assumeYes = 'yes' in this.flags;
|
|
33
36
|
|
|
@@ -1,26 +1,238 @@
|
|
|
1
|
+
const colors = require('colors/safe');
|
|
2
|
+
const { flags } = require('@oclif/command');
|
|
3
|
+
|
|
1
4
|
const ZapierBaseCommand = require('../ZapierBaseCommand');
|
|
2
|
-
const { CURRENT_APP_FILE } = require('../../constants');
|
|
5
|
+
const { CURRENT_APP_FILE, MAX_DESCRIPTION_LENGTH } = require('../../constants');
|
|
3
6
|
const { buildFlags } = require('../buildFlags');
|
|
4
|
-
const {
|
|
7
|
+
const {
|
|
8
|
+
callAPI,
|
|
9
|
+
getLinkedAppConfig,
|
|
10
|
+
getWritableApp,
|
|
11
|
+
isPublished,
|
|
12
|
+
writeLinkedAppConfig,
|
|
13
|
+
} = require('../../utils/api');
|
|
5
14
|
|
|
6
15
|
class RegisterCommand extends ZapierBaseCommand {
|
|
16
|
+
/**
|
|
17
|
+
* Entry point function that runs when user runs `zapier register`
|
|
18
|
+
*/
|
|
7
19
|
async perform() {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'
|
|
23
|
-
|
|
20
|
+
// Flag validation
|
|
21
|
+
this._validateEnumFlags();
|
|
22
|
+
if (
|
|
23
|
+
'desc' in this.flags &&
|
|
24
|
+
this.flags.desc.length > MAX_DESCRIPTION_LENGTH
|
|
25
|
+
) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Please provide a description that is ${MAX_DESCRIPTION_LENGTH} characters or less.`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { appMeta, action } = await this._promptForAppMeta();
|
|
32
|
+
|
|
33
|
+
switch (action) {
|
|
34
|
+
case 'update': {
|
|
35
|
+
this.startSpinner(
|
|
36
|
+
`Updating your existing integration "${appMeta.title}"`
|
|
37
|
+
);
|
|
38
|
+
await callAPI(`/apps/${this.app.id}`, {
|
|
39
|
+
method: 'PUT',
|
|
40
|
+
body: appMeta,
|
|
41
|
+
});
|
|
42
|
+
this.stopSpinner();
|
|
43
|
+
this.log('\nIntegration successfully updated!');
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
case 'register': {
|
|
48
|
+
this.startSpinner(
|
|
49
|
+
`Registering your new integration "${appMeta.title}"`
|
|
50
|
+
);
|
|
51
|
+
const app = await callAPI('/apps?formId=create', {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
body: appMeta,
|
|
54
|
+
});
|
|
55
|
+
this.stopSpinner();
|
|
56
|
+
this.startSpinner(
|
|
57
|
+
`Linking app to current directory with \`${CURRENT_APP_FILE}\``
|
|
58
|
+
);
|
|
59
|
+
await writeLinkedAppConfig(app, process.cwd());
|
|
60
|
+
this.stopSpinner();
|
|
61
|
+
this.log(
|
|
62
|
+
'\nFinished! Now that your integration is registered with Zapier, you can `zapier push`!'
|
|
63
|
+
);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Validates values provided for enum flags against options retrieved from the BE
|
|
71
|
+
* (see getAppRegistrationFieldChoices hook for more details)
|
|
72
|
+
*/
|
|
73
|
+
_validateEnumFlags() {
|
|
74
|
+
const flagFieldMappings = {
|
|
75
|
+
audience: 'intention',
|
|
76
|
+
role: 'role',
|
|
77
|
+
category: 'app_category',
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
for (const [flag, flagValue] of Object.entries(this.flags)) {
|
|
81
|
+
// Only validate user input for enum flags (in flagFieldMappings)
|
|
82
|
+
if (!flagFieldMappings[flag]) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check user input against this.config.enumFieldChoices (retrieved in getAppRegistrationFieldChoices hook)
|
|
87
|
+
const enumFieldChoices =
|
|
88
|
+
this.config.enumFieldChoices[flagFieldMappings[flag]];
|
|
89
|
+
if (!enumFieldChoices.find((option) => option.value === flagValue)) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`${flagValue} is not a valid value for ${flag}. Must be one of the following: ${enumFieldChoices
|
|
92
|
+
.map((option) => option.value)
|
|
93
|
+
.join(', ')}`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Prompts user for values that have not been provided
|
|
101
|
+
* Flags can heavily impact the behavior of this function
|
|
102
|
+
* @returns { appMeta: {object}, action: string }
|
|
103
|
+
*/
|
|
104
|
+
async _promptForAppMeta() {
|
|
105
|
+
const appMeta = {};
|
|
106
|
+
|
|
107
|
+
const actionChoices = [
|
|
108
|
+
{ name: 'Yes, update current integration', value: 'update' },
|
|
109
|
+
{ name: 'No, register a new integration', value: 'register' },
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
let action = actionChoices[1].value; // Default action is register
|
|
113
|
+
|
|
114
|
+
const linkedAppId = (await getLinkedAppConfig(undefined, false))?.id;
|
|
115
|
+
if (linkedAppId) {
|
|
116
|
+
console.info(colors.yellow(`${CURRENT_APP_FILE} file detected.`));
|
|
117
|
+
if (this.flags.yes) {
|
|
118
|
+
console.info(
|
|
119
|
+
colors.yellow(
|
|
120
|
+
`-y/--yes flag passed, updating current integration (ID: ${linkedAppId}).`
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
action = actionChoices[0].value;
|
|
124
|
+
} else {
|
|
125
|
+
action = await this.promptWithList(
|
|
126
|
+
`Would you like to update your current integration (ID: ${linkedAppId})?`,
|
|
127
|
+
actionChoices
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (action === 'update') {
|
|
133
|
+
this.startSpinner('Retrieving details for your integration');
|
|
134
|
+
this.app = await getWritableApp();
|
|
135
|
+
this.stopSpinner();
|
|
136
|
+
|
|
137
|
+
// Block published apps from updating settings
|
|
138
|
+
if (this.app?.status && isPublished(this.app.status)) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
"You can't edit settings for this integration. To edit your integration details on Zapier's public app directory, email partners@zapier.com."
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
appMeta.title = this.args.title?.trim();
|
|
146
|
+
if (!appMeta.title) {
|
|
147
|
+
appMeta.title = await this.prompt(
|
|
148
|
+
'What is the title of your integration?',
|
|
149
|
+
{
|
|
150
|
+
required: true,
|
|
151
|
+
default: this.app?.title,
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
appMeta.description = this.flags.desc?.trim();
|
|
157
|
+
if (!appMeta.description) {
|
|
158
|
+
appMeta.description = await this.prompt(
|
|
159
|
+
`Please provide a sentence describing your app in ${MAX_DESCRIPTION_LENGTH} characters or less.`,
|
|
160
|
+
{
|
|
161
|
+
required: true,
|
|
162
|
+
charLimit: MAX_DESCRIPTION_LENGTH,
|
|
163
|
+
default: this.app?.description,
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
appMeta.homepage_url = this.flags.url;
|
|
169
|
+
if (!appMeta.homepage_url) {
|
|
170
|
+
appMeta.homepage_url = await this.prompt(
|
|
171
|
+
'What is the homepage URL of your app? (optional)',
|
|
172
|
+
{ default: this.app?.homepage_url }
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
appMeta.intention = this.flags.audience;
|
|
177
|
+
if (!appMeta.intention) {
|
|
178
|
+
appMeta.intention = await this.promptWithList(
|
|
179
|
+
'Are you building a public or private integration?',
|
|
180
|
+
this.config.enumFieldChoices.intention,
|
|
181
|
+
{ default: this.app?.intention }
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
appMeta.role = this.flags.role;
|
|
186
|
+
if (!appMeta.role) {
|
|
187
|
+
appMeta.role = await this.promptWithList(
|
|
188
|
+
"What is your relationship with the app you're integrating with Zapier?",
|
|
189
|
+
this._getRoleChoicesWithAppTitle(
|
|
190
|
+
appMeta.title,
|
|
191
|
+
this.config.enumFieldChoices.role
|
|
192
|
+
),
|
|
193
|
+
{ default: this.app?.role }
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
appMeta.app_category = this.flags.category;
|
|
198
|
+
if (!appMeta.app_category) {
|
|
199
|
+
appMeta.app_category = await this.promptWithList(
|
|
200
|
+
'How would you categorize your app?',
|
|
201
|
+
this.config.enumFieldChoices.app_category,
|
|
202
|
+
{ default: this.app?.app_category }
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (action === 'register') {
|
|
207
|
+
appMeta.subscription = this.flags.subscribe;
|
|
208
|
+
if (typeof this.flags.yes !== 'undefined') {
|
|
209
|
+
appMeta.subscription = true;
|
|
210
|
+
} else if (typeof appMeta.subscription === 'undefined') {
|
|
211
|
+
// boolean field, so using `typeof` === `undefined`
|
|
212
|
+
appMeta.subscription = await this.promptWithList(
|
|
213
|
+
'Subscribe to Updates about your Integration',
|
|
214
|
+
[
|
|
215
|
+
{ name: 'Yes', value: true },
|
|
216
|
+
{ name: 'No', value: false },
|
|
217
|
+
]
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { appMeta, action };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
*
|
|
227
|
+
* @param {string} title title of integration
|
|
228
|
+
* @param {array} choices retrieved role choices with `[app_title]` tokens
|
|
229
|
+
* @returns {array} array of choices with integration titles (instead of `[app_title]` tokens)
|
|
230
|
+
*/
|
|
231
|
+
_getRoleChoicesWithAppTitle(title, choices) {
|
|
232
|
+
return choices.map((choice) => ({
|
|
233
|
+
value: choice.value,
|
|
234
|
+
name: choice.name.replace('[app_title]', title),
|
|
235
|
+
}));
|
|
24
236
|
}
|
|
25
237
|
}
|
|
26
238
|
|
|
@@ -32,15 +244,55 @@ RegisterCommand.args = [
|
|
|
32
244
|
"Your integrations's public title. Asked interactively if not present.",
|
|
33
245
|
},
|
|
34
246
|
];
|
|
35
|
-
|
|
247
|
+
|
|
248
|
+
RegisterCommand.flags = buildFlags({
|
|
249
|
+
commandFlags: {
|
|
250
|
+
desc: flags.string({
|
|
251
|
+
char: 'D',
|
|
252
|
+
description: `A sentence describing your app in ${MAX_DESCRIPTION_LENGTH} characters or less, e.g. "Trello is a team collaboration tool to organize tasks and keep projects on track."`,
|
|
253
|
+
}),
|
|
254
|
+
url: flags.string({
|
|
255
|
+
char: 'u',
|
|
256
|
+
description: 'The homepage URL of your app, e.g., https://example.com.',
|
|
257
|
+
}),
|
|
258
|
+
audience: flags.string({
|
|
259
|
+
char: 'a',
|
|
260
|
+
description: 'Are you building a public or private integration?',
|
|
261
|
+
}),
|
|
262
|
+
role: flags.string({
|
|
263
|
+
char: 'r',
|
|
264
|
+
description:
|
|
265
|
+
"What is your relationship with the app you're integrating with Zapier?",
|
|
266
|
+
}),
|
|
267
|
+
category: flags.string({
|
|
268
|
+
char: 'c',
|
|
269
|
+
description:
|
|
270
|
+
"How would you categorize your app? Choose the most appropriate option for your app's core features.",
|
|
271
|
+
}),
|
|
272
|
+
subscribe: flags.boolean({
|
|
273
|
+
char: 's',
|
|
274
|
+
description:
|
|
275
|
+
'Get tips and recommendations about this integration along with our monthly newsletter that details the performance of your integration and the latest Zapier news.',
|
|
276
|
+
allowNo: true,
|
|
277
|
+
}),
|
|
278
|
+
yes: flags.boolean({
|
|
279
|
+
char: 'y',
|
|
280
|
+
description:
|
|
281
|
+
'Assume yes for all yes/no prompts. This flag will also update an existing integration (as opposed to registering a new one) if a .zapierapprc file is found.',
|
|
282
|
+
}),
|
|
283
|
+
},
|
|
284
|
+
});
|
|
36
285
|
RegisterCommand.examples = [
|
|
37
286
|
'zapier register',
|
|
38
287
|
'zapier register "My Cool Integration"',
|
|
288
|
+
'zapier register "My Cool Integration" --desc "My Cool Integration helps you integrate your apps with the apps that you need." --no-subscribe',
|
|
289
|
+
'zapier register "My Cool Integration" --url "https://www.zapier.com" --audience private --role employee --category marketing-automation',
|
|
290
|
+
'zapier register --subscribe',
|
|
39
291
|
];
|
|
40
|
-
RegisterCommand.description = `Register a new integration in your account.
|
|
292
|
+
RegisterCommand.description = `Register a new integration in your account, or update the existing one if a \`${CURRENT_APP_FILE}\` file is found.
|
|
41
293
|
|
|
42
|
-
|
|
294
|
+
This command creates a new integration and links it in the \`./${CURRENT_APP_FILE}\` file. If \`${CURRENT_APP_FILE}\` already exists, it will ask you if you want to update the currently-linked integration, as opposed to creating a new one.
|
|
43
295
|
|
|
44
|
-
This will change
|
|
296
|
+
After registering a new integration, you can run \`zapier push\` to build and upload your integration for use in the Zapier editor. This will change \`${CURRENT_APP_FILE}\`, which identifies this directory as holding code for a specific integration.`;
|
|
45
297
|
|
|
46
298
|
module.exports = RegisterCommand;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const { callAPI } = require('../../utils/api');
|
|
2
|
+
|
|
3
|
+
module.exports = async function (options) {
|
|
4
|
+
// We only need to run this for the register command
|
|
5
|
+
if (!options || !options.id || options.id !== 'register') {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const enumFieldChoices = {};
|
|
10
|
+
let formFields;
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
formFields = await callAPI('/apps/fields-choices');
|
|
14
|
+
} catch (e) {
|
|
15
|
+
this.error(
|
|
16
|
+
`Unable to connect to Zapier API. Please check your connection and try again. ${e}`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
for (const fieldName of ['intention', 'role', 'app_category']) {
|
|
21
|
+
enumFieldChoices[fieldName] = formFields[fieldName];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.config.enumFieldChoices = enumFieldChoices;
|
|
25
|
+
|
|
26
|
+
// This enables us to see all available options when running `zapier register --help`
|
|
27
|
+
const cmd = options.config.findCommand('register');
|
|
28
|
+
if (cmd && cmd.flags) {
|
|
29
|
+
if (cmd.flags.audience) {
|
|
30
|
+
cmd.flags.audience.options = formFields.intention.map(
|
|
31
|
+
(audienceOption) => audienceOption.value
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (cmd.flags.role) {
|
|
35
|
+
cmd.flags.role.options = formFields.role.map(
|
|
36
|
+
(roleOption) => roleOption.value
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
if (cmd.flags.category) {
|
|
40
|
+
cmd.flags.category.options = formFields.app_category.map(
|
|
41
|
+
(categoryOption) => categoryOption.value
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
package/src/utils/api.js
CHANGED
|
@@ -265,6 +265,12 @@ const getVersionInfo = () => {
|
|
|
265
265
|
});
|
|
266
266
|
};
|
|
267
267
|
|
|
268
|
+
// Intended to match logic of https://gitlab.com/zapier/team-developer-platform/dev-platform/-/blob/9fa28d8bacd04ebdad5937bd039c71aede4ede47/web/frontend/assets/app/entities/CliApp/CliApp.ts#L96
|
|
269
|
+
const isPublished = (appStatus) => {
|
|
270
|
+
const publishedStatuses = ['public', 'beta'];
|
|
271
|
+
return publishedStatuses.indexOf(appStatus) > -1;
|
|
272
|
+
};
|
|
273
|
+
|
|
268
274
|
const listApps = async () => {
|
|
269
275
|
let linkedApp;
|
|
270
276
|
try {
|
|
@@ -405,6 +411,7 @@ module.exports = {
|
|
|
405
411
|
getLinkedAppConfig,
|
|
406
412
|
getWritableApp,
|
|
407
413
|
getVersionInfo,
|
|
414
|
+
isPublished,
|
|
408
415
|
listApps,
|
|
409
416
|
listEndpoint,
|
|
410
417
|
listEndpointMulti,
|
package/src/utils/build.js
CHANGED
|
@@ -43,6 +43,8 @@ const {
|
|
|
43
43
|
validateApp,
|
|
44
44
|
} = require('./api');
|
|
45
45
|
|
|
46
|
+
const checkMissingAppInfo = require('./check-missing-app-info');
|
|
47
|
+
|
|
46
48
|
const { runCommand, isWindows } = require('./misc');
|
|
47
49
|
|
|
48
50
|
const debug = require('debug')('zapier:build');
|
|
@@ -167,6 +169,7 @@ const forceIncludeDumbPath = (appConfig, filePath) => {
|
|
|
167
169
|
// include old async deasync versions so this runs seamlessly across node versions
|
|
168
170
|
filePath.endsWith(path.join('bin', 'linux-x64-node-10', 'deasync.node')) ||
|
|
169
171
|
filePath.endsWith(path.join('bin', 'linux-x64-node-12', 'deasync.node')) ||
|
|
172
|
+
filePath.endsWith(path.join('bin', 'linux-x64-node-14', 'deasync.node')) ||
|
|
170
173
|
filePath.endsWith(
|
|
171
174
|
// Special, for zapier-platform-legacy-scripting-runner
|
|
172
175
|
path.join('bin', `linux-x64-node-${nodeMajorVersion}`, 'deasync.node')
|
|
@@ -497,6 +500,7 @@ const buildAndOrUpload = async (
|
|
|
497
500
|
let app;
|
|
498
501
|
if (upload) {
|
|
499
502
|
app = await getWritableApp();
|
|
503
|
+
checkMissingAppInfo(app);
|
|
500
504
|
}
|
|
501
505
|
|
|
502
506
|
if (build) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const { isPublished } = require('../utils/api');
|
|
2
|
+
|
|
3
|
+
module.exports = (app) => {
|
|
4
|
+
if (app.status && isPublished(app.status)) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
const requiredFields = [
|
|
8
|
+
{ apiName: 'title' },
|
|
9
|
+
{ apiName: 'description' },
|
|
10
|
+
{ apiName: 'app_category', cliName: 'category' },
|
|
11
|
+
{ apiName: 'intention', cliName: 'audience' },
|
|
12
|
+
{ apiName: 'role' },
|
|
13
|
+
];
|
|
14
|
+
const missingRequiredFields = requiredFields.filter(
|
|
15
|
+
(field) => app[field.apiName] == null
|
|
16
|
+
);
|
|
17
|
+
if (missingRequiredFields.length) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`Your integration is missing required info (${missingRequiredFields
|
|
20
|
+
.map((field) => field.cliName ?? field.apiName)
|
|
21
|
+
.join(', ')}). Please, run "zapier register" to add it.`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return false;
|
|
26
|
+
};
|
package/src/version-store.js
CHANGED
|
@@ -15,5 +15,5 @@ module.exports = [
|
|
|
15
15
|
{ nodeVersion: '12', npmVersion: '>=5.6.0' }, // 10.x
|
|
16
16
|
{ nodeVersion: '14', npmVersion: '>=5.6.0' }, // 11.x
|
|
17
17
|
{ nodeVersion: '14', npmVersion: '>=5.6.0' }, // 12.x
|
|
18
|
-
|
|
18
|
+
{ nodeVersion: '16', npmVersion: '>=5.6.0' }, // 13.x
|
|
19
19
|
];
|