zapier-platform-cli 13.0.0 → 14.0.1

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 CHANGED
@@ -443,6 +443,35 @@ Also, `authentication.oauth2Config.getAccessToken` has access to the additional
443
443
 
444
444
  If you define `fields` to collect additional details from the user, please note that `client_id` and `client_secret` are reserved keys and cannot be used as keys for input form fields.
445
445
 
446
+
447
+ ### OAuth2 with PKCE
448
+
449
+ *Added in v14.0.0.*
450
+
451
+ Zapier's OAuth2 implementation also supports [PKCE](https://oauth.net/2/pkce/). This implementation is an extension of the OAuth2 `authorization_code` flow described above.
452
+
453
+ To use PKCE in your OAuth2 flow, you'll need to set the following variables:
454
+ 1. `enablePkce: true`
455
+ 2. `getAccessToken.body` to include `code_verifier: "{{bundle.inputData.code_verifier}}"`
456
+
457
+ The OAuth2 PKCE flow uses the same flow as OAuth2 but adds a few extra parameters:
458
+
459
+ 1. Zapier computes a `code_verifier` and `code_challenge` internally and stores the `code_verifier` in the Zapier bundle.
460
+ 2. Zapier sends the user to the authorization URL defined by your app, passing along the computed `code_challenge`.
461
+ 3. Once authorized, your website sends the user to the `redirect_uri` Zapier provided.
462
+ 4. Zapier makes a call to your API to exchange the `code` and the computed `code_verifier` for an `access_token`.
463
+ 5. Zapier stores the `access_token` and uses it to make calls on behalf of the user.
464
+
465
+ Your auth definition would look something like this:
466
+
467
+ ```js
468
+ [insert-file:./snippets/oauth2-pkce.js]
469
+ ```
470
+
471
+ The computed `code_verifier` uses this standard: [RFC 7636 Code Verifier](https://www.rfc-editor.org/rfc/rfc7636#section-4.1)
472
+
473
+ The computed `code_challenge` uses this standard: [RFC 7636 Code Challenge](https://www.rfc-editor.org/rfc/rfc7636#section-4.2)
474
+
446
475
  ### Connection Label
447
476
 
448
477
  When a user connects to your app via Zapier and a connection is created to hold the related data in `bundle.authData`, the connection is automatically labeled with the app name. You also have the option of setting a connection label (`connectionLabel`), which can be extremely helpful to identify information like which user is connected or what instance of your app they are connected to. That way, users don't get confused if they have multiple connections to your app.
@@ -529,16 +558,52 @@ You can find more details on the definition for each by looking at the [Trigger
529
558
  > To add a trigger, search, or create to an existing integration, run `zapier scaffold [trigger|search|create] [noun]` to create the necessary files to your project. For example, `zapier scaffold trigger post` will create a new trigger called "New Post".
530
559
  ### Return Types
531
560
 
532
- Each of the 3 types of function expects a certain type of object. As of core v1.0.11, there are automated checks to let you know when you're trying to pass the wrong type back. For reference, each expects:
561
+ Each of the 3 types of function should return a certain data type for use by the platform. There are automated checks to let you know when you're trying to pass the wrong type back. For reference, each expects:
533
562
 
534
563
  | Method | Return Type | Notes |
535
564
  |---------|-------------|----------------------------------------------------------------------------------------------------------------------|
536
- | Trigger | Array | 0 or more objects; passed to the [deduper](https://zapier.com/developer/documentation/v2/deduplication/) if polling |
565
+ | Trigger | Array | 0 or more objects; passed to the [deduper](https://platform.zapier.com/docs/dedupe/) if polling |
537
566
  | Search | Array | 0 or more objects. Only the first object will be returned, so if len > 1, put the best match first |
538
- | Action | Object | Return values are evaluated by [`isPlainObject`](https://lodash.com/docs#isPlainObject) |
567
+ | Create | Object | Return values are evaluated by [`isPlainObject`](https://lodash.com/docs#isPlainObject) |
539
568
 
540
569
  When a trigger function returns an empty array, the Zap will not trigger. For REST Hook triggers, this can be used to filter data if the available subscription options are not specific enough for the Zap's needs.
541
570
 
571
+ #### Returning Line Items (Array of Objects)
572
+
573
+ In some cases, you may want to include multiple items in the data you return for Searches or Creates. To do that, return the set of items as an array of objects under a descriptive key. This may be as part of another object (like items in an invoice) or as multiple top-level items.
574
+
575
+ For example, a Create Order action returning an order with multiple items might look like this:
576
+
577
+ ```
578
+ order = {
579
+ name: 'Zap Zaplar',
580
+ total_cost: 25.96,
581
+ items: [
582
+ { name: 'Zapier T-Shirt', unit_price: 11.99, quantity: 3, line_amount: 35.97, category: 'shirts' },
583
+ { name: 'Orange Widget', unit_price: 7.99, quantity: 10, line_amount: 79.90, category: 'widgets' },
584
+ { name:'Stuff', unit_price: 2.99, quantity: 7, line_amount: 20.93, category: 'stuff' },
585
+ { name: 'Allbird Shoes', unit_price: 2.99, quantity: 7, line_amount: 20.93, category: 'shoes' },
586
+ ],
587
+ zip: 01002
588
+ }
589
+ ```
590
+
591
+ While a Find Users search could return multiple items under an object key within an array, like this:
592
+
593
+ ```
594
+ result = [{
595
+ users: [
596
+ { name: 'Zap Zaplar', age: 12, city: 'Columbia', region: 'Missouri' },
597
+ { name: 'Orange Crush', age: 28, city: 'West Ocean City', region: 'Maryland' },
598
+ { name: 'Lego Brick', age: 91, city: 'Billund', region: 'Denmark' },
599
+ ],
600
+ }];
601
+ ```
602
+
603
+ A standard search would return just the inner array of users, and only the first user would be provided as a final result. Returning line items instead means that the "first result" return is the object containing all the user details within it.
604
+
605
+ Using the standard approach is recommended, because not all Zapier integrations support line items directly, so users may need to take additional actions to reformat this data for use in their Zaps. More detail on that at [Use line items in Zaps](https://zapier.com/help/create/basics/use-line-items-in-zaps). However, there are use cases where returning multiple results is helpful enough to outweigh that additional effort.
606
+
542
607
  ### Fallback Sample
543
608
  In cases where Zapier needs to show an example record to the user, but we are unable to get a live example from the API, Zapier will fallback to this hard-coded sample. This should reflect the data structure of the Trigger's perform method, and have dummy values that we can show to any user.
544
609
 
@@ -766,7 +831,7 @@ The available errors are:
766
831
 
767
832
  * `Error` (_added in v9.3.0_) - Stops the current operation, allowing for (auto) replay. Read more on [General Errors](#general-errors)
768
833
  * `HaltedError` - Stops current operation, but will never turn off Zap. Read more on [Halting Execution](#halting-execution)
769
- * `ExpiredAuthError` - Turns off Zap and emails user to manually reconnect. Read more on [Stale Authentication Credentials](#stale-authentication-credentials)
834
+ * `ExpiredAuthError` - Stops the current operation and emails user to manually reconnect. Read more on [Stale Authentication Credentials](#stale-authentication-credentials)
770
835
  * `RefreshAuthError` - (OAuth2 or Session Auth) Tells Zapier to refresh credentials and retry operation. Read more on [Stale Authentication Credentials](#stale-authentication-credentials)
771
836
  * `ThrottledError` (_new in v11.2.0_) - Tells Zapier to retry the current operation after a delay specified in seconds. Read more on [Handling Throttled Requests](#handling-throttled-requests)
772
837
 
@@ -1528,7 +1593,7 @@ provides a mechanism to notify users of expired credentials. With the
1528
1593
  is sent out asking the user to refresh the credentials. While the auth is
1529
1594
  disconnected, Zap runs will not be executed, to prevent more calls with expired
1530
1595
  credentials. (The runs will be
1531
- [Held](https://zapier.com/help/manage/history/view-and-manage-your-zap-history#holding),
1596
+ [Held](https://help.zapier.com/hc/en-us/articles/8496291148685-View-and-manage-your-Zap-history#held-0-3),
1532
1597
  and the user will be able to replay them after reconnecting.)
1533
1598
 
1534
1599
  Example: `throw new z.errors.ExpiredAuthError('You must manually reconnect this auth.');`
@@ -1543,15 +1608,12 @@ Example: `throw new z.errors.RefreshAuthError();`
1543
1608
  ### Handling Throttled Requests
1544
1609
 
1545
1610
  Since v11.2.0, there are two types of errors that can cause Zapier to throttle an operation and retry at a later time.
1546
- This is useful if the API you're interfacing with is reports it is receiving too many requests, often indicated by
1611
+ This is useful if the API you're interfacing with reports it is receiving too many requests, often indicated by
1547
1612
  receiving a response status code of 429.
1548
1613
 
1549
1614
  If a response receives a status code of 429 and is not caught, Zapier will re-attempt the operation after a delay.
1550
- The delay can be customized by the server response containing a
1551
- [Retry-After](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header.
1552
-
1553
- Another way to request Zapier retry an operation is to throw a `ThrottledError`, which may also optionally specify a
1554
- delay in seconds:
1615
+ The delay can be customized by the server response containing a specific
1616
+ [Retry-After](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header in your error response or with a specified time delay in seconds using a `ThrottledError`:
1555
1617
 
1556
1618
  ```js
1557
1619
  const yourAfterResponse = (resp) => {
@@ -1561,6 +1623,9 @@ const yourAfterResponse = (resp) => {
1561
1623
  return resp;
1562
1624
  };
1563
1625
  ```
1626
+ Instead of a user’s Zap erroring and halting, the request will be repeatedly retried at the specified time.
1627
+
1628
+ For throttled requests that occur during processing of a webhook trigger's perform, before results are produced; there is a max retry delay of 300 seconds and a default delay of 60 seconds if none is specified. For webhook processing only, if a request during the retry attempt is also throttled, it will not be re-attempted again.
1564
1629
 
1565
1630
  ## Testing
1566
1631
 
package/README.md CHANGED
@@ -22,7 +22,7 @@ You may find docs duplicate or outdated across the Zapier site. The most up-to-d
22
22
 
23
23
  Our code is updated frequently. To see a full list of changes, look no further than [the CHANGELOG](https://github.com/zapier/zapier-platform/blob/main/CHANGELOG.md).
24
24
 
25
- This doc describes the latest CLI version (**13.0.0**), as of this writing. If you're using an older version of the CLI, you may want to check out these historical releases:
25
+ This doc describes the latest CLI version (**14.0.1**), as of this writing. If you're using an older version of the CLI, you may want to check out these historical releases:
26
26
 
27
27
  - CLI Docs: [11.3.3](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@11.3.3/packages/cli/README.md), [10.2.0](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@10.2.0/packages/cli/README.md), [9.7.3](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@9.7.3/packages/cli/README.md)
28
28
  - CLI Reference: [11.3.3](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@11.3.3/packages/cli/docs/cli.md), [10.2.0](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@10.2.0/packages/cli/docs/cli.md), [9.7.3](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@9.7.3/packages/cli/docs/cli.md)
@@ -55,11 +55,13 @@ This doc describes the latest CLI version (**13.0.0**), as of this writing. If y
55
55
  * [Session](#session)
56
56
  * [OAuth1](#oauth1)
57
57
  * [OAuth2](#oauth2)
58
+ * [OAuth2 with PKCE](#oauth2-with-pkce)
58
59
  * [Connection Label](#connection-label)
59
60
  - [Resources](#resources)
60
61
  * [Resource Definition](#resource-definition)
61
62
  - [Triggers/Searches/Creates](#triggerssearchescreates)
62
63
  * [Return Types](#return-types)
64
+ + [Returning Line Items (Array of Objects)](#returning-line-items-array-of-objects)
63
65
  * [Fallback Sample](#fallback-sample)
64
66
  - [Input Fields](#input-fields)
65
67
  * [Custom/Dynamic Fields](#customdynamic-fields)
@@ -861,6 +863,99 @@ Also, `authentication.oauth2Config.getAccessToken` has access to the additional
861
863
 
862
864
  If you define `fields` to collect additional details from the user, please note that `client_id` and `client_secret` are reserved keys and cannot be used as keys for input form fields.
863
865
 
866
+
867
+ ### OAuth2 with PKCE
868
+
869
+ *Added in v14.0.0.*
870
+
871
+ Zapier's OAuth2 implementation also supports [PKCE](https://oauth.net/2/pkce/). This implementation is an extension of the OAuth2 `authorization_code` flow described above.
872
+
873
+ To use PKCE in your OAuth2 flow, you'll need to set the following variables:
874
+ 1. `enablePkce: true`
875
+ 2. `getAccessToken.body` to include `code_verifier: "{{bundle.inputData.code_verifier}}"`
876
+
877
+ The OAuth2 PKCE flow uses the same flow as OAuth2 but adds a few extra parameters:
878
+
879
+ 1. Zapier computes a `code_verifier` and `code_challenge` internally and stores the `code_verifier` in the Zapier bundle.
880
+ 2. Zapier sends the user to the authorization URL defined by your app, passing along the computed `code_challenge`.
881
+ 3. Once authorized, your website sends the user to the `redirect_uri` Zapier provided.
882
+ 4. Zapier makes a call to your API to exchange the `code` and the computed `code_verifier` for an `access_token`.
883
+ 5. Zapier stores the `access_token` and uses it to make calls on behalf of the user.
884
+
885
+ Your auth definition would look something like this:
886
+
887
+ ```js
888
+ const authentication = {
889
+ type: 'oauth2',
890
+ test: {
891
+ url: 'https://{{bundle.authData.subdomain}}.example.com/api/accounts/me.json',
892
+ },
893
+ // you can provide additional fields for inclusion in authData
894
+ oauth2Config: {
895
+ // "authorizeUrl" could also be a function returning a string url
896
+ authorizeUrl: {
897
+ method: 'GET',
898
+ url: 'https://{{bundle.inputData.subdomain}}.example.com/api/oauth2/authorize',
899
+ params: {
900
+ client_id: '{{process.env.CLIENT_ID}}',
901
+ state: '{{bundle.inputData.state}}',
902
+ redirect_uri: '{{bundle.inputData.redirect_uri}}',
903
+ response_type: 'code',
904
+ },
905
+ },
906
+ // Zapier expects a response providing {access_token: 'abcd'}
907
+ // "getAccessToken" could also be a function returning an object
908
+ getAccessToken: {
909
+ method: 'POST',
910
+ url: 'https://{{bundle.inputData.subdomain}}.example.com/api/v2/oauth2/token',
911
+ body: {
912
+ code: '{{bundle.inputData.code}}',
913
+ client_id: '{{process.env.CLIENT_ID}}',
914
+ client_secret: '{{process.env.CLIENT_SECRET}}',
915
+ redirect_uri: '{{bundle.inputData.redirect_uri}}',
916
+ grant_type: 'authorization_code',
917
+ code_verifier: '{{bundle.inputData.code_verifier}}', // Added for PKCE
918
+ },
919
+ headers: {
920
+ 'Content-Type': 'application/x-www-form-urlencoded',
921
+ },
922
+ },
923
+ scope: 'read,write',
924
+ enablePkce: true, // Added for PKCE
925
+ },
926
+ // If you need any fields upfront, put them here
927
+ fields: [
928
+ { key: 'subdomain', type: 'string', required: true, default: 'app' },
929
+ // For OAuth2 we store `access_token` and `refresh_token` automatically
930
+ // in `bundle.authData` for future use. If you need to save/use something
931
+ // that the user shouldn't need to type/choose, add a "computed" field, like:
932
+ // {key: 'user_id': type: 'string', required: false, computed: true}
933
+ // And remember to return it in oauth2Config.getAccessToken/refreshAccessToken
934
+ ],
935
+ };
936
+
937
+ const addBearerHeader = (request, z, bundle) => {
938
+ if (bundle.authData && bundle.authData.access_token) {
939
+ request.headers.Authorization = `Bearer ${bundle.authData.access_token}`;
940
+ }
941
+ return request;
942
+ };
943
+
944
+ const App = {
945
+ // ...
946
+ authentication,
947
+ beforeRequest: [addBearerHeader],
948
+ // ...
949
+ };
950
+
951
+ module.exports = App;
952
+
953
+ ```
954
+
955
+ The computed `code_verifier` uses this standard: [RFC 7636 Code Verifier](https://www.rfc-editor.org/rfc/rfc7636#section-4.1)
956
+
957
+ The computed `code_challenge` uses this standard: [RFC 7636 Code Challenge](https://www.rfc-editor.org/rfc/rfc7636#section-4.2)
958
+
864
959
  ### Connection Label
865
960
 
866
961
  When a user connects to your app via Zapier and a connection is created to hold the related data in `bundle.authData`, the connection is automatically labeled with the app name. You also have the option of setting a connection label (`connectionLabel`), which can be extremely helpful to identify information like which user is connected or what instance of your app they are connected to. That way, users don't get confused if they have multiple connections to your app.
@@ -1021,16 +1116,52 @@ You can find more details on the definition for each by looking at the [Trigger
1021
1116
  > To add a trigger, search, or create to an existing integration, run `zapier scaffold [trigger|search|create] [noun]` to create the necessary files to your project. For example, `zapier scaffold trigger post` will create a new trigger called "New Post".
1022
1117
  ### Return Types
1023
1118
 
1024
- Each of the 3 types of function expects a certain type of object. As of core v1.0.11, there are automated checks to let you know when you're trying to pass the wrong type back. For reference, each expects:
1119
+ Each of the 3 types of function should return a certain data type for use by the platform. There are automated checks to let you know when you're trying to pass the wrong type back. For reference, each expects:
1025
1120
 
1026
1121
  | Method | Return Type | Notes |
1027
1122
  |---------|-------------|----------------------------------------------------------------------------------------------------------------------|
1028
- | Trigger | Array | 0 or more objects; passed to the [deduper](https://zapier.com/developer/documentation/v2/deduplication/) if polling |
1123
+ | Trigger | Array | 0 or more objects; passed to the [deduper](https://platform.zapier.com/docs/dedupe/) if polling |
1029
1124
  | Search | Array | 0 or more objects. Only the first object will be returned, so if len > 1, put the best match first |
1030
- | Action | Object | Return values are evaluated by [`isPlainObject`](https://lodash.com/docs#isPlainObject) |
1125
+ | Create | Object | Return values are evaluated by [`isPlainObject`](https://lodash.com/docs#isPlainObject) |
1031
1126
 
1032
1127
  When a trigger function returns an empty array, the Zap will not trigger. For REST Hook triggers, this can be used to filter data if the available subscription options are not specific enough for the Zap's needs.
1033
1128
 
1129
+ #### Returning Line Items (Array of Objects)
1130
+
1131
+ In some cases, you may want to include multiple items in the data you return for Searches or Creates. To do that, return the set of items as an array of objects under a descriptive key. This may be as part of another object (like items in an invoice) or as multiple top-level items.
1132
+
1133
+ For example, a Create Order action returning an order with multiple items might look like this:
1134
+
1135
+ ```
1136
+ order = {
1137
+ name: 'Zap Zaplar',
1138
+ total_cost: 25.96,
1139
+ items: [
1140
+ { name: 'Zapier T-Shirt', unit_price: 11.99, quantity: 3, line_amount: 35.97, category: 'shirts' },
1141
+ { name: 'Orange Widget', unit_price: 7.99, quantity: 10, line_amount: 79.90, category: 'widgets' },
1142
+ { name:'Stuff', unit_price: 2.99, quantity: 7, line_amount: 20.93, category: 'stuff' },
1143
+ { name: 'Allbird Shoes', unit_price: 2.99, quantity: 7, line_amount: 20.93, category: 'shoes' },
1144
+ ],
1145
+ zip: 01002
1146
+ }
1147
+ ```
1148
+
1149
+ While a Find Users search could return multiple items under an object key within an array, like this:
1150
+
1151
+ ```
1152
+ result = [{
1153
+ users: [
1154
+ { name: 'Zap Zaplar', age: 12, city: 'Columbia', region: 'Missouri' },
1155
+ { name: 'Orange Crush', age: 28, city: 'West Ocean City', region: 'Maryland' },
1156
+ { name: 'Lego Brick', age: 91, city: 'Billund', region: 'Denmark' },
1157
+ ],
1158
+ }];
1159
+ ```
1160
+
1161
+ A standard search would return just the inner array of users, and only the first user would be provided as a final result. Returning line items instead means that the "first result" return is the object containing all the user details within it.
1162
+
1163
+ Using the standard approach is recommended, because not all Zapier integrations support line items directly, so users may need to take additional actions to reformat this data for use in their Zaps. More detail on that at [Use line items in Zaps](https://zapier.com/help/create/basics/use-line-items-in-zaps). However, there are use cases where returning multiple results is helpful enough to outweigh that additional effort.
1164
+
1034
1165
  ### Fallback Sample
1035
1166
  In cases where Zapier needs to show an example record to the user, but we are unable to get a live example from the API, Zapier will fallback to this hard-coded sample. This should reflect the data structure of the Trigger's perform method, and have dummy values that we can show to any user.
1036
1167
 
@@ -1673,7 +1804,7 @@ The available errors are:
1673
1804
 
1674
1805
  * `Error` (_added in v9.3.0_) - Stops the current operation, allowing for (auto) replay. Read more on [General Errors](#general-errors)
1675
1806
  * `HaltedError` - Stops current operation, but will never turn off Zap. Read more on [Halting Execution](#halting-execution)
1676
- * `ExpiredAuthError` - Turns off Zap and emails user to manually reconnect. Read more on [Stale Authentication Credentials](#stale-authentication-credentials)
1807
+ * `ExpiredAuthError` - Stops the current operation and emails user to manually reconnect. Read more on [Stale Authentication Credentials](#stale-authentication-credentials)
1677
1808
  * `RefreshAuthError` - (OAuth2 or Session Auth) Tells Zapier to refresh credentials and retry operation. Read more on [Stale Authentication Credentials](#stale-authentication-credentials)
1678
1809
  * `ThrottledError` (_new in v11.2.0_) - Tells Zapier to retry the current operation after a delay specified in seconds. Read more on [Handling Throttled Requests](#handling-throttled-requests)
1679
1810
 
@@ -2255,7 +2386,7 @@ This behavior has changed periodically across major versions, which changes how/
2255
2386
 
2256
2387
  ![](https://cdn.zappy.app/e835d9beca1b6489a065d51a381613f3.png)
2257
2388
 
2258
- Ensure you're handling errors correctly for your platform version. The latest released version is **13.0.0**.
2389
+ Ensure you're handling errors correctly for your platform version. The latest released version is **14.0.1**.
2259
2390
 
2260
2391
  ### HTTP Request Options
2261
2392
 
@@ -2677,7 +2808,7 @@ provides a mechanism to notify users of expired credentials. With the
2677
2808
  is sent out asking the user to refresh the credentials. While the auth is
2678
2809
  disconnected, Zap runs will not be executed, to prevent more calls with expired
2679
2810
  credentials. (The runs will be
2680
- [Held](https://zapier.com/help/manage/history/view-and-manage-your-zap-history#holding),
2811
+ [Held](https://help.zapier.com/hc/en-us/articles/8496291148685-View-and-manage-your-Zap-history#held-0-3),
2681
2812
  and the user will be able to replay them after reconnecting.)
2682
2813
 
2683
2814
  Example: `throw new z.errors.ExpiredAuthError('You must manually reconnect this auth.');`
@@ -2692,15 +2823,12 @@ Example: `throw new z.errors.RefreshAuthError();`
2692
2823
  ### Handling Throttled Requests
2693
2824
 
2694
2825
  Since v11.2.0, there are two types of errors that can cause Zapier to throttle an operation and retry at a later time.
2695
- This is useful if the API you're interfacing with is reports it is receiving too many requests, often indicated by
2826
+ This is useful if the API you're interfacing with reports it is receiving too many requests, often indicated by
2696
2827
  receiving a response status code of 429.
2697
2828
 
2698
2829
  If a response receives a status code of 429 and is not caught, Zapier will re-attempt the operation after a delay.
2699
- The delay can be customized by the server response containing a
2700
- [Retry-After](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header.
2701
-
2702
- Another way to request Zapier retry an operation is to throw a `ThrottledError`, which may also optionally specify a
2703
- delay in seconds:
2830
+ The delay can be customized by the server response containing a specific
2831
+ [Retry-After](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header in your error response or with a specified time delay in seconds using a `ThrottledError`:
2704
2832
 
2705
2833
  ```js
2706
2834
  const yourAfterResponse = (resp) => {
@@ -2710,6 +2838,9 @@ const yourAfterResponse = (resp) => {
2710
2838
  return resp;
2711
2839
  };
2712
2840
  ```
2841
+ Instead of a user’s Zap erroring and halting, the request will be repeatedly retried at the specified time.
2842
+
2843
+ For throttled requests that occur during processing of a webhook trigger's perform, before results are produced; there is a max retry delay of 300 seconds and a default delay of 60 seconds if none is specified. For webhook processing only, if a request during the retry attempt is also throttled, it will not be re-attempted again.
2713
2844
 
2714
2845
  ## Testing
2715
2846
 
@@ -3448,7 +3579,7 @@ Broadly speaking, all releases will continue to work indefinitely. While you nev
3448
3579
  For more info about which Node versions are supported, see [the faq](#how-do-i-manually-set-the-nodejs-version-to-run-my-app-with).
3449
3580
 
3450
3581
  <!-- TODO: if we decouple releases, change this -->
3451
- The most recently released version of `cli` and `core` is **13.0.0**. You can see the versions you're working with by running `zapier -v`.
3582
+ The most recently released version of `cli` and `core` is **14.0.1**. You can see the versions you're working with by running `zapier -v`.
3452
3583
 
3453
3584
  To update `cli`, run `npm install -g zapier-platform-cli`.
3454
3585
 
@@ -1 +1 @@
1
- {"version":"13.0.0","commands":{"analytics":{"id":"analytics","description":"Show the status of the analytics that are collected. Also used to change what is collected.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier analytics --mode enabled"],"flags":{"mode":{"name":"mode","type":"option","char":"m","description":"Choose how much information to share. Anonymous mode drops the OS type and Zapier user id, but keeps command info. Identifying information is used only for debugging purposes.","options":["enabled","anonymous","disabled"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"build":{"id":"build","description":"Build a pushable zip from the current directory.\n\nThis command does the following:\n\n* Creates a temporary folder\n* Copies all code into the temporary folder\n* Adds an entry point: `zapierwrapper.js`\n* Generates and validates app definition.\n* Detects dependencies via browserify (optional, on by default)\n* Zips up all needed `.js` files. If you want to include more files, add a \"includeInBuild\" property (array with strings of regexp paths) to your `.zapierapprc`.\n* Moves the zip to `build/build.zip` and `build/source.zip` and deletes the temp folder\n\nThis command is typically followed by `zapier upload`.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"disable-dependency-detection":{"name":"disable-dependency-detection","type":"boolean","description":"Disable \"smart\" file inclusion. By default, Zapier only includes files that are required by `index.js`. If you (or your dependencies) require files dynamically (such as with `require(someVar)`), then you may see \"Cannot find module\" errors. Disabling this may make your `build.zip` too large. If that's the case, try using the `includeInBuild` option in your `.zapierapprc`. See the docs about `includeInBuild` for more info.","allowNo":false},"skip-npm-install":{"name":"skip-npm-install","type":"boolean","description":"Skips installing a fresh copy of npm dependencies on build. Helpful for using `yarn` or local copies of dependencies.","hidden":true,"allowNo":false},"skip-validation":{"name":"skip-validation","type":"boolean","description":"Skips local pre-push validation checks, and remote validation check of the CLI app's schema and AppVersion integrity.","hidden":true,"allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"convert":{"id":"convert","description":"Convert a Visual Builder integration to a CLI integration.\n\nThe resulting CLI integraiton will be identical to its Visual Builder version and ready to push and use immediately!\n\nIf you re-run this command on an existing directory it will leave existing files alone and not clobber them.\n\nYou'll need to do a `zapier push` before the new version is visible in the editor, but otherwise you're good to go.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"version":{"name":"version","type":"option","char":"v","description":"Convert a specific version. Required when converting a Visual Builder integration.","required":true},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"integrationId","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.","required":true},{"name":"path","description":"Relative to your current path - IE: `.` for current directory.","required":true}]},"deprecate":{"id":"deprecate","description":"Mark a non-production version of your integration as deprecated, with removal by a certain date.\n\nUse this when an integration version will not be supported or start breaking at a known date.\n\nZapier will send an email warning users of the deprecation once a date is set, they'll start seeing it as \"Deprecated\" in the UI, and once the deprecation date arrives, if the Zaps weren't updated, they'll be paused and the users will be emailed again explaining what happened.\n\nAfter the deprecation date has passed it will be safe to delete that integration version.\n\nDo not use this if you have non-breaking changes, such as fixing help text.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier deprecate 1.2.3 2011-10-01"],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"The version to deprecate.","required":true},{"name":"date","description":"The date (YYYY-MM-DD) when Zapier will make the specified version unavailable.","required":true}]},"describe":{"id":"describe","description":"Describe the current integration.\n\nThis command prints a human readable enumeration of your integrations's\ntriggers, searches, and creates as seen by Zapier. Useful to understand how your\nresources convert and relate to different actions.\n\n* **Noun**: your action's noun\n* **Label**: your action's label\n* **Resource**: the resource (if any) this action is tied to\n* **Available Methods**: testable methods for this action","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"history":{"id":"history","description":"Get the history of your integration.\n\nHistory includes all the changes made over the lifetime of your integration. This includes everything from creation, updates, migrations, admins, and invitee changes, as well as who made the change and when.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"init":{"id":"init","description":"Initialize a new Zapier integration with a project template.\n\nAfter running this, you'll have a new integration in the specified directory. If you re-run this command on an existing directory, it will prompt before overwriting any existing files.\n\nThis doesn't register or deploy the integration with Zapier - try the `zapier register` and `zapier push` commands for that!","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier init myapp","zapier init ./path/myapp --template oauth2"],"flags":{"template":{"name":"template","type":"option","char":"t","description":"The template to start your integration with.","options":["basic-auth","callback","custom-auth","digest-auth","dynamic-dropdown","files","minimal","oauth1-trello","oauth2","search-or-create","session-auth","typescript"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"path","description":"Where to create the new integration. If the directory doesn't exist, it will be created. If the directory isn't empty, we'll ask for confirmation","required":true}]},"integrations":{"id":"integrations","description":"List integrations you have admin access to.\n\nThis command also checks the current directory for a linked integration.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["apps"],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"jobs":{"id":"jobs","description":"Lists ongoing migration or promotion jobs for the current integration.\n\nA job represents a background process that will be queued up when users execute a \"migrate\" or \"promote\" command for the current integration.\n\nEach job will be added to the end of a queue of \"promote\" and \"migration\" jobs where the \"Job Stage\" will then be initialized with \"requested\".\n\nJob stages will then move to \"estimating\", \"in_progress\" and finally one of four \"end\" stages: \"complete\", \"aborted\", \"errored\" or \"paused\".\n\nJob times will vary as it depends on the size of the queue and how many users your integration has.\n\nJobs are returned from oldest to newest.\n","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier jobs"],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"link":{"id":"link","description":"Link the current directory with an existing integration.\n\nThis command generates a `.zapierapprc` file in the directory in which it's ran. This file ties this code to an integration and is referenced frequently during `push` and `validate` operations. This file should be checked into source control.\n\nIf you're starting an integration from scratch, use `zapier init` instead.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"login":{"id":"login","description":"Configure your `~/.zapierrc` with a deploy key.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"sso":{"name":"sso","type":"boolean","char":"s","description":"Use this flag if you log into Zapier a Single Sign-On (SSO) button and don't have a Zapier password.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"logout":{"id":"logout","description":"Deactivate your active deploy key and reset `~/.zapierrc`.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"logs":{"id":"logs","description":"Print recent logs.\n\nLogs are created when your integration is run as part of a Zap. They come from explicit calls to `z.console.log()`, usage of `z.request()`, and any runtime errors.\n\nThis won't show logs from running locally with `zapier test`, since those never hit our server.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"version":{"name":"version","type":"option","char":"v","description":"Filter logs to the specified version."},"status":{"name":"status","type":"option","char":"s","description":"Filter logs to only see errors or successes","options":["any","success","error"],"default":"any"},"type":{"name":"type","type":"option","char":"t","description":"See logs of the specified type","options":["console","bundle","http"],"default":"console"},"detailed":{"name":"detailed","type":"boolean","description":"See extra info, like request/response body and headers.","allowNo":false},"user":{"name":"user","type":"option","char":"u","description":"Only show logs for this user. Defaults to your account.","default":"me"},"limit":{"name":"limit","type":"option","description":"Cap the number of logs returned. Max is 50 (also the default)","default":50},"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"migrate":{"id":"migrate","description":"Migrate a percentage of users or a single user from one version of your integration to another.\n\nStart a migration to move users between different versions of your integration. You may also \"revert\" by simply swapping the from/to verion strings in the command line arguments (i.e. `zapier migrate 1.0.1 1.0.0`).\n\n**Only use this command to migrate users between non-breaking versions, use `zapier deprecate` if you have breaking changes!**\n\nMigration time varies based on the number of affected Zaps. Be patient and check `zapier jobs` to track the status. Or use `zapier history` if you want to see older jobs.\n\nSince a migration is only for non-breaking changes, users are not emailed about the update/migration. It will be a transparent process for them.\n\nWe recommend migrating a small subset of users first, via the percent argument, then watching error logs of the new version for any sort of odd behavior. When you feel confident there are no bugs, go ahead and migrate everyone. If you see unexpected errors, you can revert.\n\nYou can migrate a specific user's Zaps by using `--user` (i.e. `zapier migrate 1.0.0 1.0.1 --user=user@example.com`). This will migrate Zaps in any account the user is a member of where the following criteria is met.\n\n - The Zap is owned by the user.\n - The Zap is not shared.\n - The integration auth used is not shared.\n\nAlternatively, you can pass the `--account` flag, (i.e. `zapier migrate 1.0.0 1.0.1 --account=account@example.com`). This will migrate all users' Zaps, Private & Shared, within all accounts for which the specified user is a member.\n\n**The `--account` flag should be used cautiously as it can break shared Zaps for other users in Team or Company accounts.**\n\nYou cannot pass both `PERCENT` and `--user` or `--account`.\n\nYou cannot pass both `--user` and `--account`.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier migrate 1.0.0 1.0.1","zapier migrate 1.0.1 2.0.0 10","zapier migrate 2.0.0 2.0.1 --user=user@example.com","zapier migrate 2.0.0 2.0.1 --account=account@example.com"],"flags":{"user":{"name":"user","type":"option","description":"Migrates all of a users' Private Zaps within all accounts for which the specified user is a member"},"account":{"name":"account","type":"option","description":"Migrates all of a users' Zaps, Private & Shared, within all accounts for which the specified user is a member"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"fromVersion","description":"The version FROM which to migrate users.","required":true},{"name":"toVersion","description":"The version TO which to migrate users.","required":true},{"name":"percent","description":"Percentage (between 1 and 100) of users to migrate.","default":100}]},"promote":{"id":"promote","description":"Promote a specific version to public access.\n\nPromote an integration version into production (non-private) rotation, which means new users can use this integration version.\n\n* This **does** mark the version as the official public version - all other versions & users are grandfathered.\n* This does **NOT** build/upload or deploy a version to Zapier - you should `zapier push` first.\n* This does **NOT** move old users over to this version - `zapier migrate 1.0.0 1.0.1` does that.\n* This does **NOT** recommend old users stop using this version - `zapier deprecate 1.0.0 2017-01-01` does that.\n\nPromotes are an inherently safe operation for all existing users of your integration.\n\nIf your integration is private and passes our integration checks, this will give you a URL to a form where you can fill in additional information for your integration to go public. After reviewing, the Zapier team will approve to make it public if there are no issues or decline with feedback.\n\nCheck `zapier jobs` to track the status of the promotion. Or use `zapier history` if you want to see older jobs.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier promote 1.0.0"],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Automatically answer \"yes\" to any prompts. Useful if you want to avoid interactive prompts to run this command in CI.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"The version you want to promote.","required":true}]},"push":{"id":"push","description":"Build and upload the current integration.\n\nThis command is the same as running `zapier build` and `zapier upload` in sequence. See those for more info.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"disable-dependency-detection":{"name":"disable-dependency-detection","type":"boolean","description":"Disable \"smart\" file inclusion. By default, Zapier only includes files that are required by `index.js`. If you (or your dependencies) require files dynamically (such as with `require(someVar)`), then you may see \"Cannot find module\" errors. Disabling this may make your `build.zip` too large. If that's the case, try using the `includeInBuild` option in your `.zapierapprc`. See the docs about `includeInBuild` for more info.","allowNo":false},"skip-npm-install":{"name":"skip-npm-install","type":"boolean","description":"Skips installing a fresh copy of npm dependencies on build. Helpful for using `yarn` or local copies of dependencies.","hidden":true,"allowNo":false},"skip-validation":{"name":"skip-validation","type":"boolean","description":"Skips local pre-push validation checks, and remote validation check of the CLI app's schema and AppVersion integrity.","hidden":true,"allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"register":{"id":"register","description":"Register a new integration in your account.\n\nAfter running this, you can run `zapier push` to build and upload your integration for use in the Zapier editor.\n\nThis will change the `./.zapierapprc` (which identifies this directory as holding code for a specific integration).","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier register","zapier register \"My Cool Integration\""],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"title","description":"Your integrations's public title. Asked interactively if not present."}]},"scaffold":{"id":"scaffold","description":"Add a starting trigger, create, search, or resource to your integration.\n\nThe first argument should be one of `trigger|search|create|resource` followed by the noun that this will act on (something like \"contact\" or \"deal\").\n\nThe scaffold command does two general things:\n\n* Creates a new file (such as `triggers/contact.js`)\n* Imports and registers it inside your `index.js`\n\nYou can mix and match several options to customize the created scaffold for your project.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier scaffold trigger contact","zapier scaffold search contact --dest=my_src/searches","zapier scaffold create contact --entry=src/index.js","zapier scaffold resource contact --force"],"flags":{"dest":{"name":"dest","type":"option","char":"d","description":"Specify the new file's directory. Use this flag when you want to create a different folder structure such as `src/triggers` instead of the default `triggers`. Defaults to `[triggers|searches|creates]/{noun}`."},"test-dest":{"name":"test-dest","type":"option","description":"Specify the new test file's directory. Use this flag when you want to create a different folder structure such as `src/triggers` instead of the default `triggers`. Defaults to `test/[triggers|searches|creates]/{noun}`."},"entry":{"name":"entry","type":"option","char":"e","description":"Supply the path to your integration's root (`index.js`). Only needed if your `index.js` is in a subfolder, like `src`.","default":"index.js"},"force":{"name":"force","type":"boolean","char":"f","description":"Should we overwrite an exisiting trigger/search/create file?","allowNo":false},"no-help":{"name":"no-help","type":"boolean","description":"When scaffolding, should we skip adding helpful intro comments? Useful if this isn't your first rodeo.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"actionType","required":true,"options":["trigger","search","create","resource"]},{"name":"noun","required":true}]},"test":{"id":"test","description":"Test your integration via the \"test\" script in your \"package.json\".\n\nThis command is a wrapper around `npm test` that also validates the structure of your integration and sets up extra environment variables.\n\nYou can pass any args/flags after a `--`; they will get forwarded onto your test script.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier test","zapier test --skip-validate -- -t 30000 --grep api","zapier test -- -fo --testNamePattern \"auth pass\""],"flags":{"skip-validate":{"name":"skip-validate","type":"boolean","description":"Forgo running `zapier validate` before tests are run. This will speed up tests if you're modifying functionality of an existing integration rather than adding new actions.","allowNo":false},"yarn":{"name":"yarn","type":"boolean","description":"Use `yarn` instead of `npm`. This happens automatically if there's a `yarn.lock` file, but you can manually force `yarn` if you run tests from a sub-directory.","allowNo":false},"pnpm":{"name":"pnpm","type":"boolean","description":"Use `pnpm` instead of `npm`. This happens automatically if there's a `pnpm-lock.yaml` file, but you can manually force `pnpm` if you run tests from a sub-directory.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"upload":{"id":"upload","description":"Upload the latest build of your integration to Zapier.\n\nThis command sends both build/build.zip and build/source.zip to Zapier for use.\n\nTypically we recommend using `zapier push`, which does a build and upload, rather than `upload` by itself.\n","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"validate":{"id":"validate","description":"Validate your integration.\n\nRun the standard validation routine powered by json-schema that checks your integration for any structural errors. This is the same routine that runs during `zapier build`, `zapier upload`, `zapier push` or even as a test in `zapier test`.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier validate","zapier validate --without-style","zapier validate --format json"],"flags":{"without-style":{"name":"without-style","type":"boolean","description":"Forgo pinging the Zapier server to run further checks.","allowNo":false},"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"versions":{"id":"versions","description":"List the versions of your integration available for use in the Zapier editor.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"delete:integration":{"id":"delete:integration","description":"Delete your integration (including all versions).\n\nThis only works if there are no active users or Zaps on any version. If you only want to delete certain versions, use the `zapier delete:version` command instead. It's unlikely that you'll be able to run this on an app that you've pushed publicly, since there are usually still users.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["delete:app"],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"delete:version":{"id":"delete:version","description":"Delete a specific version of your integration.\n\nThis only works if there are no users or Zaps on that version. You will probably need to have run `zapier migrate` and `zapier deprecate` before this command will work.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"Specify the version to delete. It must have no users or Zaps.","required":true}]},"env:get":{"id":"env:get","description":"Get environment variables for a version.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier env:get 1.2.3"],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"The version to get the environment for.","required":true}]},"env:set":{"id":"env:set","description":"Set environment variables for a version.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier env:set 1.2.3 SECRET=12345 OTHER=4321"],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"The version to set the environment for. Values are copied forward when a new version is created, but this command will only ever affect the specified version.","required":true},{"name":"key-value pairs...","description":"The key-value pairs to set. Keys are case-insensitive. Each pair should be space separated and pairs should be separated by an `=`. For example: `A=123 B=456`"}]},"env:unset":{"id":"env:unset","description":"Unset environment variables for a version.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier env:unset 1.2.3 SECRET OTHER"],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"The version to set the environment for.","required":true},{"name":"keys...","description":"The keys to unset. Keys are case-insensitive."}]},"team:add":{"id":"team:add","description":"Add a team member to your integration.\n\nThese users come in three levels:\n\n * `admin`, who can edit everything about the integration\n * `collaborator`, who has read-only access for the app, and will receive periodic email updates. These updates include quarterly health scores and more.\n * `subscriber`, who can't directly access the app, but will receive periodic email updates. These updates include quarterly health scores and more.\n\nTeam members can be freely added and removed.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["team:invite"],"examples":["zapier team:add bruce@wayne.com admin","zapier team:add robin@wayne.com collaborator \"Hey Robin, check out this app.\"","zapier team:add alfred@wayne.com subscriber \"Hey Alfred, check out this app.\""],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"email","description":"The user to be invited. If they don't have a Zapier account, they'll be prompted to create one.","required":true},{"name":"role","description":"The level the invited team member should be at. Admins can edit everything and get email updates. Collaborators have read-access to the app and get email updates. Subscribers only get email updates.","required":true,"options":["admin","collaborator","subscriber"]},{"name":"message","description":"A message sent in the email to your team member, if you need to provide context. Wrap the message in quotes to ensure spaces get saved."}]},"team:get":{"id":"team:get","description":"Get team members involved with your integration.\n\nThese users come in three levels:\n\n * `admin`, who can edit everything about the integration\n * `collaborator`, who has read-only access for the app, and will receive periodic email updates. These updates include quarterly health scores and more.\n * `subscriber`, who can't directly access the app, but will receive periodic email updates. These updates include quarterly health scores and more.\n\nUse the `zapier team:add` and `zapier team:remove` commands to modify your team.\n","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["team:list"],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"team:remove":{"id":"team:remove","description":"Remove a team member from all versions of your integration.\n\nAdmins will immediately lose write access to the integration.\nCollaborators will immediately lose read access to the integration.\nSubscribers won't receive future email updates.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["team:delete"],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"users:add":{"id":"users:add","description":"Add a user to some or all versions of your integration.\n\nWhen this command is run, we'll send an email to the user inviting them to try your integration. You can track the status of that invite using the `zapier users:get` command.\n\nInvited users will be able to see your integration's name, logo, and description. They'll also be able to create Zaps using any available triggers and actions.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["users:invite"],"examples":["zapier users:add bruce@wayne.com","zapier users:add alfred@wayne.com 1.2.3"],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"Skip confirmation. Useful for running programatically.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"email","description":"The user to be invited. If they don't have a Zapier account, they'll be prompted to create one.","required":true},{"name":"version","description":"A version string (like 1.2.3). Optional, used only if you want to invite a user to a specific version instead of all versions."}]},"users:get":{"id":"users:get","description":"Get a list of users who have been invited to your integration.\n\nNote that this list of users is NOT a comprehensive list of everyone who is using your integration. It only includes users who were invited directly by email (using the `\u001b[36mzapier users:add\u001b[39m` command or the web UI). Users who joined by clicking links generated using the `\u001b[36mzapier user:links\u001b[39m` command won't show up here.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["users:list"],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"users:links":{"id":"users:links","description":"Get a list of links that are used to invite users to your integration.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"users:remove":{"id":"users:remove","description":"Remove a user from all versions of your integration.\n\nWhen this command is run, their Zaps will immediately turn off. They won't be able to use your app again until they're re-invited or it has gone public. In practice, this command isn't run often as it's very disruptive to users.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["users:delete"],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"Skips confirmation. Useful for running programatically.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"email","description":"The user to be removed.","required":true}]}}}
1
+ {"version":"14.0.1","commands":{"analytics":{"id":"analytics","description":"Show the status of the analytics that are collected. Also used to change what is collected.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier analytics --mode enabled"],"flags":{"mode":{"name":"mode","type":"option","char":"m","description":"Choose how much information to share. Anonymous mode drops the OS type and Zapier user id, but keeps command info. Identifying information is used only for debugging purposes.","options":["enabled","anonymous","disabled"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"build":{"id":"build","description":"Build a pushable zip from the current directory.\n\nThis command does the following:\n\n* Creates a temporary folder\n* Copies all code into the temporary folder\n* Adds an entry point: `zapierwrapper.js`\n* Generates and validates app definition.\n* Detects dependencies via browserify (optional, on by default)\n* Zips up all needed `.js` files. If you want to include more files, add a \"includeInBuild\" property (array with strings of regexp paths) to your `.zapierapprc`.\n* Moves the zip to `build/build.zip` and `build/source.zip` and deletes the temp folder\n\nThis command is typically followed by `zapier upload`.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"disable-dependency-detection":{"name":"disable-dependency-detection","type":"boolean","description":"Disable \"smart\" file inclusion. By default, Zapier only includes files that are required by `index.js`. If you (or your dependencies) require files dynamically (such as with `require(someVar)`), then you may see \"Cannot find module\" errors. Disabling this may make your `build.zip` too large. If that's the case, try using the `includeInBuild` option in your `.zapierapprc`. See the docs about `includeInBuild` for more info.","allowNo":false},"skip-npm-install":{"name":"skip-npm-install","type":"boolean","description":"Skips installing a fresh copy of npm dependencies on build. Helpful for using `yarn` or local copies of dependencies.","hidden":true,"allowNo":false},"skip-validation":{"name":"skip-validation","type":"boolean","description":"Skips local pre-push validation checks, and remote validation check of the CLI app's schema and AppVersion integrity.","hidden":true,"allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"convert":{"id":"convert","description":"Convert a Visual Builder integration to a CLI integration.\n\nThe resulting CLI integration will be identical to its Visual Builder version and ready to push and use immediately!\n\nIf you re-run this command on an existing directory it will leave existing files alone and not clobber them.\n\nYou'll need to do a `zapier push` before the new version is visible in the editor, but otherwise you're good to go.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"version":{"name":"version","type":"option","char":"v","description":"Convert a specific version. Required when converting a Visual Builder integration.","required":true},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"integrationId","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.","required":true},{"name":"path","description":"Relative to your current path - IE: `.` for current directory.","required":true}]},"deprecate":{"id":"deprecate","description":"Mark a non-production version of your integration as deprecated, with removal by a certain date.\n\nUse this when an integration version will not be supported or start breaking at a known date.\n\nZapier will send an email warning users of the deprecation once a date is set, they'll start seeing it as \"Deprecated\" in the UI, and once the deprecation date arrives, if the Zaps weren't updated, they'll be paused and the users will be emailed again explaining what happened.\n\nAfter the deprecation date has passed it will be safe to delete that integration version.\n\nDo not use this if you have non-breaking changes, such as fixing help text.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier deprecate 1.2.3 2011-10-01"],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"The version to deprecate.","required":true},{"name":"date","description":"The date (YYYY-MM-DD) when Zapier will make the specified version unavailable.","required":true}]},"describe":{"id":"describe","description":"Describe the current integration.\n\nThis command prints a human readable enumeration of your integrations's\ntriggers, searches, and creates as seen by Zapier. Useful to understand how your\nresources convert and relate to different actions.\n\n* **Noun**: your action's noun\n* **Label**: your action's label\n* **Resource**: the resource (if any) this action is tied to\n* **Available Methods**: testable methods for this action","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"history":{"id":"history","description":"Get the history of your integration.\n\nHistory includes all the changes made over the lifetime of your integration. This includes everything from creation, updates, migrations, admins, and invitee changes, as well as who made the change and when.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"init":{"id":"init","description":"Initialize a new Zapier integration with a project template.\n\nAfter running this, you'll have a new integration in the specified directory. If you re-run this command on an existing directory, it will prompt before overwriting any existing files.\n\nThis doesn't register or deploy the integration with Zapier - try the `zapier register` and `zapier push` commands for that!","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier init myapp","zapier init ./path/myapp --template oauth2"],"flags":{"template":{"name":"template","type":"option","char":"t","description":"The template to start your integration with.","options":["basic-auth","callback","custom-auth","digest-auth","dynamic-dropdown","files","minimal","oauth1-trello","oauth2","search-or-create","session-auth","typescript"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"path","description":"Where to create the new integration. If the directory doesn't exist, it will be created. If the directory isn't empty, we'll ask for confirmation","required":true}]},"integrations":{"id":"integrations","description":"List integrations you have admin access to.\n\nThis command also checks the current directory for a linked integration.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["apps"],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"jobs":{"id":"jobs","description":"Lists ongoing migration or promotion jobs for the current integration.\n\nA job represents a background process that will be queued up when users execute a \"migrate\" or \"promote\" command for the current integration.\n\nEach job will be added to the end of a queue of \"promote\" and \"migration\" jobs where the \"Job Stage\" will then be initialized with \"requested\".\n\nJob stages will then move to \"estimating\", \"in_progress\" and finally one of four \"end\" stages: \"complete\", \"aborted\", \"errored\" or \"paused\".\n\nJob times will vary as it depends on the size of the queue and how many users your integration has.\n\nJobs are returned from oldest to newest.\n","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier jobs"],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"link":{"id":"link","description":"Link the current directory with an existing integration.\n\nThis command generates a `.zapierapprc` file in the directory in which it's ran. This file ties this code to an integration and is referenced frequently during `push` and `validate` operations. This file should be checked into source control.\n\nIf you're starting an integration from scratch, use `zapier init` instead.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"login":{"id":"login","description":"Configure your `~/.zapierrc` with a deploy key.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"sso":{"name":"sso","type":"boolean","char":"s","description":"Use this flag if you log into Zapier a Single Sign-On (SSO) button and don't have a Zapier password.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"logout":{"id":"logout","description":"Deactivate your active deploy key and reset `~/.zapierrc`.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"logs":{"id":"logs","description":"Print recent logs.\n\nLogs are created when your integration is run as part of a Zap. They come from explicit calls to `z.console.log()`, usage of `z.request()`, and any runtime errors.\n\nThis won't show logs from running locally with `zapier test`, since those never hit our server.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"version":{"name":"version","type":"option","char":"v","description":"Filter logs to the specified version."},"status":{"name":"status","type":"option","char":"s","description":"Filter logs to only see errors or successes","options":["any","success","error"],"default":"any"},"type":{"name":"type","type":"option","char":"t","description":"See logs of the specified type","options":["console","bundle","http"],"default":"console"},"detailed":{"name":"detailed","type":"boolean","description":"See extra info, like request/response body and headers.","allowNo":false},"user":{"name":"user","type":"option","char":"u","description":"Only show logs for this user. Defaults to your account.","default":"me"},"limit":{"name":"limit","type":"option","description":"Cap the number of logs returned. Max is 50 (also the default)","default":50},"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"migrate":{"id":"migrate","description":"Migrate a percentage of users or a single user from one version of your integration to another.\n\nStart a migration to move users between different versions of your integration. You may also \"revert\" by simply swapping the from/to verion strings in the command line arguments (i.e. `zapier migrate 1.0.1 1.0.0`).\n\n**Only use this command to migrate users between non-breaking versions, use `zapier deprecate` if you have breaking changes!**\n\nMigration time varies based on the number of affected Zaps. Be patient and check `zapier jobs` to track the status. Or use `zapier history` if you want to see older jobs.\n\nSince a migration is only for non-breaking changes, users are not emailed about the update/migration. It will be a transparent process for them.\n\nWe recommend migrating a small subset of users first, via the percent argument, then watching error logs of the new version for any sort of odd behavior. When you feel confident there are no bugs, go ahead and migrate everyone. If you see unexpected errors, you can revert.\n\nYou can migrate a specific user's Zaps by using `--user` (i.e. `zapier migrate 1.0.0 1.0.1 --user=user@example.com`). This will migrate Zaps in any account the user is a member of where the following criteria is met.\n\n - The Zap is owned by the user.\n - The Zap is not shared.\n - The integration auth used is not shared.\n\nAlternatively, you can pass the `--account` flag, (i.e. `zapier migrate 1.0.0 1.0.1 --account=account@example.com`). This will migrate all users' Zaps, Private & Shared, within all accounts for which the specified user is a member.\n\n**The `--account` flag should be used cautiously as it can break shared Zaps for other users in Team or Company accounts.**\n\nYou cannot pass both `PERCENT` and `--user` or `--account`.\n\nYou cannot pass both `--user` and `--account`.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier migrate 1.0.0 1.0.1","zapier migrate 1.0.1 2.0.0 10","zapier migrate 2.0.0 2.0.1 --user=user@example.com","zapier migrate 2.0.0 2.0.1 --account=account@example.com"],"flags":{"user":{"name":"user","type":"option","description":"Migrates all of a users' Private Zaps within all accounts for which the specified user is a member"},"account":{"name":"account","type":"option","description":"Migrates all of a users' Zaps, Private & Shared, within all accounts for which the specified user is a member"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"fromVersion","description":"The version FROM which to migrate users.","required":true},{"name":"toVersion","description":"The version TO which to migrate users.","required":true},{"name":"percent","description":"Percentage (between 1 and 100) of users to migrate.","default":100}]},"promote":{"id":"promote","description":"Promote a specific version to public access.\n\nPromote an integration version into production (non-private) rotation, which means new users can use this integration version.\n\n* This **does** mark the version as the official public version - all other versions & users are grandfathered.\n* This does **NOT** build/upload or deploy a version to Zapier - you should `zapier push` first.\n* This does **NOT** move old users over to this version - `zapier migrate 1.0.0 1.0.1` does that.\n* This does **NOT** recommend old users stop using this version - `zapier deprecate 1.0.0 2017-01-01` does that.\n\nPromotes are an inherently safe operation for all existing users of your integration.\n\nIf your integration is private and passes our integration checks, this will give you a URL to a form where you can fill in additional information for your integration to go public. After reviewing, the Zapier team will approve to make it public if there are no issues or decline with feedback.\n\nCheck `zapier jobs` to track the status of the promotion. Or use `zapier history` if you want to see older jobs.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier promote 1.0.0"],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Automatically answer \"yes\" to any prompts. Useful if you want to avoid interactive prompts to run this command in CI.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"The version you want to promote.","required":true}]},"push":{"id":"push","description":"Build and upload the current integration.\n\nThis command is the same as running `zapier build` and `zapier upload` in sequence. See those for more info.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"disable-dependency-detection":{"name":"disable-dependency-detection","type":"boolean","description":"Disable \"smart\" file inclusion. By default, Zapier only includes files that are required by `index.js`. If you (or your dependencies) require files dynamically (such as with `require(someVar)`), then you may see \"Cannot find module\" errors. Disabling this may make your `build.zip` too large. If that's the case, try using the `includeInBuild` option in your `.zapierapprc`. See the docs about `includeInBuild` for more info.","allowNo":false},"skip-npm-install":{"name":"skip-npm-install","type":"boolean","description":"Skips installing a fresh copy of npm dependencies on build. Helpful for using `yarn` or local copies of dependencies.","hidden":true,"allowNo":false},"skip-validation":{"name":"skip-validation","type":"boolean","description":"Skips local pre-push validation checks, and remote validation check of the CLI app's schema and AppVersion integrity.","hidden":true,"allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"register":{"id":"register","description":"Register a new integration in your account, or update the existing one if a `.zapierapprc` file is found.\n\nThis command creates a new integration and links it in the `./.zapierapprc` file. If `.zapierapprc` already exists, it will ask you if you want to update the currently-linked integration, as opposed to creating a new one.\n\nAfter registering a new integration, you can run `zapier push` to build and upload your integration for use in the Zapier editor. This will change `.zapierapprc`, which identifies this directory as holding code for a specific integration.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier register","zapier register \"My Cool Integration\"","zapier register \"My Cool Integration\" --desc \"My Cool Integration helps you integrate your apps with the apps that you need.\" --no-subscribe","zapier register \"My Cool Integration\" --url \"https://www.zapier.com\" --audience private --role employee --category marketing-automation","zapier register --subscribe"],"flags":{"desc":{"name":"desc","type":"option","char":"D","description":"A sentence describing your app in 140 characters or less, e.g. \"Trello is a team collaboration tool to organize tasks and keep projects on track.\""},"url":{"name":"url","type":"option","char":"u","description":"The homepage URL of your app, e.g., https://example.com."},"audience":{"name":"audience","type":"option","char":"a","description":"Are you building a public or private integration?"},"role":{"name":"role","type":"option","char":"r","description":"What is your relationship with the app you're integrating with Zapier?"},"category":{"name":"category","type":"option","char":"c","description":"How would you categorize your app? Choose the most appropriate option for your app's core features."},"subscribe":{"name":"subscribe","type":"boolean","char":"s","description":"Get tips and recommendations about this integration along with our monthly newsletter that details the performance of your integration and the latest Zapier news.","allowNo":true},"yes":{"name":"yes","type":"boolean","char":"y","description":"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.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"title","description":"Your integrations's public title. Asked interactively if not present."}]},"scaffold":{"id":"scaffold","description":"Add a starting trigger, create, search, or resource to your integration.\n\nThe first argument should be one of `trigger|search|create|resource` followed by the noun that this will act on (something like \"contact\" or \"deal\").\n\nThe scaffold command does two general things:\n\n* Creates a new file (such as `triggers/contact.js`)\n* Imports and registers it inside your `index.js`\n\nYou can mix and match several options to customize the created scaffold for your project.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier scaffold trigger contact","zapier scaffold search contact --dest=my_src/searches","zapier scaffold create contact --entry=src/index.js","zapier scaffold resource contact --force"],"flags":{"dest":{"name":"dest","type":"option","char":"d","description":"Specify the new file's directory. Use this flag when you want to create a different folder structure such as `src/triggers` instead of the default `triggers`. Defaults to `[triggers|searches|creates]/{noun}`."},"test-dest":{"name":"test-dest","type":"option","description":"Specify the new test file's directory. Use this flag when you want to create a different folder structure such as `src/triggers` instead of the default `triggers`. Defaults to `test/[triggers|searches|creates]/{noun}`."},"entry":{"name":"entry","type":"option","char":"e","description":"Supply the path to your integration's root (`index.js`). Only needed if your `index.js` is in a subfolder, like `src`.","default":"index.js"},"force":{"name":"force","type":"boolean","char":"f","description":"Should we overwrite an exisiting trigger/search/create file?","allowNo":false},"no-help":{"name":"no-help","type":"boolean","description":"When scaffolding, should we skip adding helpful intro comments? Useful if this isn't your first rodeo.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"actionType","required":true,"options":["trigger","search","create","resource"]},{"name":"noun","required":true}]},"test":{"id":"test","description":"Test your integration via the \"test\" script in your \"package.json\".\n\nThis command is a wrapper around `npm test` that also validates the structure of your integration and sets up extra environment variables.\n\nYou can pass any args/flags after a `--`; they will get forwarded onto your test script.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier test","zapier test --skip-validate -- -t 30000 --grep api","zapier test -- -fo --testNamePattern \"auth pass\""],"flags":{"skip-validate":{"name":"skip-validate","type":"boolean","description":"Forgo running `zapier validate` before tests are run. This will speed up tests if you're modifying functionality of an existing integration rather than adding new actions.","allowNo":false},"yarn":{"name":"yarn","type":"boolean","description":"Use `yarn` instead of `npm`. This happens automatically if there's a `yarn.lock` file, but you can manually force `yarn` if you run tests from a sub-directory.","allowNo":false},"pnpm":{"name":"pnpm","type":"boolean","description":"Use `pnpm` instead of `npm`. This happens automatically if there's a `pnpm-lock.yaml` file, but you can manually force `pnpm` if you run tests from a sub-directory.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"upload":{"id":"upload","description":"Upload the latest build of your integration to Zapier.\n\nThis command sends both build/build.zip and build/source.zip to Zapier for use.\n\nTypically we recommend using `zapier push`, which does a build and upload, rather than `upload` by itself.\n","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"validate":{"id":"validate","description":"Validate your integration.\n\nRun the standard validation routine powered by json-schema that checks your integration for any structural errors. This is the same routine that runs during `zapier build`, `zapier upload`, `zapier push` or even as a test in `zapier test`.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier validate","zapier validate --without-style","zapier validate --format json"],"flags":{"without-style":{"name":"without-style","type":"boolean","description":"Forgo pinging the Zapier server to run further checks.","allowNo":false},"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"versions":{"id":"versions","description":"List the versions of your integration available for use in the Zapier editor.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"delete:integration":{"id":"delete:integration","description":"Delete your integration (including all versions).\n\nThis only works if there are no active users or Zaps on any version. If you only want to delete certain versions, use the `zapier delete:version` command instead. It's unlikely that you'll be able to run this on an app that you've pushed publicly, since there are usually still users.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["delete:app"],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"delete:version":{"id":"delete:version","description":"Delete a specific version of your integration.\n\nThis only works if there are no users or Zaps on that version. You will probably need to have run `zapier migrate` and `zapier deprecate` before this command will work.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"Specify the version to delete. It must have no users or Zaps.","required":true}]},"env:get":{"id":"env:get","description":"Get environment variables for a version.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier env:get 1.2.3"],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"The version to get the environment for.","required":true}]},"env:set":{"id":"env:set","description":"Set environment variables for a version.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier env:set 1.2.3 SECRET=12345 OTHER=4321"],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"The version to set the environment for. Values are copied forward when a new version is created, but this command will only ever affect the specified version.","required":true},{"name":"key-value pairs...","description":"The key-value pairs to set. Keys are case-insensitive. Each pair should be space separated and pairs should be separated by an `=`. For example: `A=123 B=456`"}]},"env:unset":{"id":"env:unset","description":"Unset environment variables for a version.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"examples":["zapier env:unset 1.2.3 SECRET OTHER"],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"version","description":"The version to set the environment for.","required":true},{"name":"keys...","description":"The keys to unset. Keys are case-insensitive."}]},"team:add":{"id":"team:add","description":"Add a team member to your integration.\n\nThese users come in three levels:\n\n * `admin`, who can edit everything about the integration\n * `collaborator`, who has read-only access for the app, and will receive periodic email updates. These updates include quarterly health scores and more.\n * `subscriber`, who can't directly access the app, but will receive periodic email updates. These updates include quarterly health scores and more.\n\nTeam members can be freely added and removed.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["team:invite"],"examples":["zapier team:add bruce@wayne.com admin","zapier team:add robin@wayne.com collaborator \"Hey Robin, check out this app.\"","zapier team:add alfred@wayne.com subscriber \"Hey Alfred, check out this app.\""],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"email","description":"The user to be invited. If they don't have a Zapier account, they'll be prompted to create one.","required":true},{"name":"role","description":"The level the invited team member should be at. Admins can edit everything and get email updates. Collaborators have read-access to the app and get email updates. Subscribers only get email updates.","required":true,"options":["admin","collaborator","subscriber"]},{"name":"message","description":"A message sent in the email to your team member, if you need to provide context. Wrap the message in quotes to ensure spaces get saved."}]},"team:get":{"id":"team:get","description":"Get team members involved with your integration.\n\nThese users come in three levels:\n\n * `admin`, who can edit everything about the integration\n * `collaborator`, who has read-only access for the app, and will receive periodic email updates. These updates include quarterly health scores and more.\n * `subscriber`, who can't directly access the app, but will receive periodic email updates. These updates include quarterly health scores and more.\n\nUse the `zapier team:add` and `zapier team:remove` commands to modify your team.\n","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["team:list"],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"team:remove":{"id":"team:remove","description":"Remove a team member from all versions of your integration.\n\nAdmins will immediately lose write access to the integration.\nCollaborators will immediately lose read access to the integration.\nSubscribers won't receive future email updates.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["team:delete"],"flags":{"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"users:add":{"id":"users:add","description":"Add a user to some or all versions of your integration.\n\nWhen this command is run, we'll send an email to the user inviting them to try your integration. You can track the status of that invite using the `zapier users:get` command.\n\nInvited users will be able to see your integration's name, logo, and description. They'll also be able to create Zaps using any available triggers and actions.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["users:invite"],"examples":["zapier users:add bruce@wayne.com","zapier users:add alfred@wayne.com 1.2.3"],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"Skip confirmation. Useful for running programatically.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"email","description":"The user to be invited. If they don't have a Zapier account, they'll be prompted to create one.","required":true},{"name":"version","description":"A version string (like 1.2.3). Optional, used only if you want to invite a user to a specific version instead of all versions."}]},"users:get":{"id":"users:get","description":"Get a list of users who have been invited to your integration.\n\nNote that this list of users is NOT a comprehensive list of everyone who is using your integration. It only includes users who were invited directly by email (using the `\u001b[36mzapier users:add\u001b[39m` command or the web UI). Users who joined by clicking links generated using the `\u001b[36mzapier user:links\u001b[39m` command won't show up here.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["users:list"],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"users:links":{"id":"users:links","description":"Get a list of links that are used to invite users to your integration.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":[],"flags":{"format":{"name":"format","type":"option","char":"f","description":"Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.","options":["plain","json","raw","row","table"],"default":"table"},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[]},"users:remove":{"id":"users:remove","description":"Remove a user from all versions of your integration.\n\nWhen this command is run, their Zaps will immediately turn off. They won't be able to use your app again until they're re-invited or it has gone public. In practice, this command isn't run often as it's very disruptive to users.","pluginName":"zapier-platform-cli","pluginType":"core","aliases":["users:delete"],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"Skips confirmation. Useful for running programatically.","allowNo":false},"debug":{"name":"debug","type":"boolean","char":"d","description":"Show extra debugging output.","allowNo":false},"invokedFromAnotherCommand":{"name":"invokedFromAnotherCommand","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"email","description":"The user to be removed.","required":true}]}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zapier-platform-cli",
3
- "version": "13.0.0",
3
+ "version": "14.0.1",
4
4
  "description": "The CLI for managing integrations in Zapier Developer Platform.",
5
5
  "repository": "zapier/zapier-platform",
6
6
  "homepage": "https://platform.zapier.com/",
@@ -74,6 +74,8 @@
74
74
  },
75
75
  "devDependencies": {
76
76
  "@oclif/dev-cli": "^1.26.10",
77
+ "@oclif/test": "^1.2.9",
78
+ "chai": "^4.3.7",
77
79
  "decompress": "4.2.1",
78
80
  "litdoc": "1.5.6",
79
81
  "markdown-toc": "^1",
@@ -99,7 +101,8 @@
99
101
  "./src/oclif/hooks/deprecated",
100
102
  "./src/oclif/hooks/updateNotifier",
101
103
  "./src/oclif/hooks/checkValidNodeVersion",
102
- "./src/oclif/hooks/renderMarkdownHelp"
104
+ "./src/oclif/hooks/renderMarkdownHelp",
105
+ "./src/oclif/hooks/getAppRegistrationFieldChoices"
103
106
  ]
104
107
  },
105
108
  "topics": {
package/src/constants.js CHANGED
@@ -57,6 +57,8 @@ const IS_TESTING =
57
57
  argvStr.includes('jest') ||
58
58
  (process.env.NODE_ENV || '').toLowerCase().startsWith('test');
59
59
 
60
+ const MAX_DESCRIPTION_LENGTH = 140;
61
+
60
62
  module.exports = {
61
63
  ANALYTICS_KEY,
62
64
  ANALYTICS_MODES,
@@ -76,6 +78,7 @@ module.exports = {
76
78
  ISSUES_URL,
77
79
  LAMBDA_VERSION,
78
80
  LEGACY_RUNNER_PACKAGE,
81
+ MAX_DESCRIPTION_LENGTH,
79
82
  NODE_VERSION,
80
83
  NODE_VERSION_CLI_REQUIRES,
81
84
  PACKAGE_NAME,
@@ -153,12 +153,54 @@ class ZapierBaseCommand extends Command {
153
153
  this.log(renderList(items, { spacer: '\n', maxWidth: stdtermwidth }));
154
154
  }
155
155
 
156
+ /**
157
+ *
158
+ * @param {Object} opts options object (as expected for this.prompt())
159
+ * @returns {string|boolean} Boolean if validation passes, string w/ error message if it doesn't
160
+ */
161
+ _getCustomValidatation(opts) {
162
+ return (input) => {
163
+ const validators = {
164
+ required: (input) =>
165
+ input.trim() === '' ? 'This field is required.' : true,
166
+ charLimit: (input, charLimit) =>
167
+ input.length > charLimit
168
+ ? `Please provide a value ${charLimit} characters or less.`
169
+ : true,
170
+ };
171
+ let aggregateResult = true;
172
+
173
+ for (const key in opts) {
174
+ if (typeof validators[key] === 'undefined') {
175
+ continue;
176
+ }
177
+
178
+ let individualResult;
179
+ if (validators[key].length > 1) {
180
+ individualResult = validators[key](input, opts[key]);
181
+ } else {
182
+ individualResult = validators[key](input);
183
+ }
184
+
185
+ if (individualResult !== true) {
186
+ aggregateResult = individualResult;
187
+ break;
188
+ }
189
+ }
190
+
191
+ return aggregateResult;
192
+ };
193
+ }
194
+
156
195
  /**
157
196
  * get user input
158
197
  * @param {string} question the question to ask the user
159
198
  * @param {object} opts `inquierer.js` opts ([read more](https://github.com/SBoudrias/Inquirer.js/#question))
160
199
  */
161
200
  async prompt(question, opts = {}) {
201
+ if (Object.keys(opts).length) {
202
+ opts.validate = this._getCustomValidatation(opts);
203
+ }
162
204
  const { ans } = await inquirer.prompt({
163
205
  type: 'string',
164
206
  ...opts,
@@ -183,8 +225,8 @@ class ZapierBaseCommand extends Command {
183
225
  }
184
226
 
185
227
  // see here for options for choices: https://github.com/SBoudrias/Inquirer.js/#question
186
- promptWithList(question, choices, pageSize) {
187
- return this.prompt(question, { type: 'list', choices, pageSize });
228
+ promptWithList(question, choices, additionalOpts) {
229
+ return this.prompt(question, { type: 'list', choices, ...additionalOpts });
188
230
  }
189
231
 
190
232
  /**
@@ -86,7 +86,7 @@ ConvertCommand.flags = buildFlags({
86
86
  });
87
87
  ConvertCommand.description = `Convert a Visual Builder integration to a CLI integration.
88
88
 
89
- The resulting CLI integraiton will be identical to its Visual Builder version and ready to push and use immediately!
89
+ The resulting CLI integration will be identical to its Visual Builder version and ready to push and use immediately!
90
90
 
91
91
  If you re-run this command on an existing directory it will leave existing files alone and not clobber them.
92
92
 
@@ -31,7 +31,7 @@ class LinkCommand extends BaseCommand {
31
31
  })),
32
32
  (app) => app.name.toLowerCase()
33
33
  ),
34
- 15
34
+ { pageSize: 15 }
35
35
  );
36
36
 
37
37
  this.startSpinner(`Setting up ${CURRENT_APP_FILE}`);
@@ -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';
@@ -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 { callAPI, writeLinkedAppConfig } = require('../../utils/api');
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
- let title = this.args.title;
9
- if (!title) {
10
- title = await this.prompt('What is the title of your integration?');
11
- }
12
-
13
- this.startSpinner(`Registering your new integration "${title}"`);
14
- const app = await callAPI('/apps', { method: 'POST', body: { title } });
15
- this.stopSpinner();
16
- this.startSpinner(
17
- `Linking app to current directory with \`${CURRENT_APP_FILE}\``
18
- );
19
- await writeLinkedAppConfig(app, process.cwd());
20
- this.stopSpinner();
21
- this.log(
22
- '\nFinished! Now that your integration is registered with Zapier, you can `zapier push`!'
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
- RegisterCommand.flags = buildFlags();
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
- After running this, you can run \`zapier push\` to build and upload your integration for use in the Zapier editor.
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 the \`./${CURRENT_APP_FILE}\` (which identifies this directory as holding code for a specific integration).`;
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,
@@ -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');
@@ -498,6 +500,7 @@ const buildAndOrUpload = async (
498
500
  let app;
499
501
  if (upload) {
500
502
  app = await getWritableApp();
503
+ checkMissingAppInfo(app);
501
504
  }
502
505
 
503
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
+ };
@@ -72,30 +72,30 @@ const renderTemplate = async (
72
72
 
73
73
  const getAuthFieldKeys = (appDefinition) => {
74
74
  const authFields = _.get(appDefinition, 'authentication.fields') || [];
75
- const fieldKeys = authFields.map((f) => f.key);
75
+ const fieldKeys = new Set(authFields.map((f) => f.key));
76
76
 
77
77
  const authType = _.get(appDefinition, 'authentication.type');
78
78
  switch (authType) {
79
79
  case 'basic': {
80
- fieldKeys.push('username', 'password');
80
+ fieldKeys.add('username');
81
+ fieldKeys.add('password');
81
82
  break;
82
83
  }
83
84
  case 'oauth1':
84
- fieldKeys.push('oauth_access_token');
85
+ fieldKeys.add('oauth_access_token');
85
86
  break;
86
87
  case 'oauth2':
87
- fieldKeys.push('access_token', 'refresh_token');
88
+ fieldKeys.add('access_token');
89
+ fieldKeys.add('refresh_token');
88
90
  break;
89
91
  default:
90
- fieldKeys.push(
91
- 'oauth_consumer_key',
92
- 'oauth_consumer_secret',
93
- 'oauth_token',
94
- 'oauth_token_secret'
95
- );
92
+ fieldKeys.add('oauth_consumer_key');
93
+ fieldKeys.add('oauth_consumer_secret');
94
+ fieldKeys.add('oauth_token');
95
+ fieldKeys.add('oauth_token_secret');
96
96
  break;
97
97
  }
98
- return fieldKeys;
98
+ return Array.from(fieldKeys);
99
99
  };
100
100
 
101
101
  const renderPackageJson = async (appInfo, appDefinition) => {
@@ -468,6 +468,9 @@ const writeZapierAppRc = async (appInfo, appDefinition, newAppDir) => {
468
468
  if (appInfo.id) {
469
469
  json.id = appInfo.id;
470
470
  }
471
+ if (appInfo.key) {
472
+ json.key = appInfo.key;
473
+ }
471
474
  if (appDefinition.legacy) {
472
475
  json.includeInBuild = ['scripting.js'];
473
476
  }