zapier-platform-cli 12.0.2 → 12.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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/master/CHANGELOG.md).
24
24
 
25
- This doc describes the latest CLI version (**12.0.2**), 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 (**12.0.3**), 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: [10.2.0](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@10.2.0/packages/cli/README.md), [9.6.0](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@9.6.0/packages/cli/README.md), [8.4.2](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@8.4.2/packages/cli/README.md)
28
28
  - CLI Reference: [10.2.0](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@10.2.0/packages/cli/docs/cli.md), [9.6.0](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@9.6.0/packages/cli/docs/cli.md), [8.4.2](https://github.com/zapier/zapier-platform/blob/zapier-platform-cli@8.4.2/packages/cli/docs/cli.md)
@@ -191,7 +191,7 @@ You can develop using any version of Node you'd like, but your eventual code mus
191
191
 
192
192
  To ensure stability for our users, we strongly encourage you run tests on `v14` sometime before your code reaches users. This can be done multiple ways.
193
193
 
194
- Firstly, by using a CI tool (like [Travis CI](https://travis-ci.org/) or [Circle CI](https://circleci.com/), which are free for open source projects). We provide a sample [.travis.yml](https://github.com/zapier/zapier-platform/blob/master/example-apps/minimal/.travis.yml) file in our template apps to get you started.
194
+ Firstly, by using a CI tool (like [Travis CI](https://travis-ci.org/) or [Circle CI](https://circleci.com/), which are free for open source projects). We provide a sample [.travis.yml](https://github.com/zapier/zapier-platform/blob/master/example-apps/trigger/.travis.yml) file in our template apps to get you started.
195
195
 
196
196
  Alternatively, you can change your local node version with tools such as [nvm](https://github.com/nvm-sh/nvm#installation-and-update). Then you can either swap to that version with `nvm use v14`, or do `nvm exec v14 zapier test` so you can run tests without having to switch versions while developing.
197
197
 
@@ -207,6 +207,7 @@ npm install -g zapier-platform-cli
207
207
  # setup auth to Zapier's platform with a deploy key
208
208
  zapier login
209
209
  ```
210
+ > Note: If you log into Zapier via the single sign-on (Google, Facebook, or Microsoft), you may not have a Zapier password. If that's the case, you'll need to generate a deploy key, go to [your Zapier developer accont here](https://zapier.com/developer/partner-settings/deploy-keys/) and create/copy a key, then run ```zapier login``` command with the --sso flag.
210
211
 
211
212
  Your Zapier CLI should be installed and ready to go at this point. Next up, we'll create our first app!
212
213
 
@@ -221,7 +222,7 @@ cd example-app
221
222
  npm install
222
223
  ```
223
224
 
224
- > Note: there are plenty of templates & example apps to choose from! [View all Example Apps here.](#example-apps).
225
+ > Note: When you run `zapier init`, you'll be presented with a list of templates to start with. Pick the one that matches a feature you'll need (such as "dynamic-dropdown" for an integration with [dynamic dropdown fields](#dynamic-dropdowns)), or select "minimal" for an integration with only the essentials. [View more example apps here](https://github.com/zapier/zapier-platform/tree/master/example-apps).
225
226
 
226
227
  You should now have a working local app. You can run several local commands to try it out.
227
228
 
@@ -442,15 +443,15 @@ zapier convert 1234 --version 1.0.1 my-app
442
443
 
443
444
  ## Authentication
444
445
 
445
- Most applications require some sort of authentication - and Zapier provides a handful of methods for helping your users authenticate with your application. Zapier will provide some of the core behaviors, but you'll likely need to handle the rest.
446
+ Most applications require some sort of authentication. The Zapier platform provides core behaviors for several common authentication methods that might be used with your application, as well as the ability to customize authentication further.
446
447
 
447
- > Hint: You can access the data tied to your authentication via the `bundle.authData` property in any method called in your app. Exceptions exist in OAuth and Session auth. Please see them below.
448
+ When a user authenticates to your application through Zapier, a "connection" is created representing their authentication details. Data tied to a specific authentication connection is included in the [bundle object](#bundle-object) under `bundle.authData`.
448
449
 
449
450
  ### Basic
450
451
 
451
452
  Useful if your app requires two pieces of information to authenticate: `username` and `password`, which only the end user can provide. By default, Zapier will do the standard Basic authentication base64 header encoding for you (via an automatically registered middleware).
452
453
 
453
- > Example App: Check out https://github.com/zapier/zapier-platform/tree/master/example-apps/basic-auth for a working example app for basic auth.
454
+ > To create a new integration with basic authentication, run `zapier init [your app name] --template basic-auth`. You can also review an example of that code [here](https://github.com/zapier/zapier-platform/tree/master/example-apps/basic-auth).
454
455
 
455
456
  If your app uses Basic auth with an encoded API key rather than a username and password, like `Authorization: Basic APIKEYHERE:x`, consider the [Custom](#custom) authentication method instead.
456
457
 
@@ -477,11 +478,11 @@ const App = {
477
478
 
478
479
  *New in v7.4.0.*
479
480
 
480
- The setup and user experience of Digest Auth is identical to Basic Auth. Users will provide Zapier their username and password and Zapier will handle all the nonce and quality of protection details automatically.
481
+ The setup and user experience of Digest Auth is identical to Basic Auth. Users provide Zapier their username and password, and Zapier handles all the nonce and quality of protection details automatically.
481
482
 
482
- > Example App: Check out https://github.com/zapier/zapier-platform/tree/master/example-apps/digest-auth for a working example app for digest auth.
483
+ > To create a new integration with digest authentication, run `zapier init [your app name] --template digest-auth`. You can also review an example of that code [here](https://github.com/zapier/zapier-platform/tree/master/example-apps/digest-auth).
483
484
 
484
- > Limitation: Currently, MD5-sess and SHA are not implemented. Only the MD5 algorithm is supported. In addition, server nonces are not reused. That means for every `z.request` call, Zapier will sends an additional request beforehand to get the server nonce.
485
+ > Limitation: Currently, MD5-sess and SHA are not implemented. Only the MD5 algorithm is supported. In addition, server nonces are not reused. That means for every `z.request` call, Zapier will send an additional request beforehand to get the server nonce.
485
486
 
486
487
  ```js
487
488
  const getConnectionLabel = (z, bundle) => {
@@ -510,9 +511,9 @@ const App = {
510
511
 
511
512
  ### Custom
512
513
 
513
- This is what most "API Key" driven apps should default to using. You'll likely provide some custom `beforeRequest` middleware or a `requestTemplate` to complete the authentication by adding/computing needed headers.
514
+ Custom auth is most commonly used for apps that authenticate with API keys, although it also provides flexibility for any unusual authentication setup. You'll likely provide some custom `beforeRequest` middleware or a `requestTemplate` (see [Making HTTP Requests](#making-http-requests)) to pass in data returned from the authentication process, most commonly by adding/computing needed headers.
514
515
 
515
- > Example App: Check out https://github.com/zapier/zapier-platform/tree/master/example-apps/custom-auth for a working example app for custom auth.
516
+ > To create a new integration with custom authentication, run `zapier init [your app name] --custom-auth`. You can also review an example of that code [here](https://github.com/zapier/zapier-platform/tree/master/example-apps/custom-auth).
516
517
 
517
518
  ```js
518
519
  const authentication = {
@@ -558,9 +559,9 @@ const App = {
558
559
 
559
560
  ### Session
560
561
 
561
- Probably the most "powerful" mechanism for authentication - it gives you the ability to exchange some user provided data for some authentication data (IE: username & password for a session key).
562
+ Session auth gives you the ability to exchange some user-provided data for some authentication data; for example, username and password for a session key. It can be used to implement almost any authentication method that uses that pattern - for example, alternative OAuth flows.
562
563
 
563
- > Example App: Check out https://github.com/zapier/zapier-platform/tree/master/example-apps/session-auth for a working example app for session auth.
564
+ > To create a new integration with session authentication, run `zapier init [your app name] --template session-auth`. You can also review an example of that code [here](https://github.com/zapier/zapier-platform/tree/master/example-apps/session-auth).
564
565
 
565
566
  ```js
566
567
  const getSessionKey = async (z, bundle) => {
@@ -628,15 +629,15 @@ const App = {
628
629
 
629
630
  ```
630
631
 
631
- > Note: For Session auth, `authentication.sessionConfig.perform` will have the provided fields in `bundle.inputData` instead of `bundle.authData` because `bundle.authData` will only have "previously existing" values, which will be empty the first time the Zap runs.
632
+ For Session auth, the function that fetches the additional authentication data needed to make API calls (`authentication.sessionConfig.perform`) has the user-provided fields in `bundle.inputData`. Afterwards, `bundle.authData` contains the data returned by that function (usually the session key or token).
632
633
 
633
634
  ### OAuth1
634
635
 
635
636
  *New in `v7.5.0`.*
636
637
 
637
- Zapier's OAuth1 implementation matches [Twitter's](https://developer.twitter.com/en/docs/basics/authentication/overview) and [Trello's](https://developers.trello.com/page/authorization) implementation of the 3-legged OAuth flow.
638
+ Zapier's OAuth1 implementation matches [Twitter](https://developer.twitter.com/en/docs/tutorials/authenticating-with-twitter-api-for-enterprise/authentication-method-overview#oauth1.0a) and [Trello](https://developer.atlassian.com/cloud/trello/guides/rest-api/authorization/#using-basic-oauth) implementations of the 3-legged OAuth flow.
638
639
 
639
- > Example Apps: Check out [oauth1-trello](https://github.com/zapier/zapier-platform/tree/master/example-apps/oauth1-trello), [oauth1-tumblr](https://github.com/zapier/zapier-platform/tree/master/example-apps/oauth1-tumblr), and [oauth1-twitter](https://github.com/zapier/zapier-platform/tree/master/example-apps/oauth1-twitter) for working example apps with OAuth1.
640
+ > To create a new integration with OAuth1, run `zapier init [your app name] --template oauth1-trello`. You can also check out [oauth1-trello](https://github.com/zapier/zapier-platform/tree/master/example-apps/oauth1-trello), [oauth1-tumblr](https://github.com/zapier/zapier-platform/tree/master/example-apps/oauth1-tumblr), and [oauth1-twitter](https://github.com/zapier/zapier-platform/tree/master/example-apps/oauth1-twitter) for working example apps with OAuth1.
640
641
 
641
642
  The flow works like this:
642
643
 
@@ -748,13 +749,15 @@ module.exports = App;
748
749
 
749
750
  ```
750
751
 
751
- > Note: For OAuth1, `authentication.oauth1Config.getRequestToken`, `authentication.oauth1Config.authorizeUrl`, and `authentication.oauth1Config.getAccessToken` will have the provided fields in `bundle.inputData` instead of `bundle.authData` because `bundle.authData` will only have "previously existing" values, which will be empty when the user hasn't connected their account on your service to Zapier. Also note that `authentication.oauth1Config.getAccessToken` has access to the users return values in `rawRequest` and `cleanedRequest` should you need to extract other values (for example from the query string).
752
+ For OAuth1, `authentication.oauth1Config.getRequestToken`, `authentication.oauth1Config.authorizeUrl`, and `authentication.oauth1Config.getAccessToken` have fields like `redirect_uri` and the temporary credentials in `bundle.inputData`. After `getAccessToken` runs, the resulting token value(s) will be stored in `bundle.authData` for the connection.
753
+
754
+ Also, `authentication.oauth1Config.getAccessToken` has access to the additional return values in `rawRequest` and `cleanedRequest` should you need to extract other values (for example, from the query string).
752
755
 
753
756
  ### OAuth2
754
757
 
755
758
  Zapier's OAuth2 implementation is based on the `authorization_code` flow, similar to [GitHub](https://developer.github.com/v3/oauth/) and [Facebook](https://developers.facebook.com/docs/authentication/server-side/).
756
759
 
757
- > Example App: Check out https://github.com/zapier/zapier-platform/tree/master/example-apps/oauth2 for a working example app for OAuth2.
760
+ > To create a new integration with OAuth2, run `zapier init [your app name] --template oauth2`. You can also check out [our working example app](https://github.com/zapier/zapier-platform/tree/master/example-apps/oauth2).
758
761
 
759
762
  If your app's OAuth2 flow uses a different grant type, such as `client_credentials`, try using [Session auth](#session) instead.
760
763
 
@@ -771,7 +774,7 @@ You are required to define:
771
774
  * `authorizeUrl`: The authorization URL
772
775
  * `getAccessToken`: The API call to fetch the access token
773
776
 
774
- If the access token has a limited life and you want to refresh the token when it expires, you'll also need to define the API call to perform that refresh. You can choose to set `autoRefresh: true`, as in the example app, if you want Zapier to automatically make a call to refresh the token after receiving a 401. See [Stale Authentication Credentials](#stale-authentication-credentials) for more details on handling auth refresh.
777
+ If the access token has a limited life and you want to refresh the token when it expires, you'll also need to define the API call to perform that refresh (`refreshAccessToken`). You can choose to set `autoRefresh: true`, as in the example app, if you want Zapier to automatically make a call to refresh the token after receiving a 401. See [Stale Authentication Credentials](#stale-authentication-credentials) for more details on handling auth refresh.
775
778
 
776
779
  You'll also likely want to set your `CLIENT_ID` and `CLIENT_SECRET` as environment variables:
777
780
 
@@ -855,7 +858,9 @@ module.exports = App;
855
858
 
856
859
  ```
857
860
 
858
- > Note: For OAuth2, `authentication.oauth2Config.authorizeUrl`, `authentication.oauth2Config.getAccessToken`, and `authentication.oauth2Config.refreshAccessToken` will have the provided fields in `bundle.inputData` instead of `bundle.authData` because `bundle.authData` will only have "previously existing" values, which will be empty when the user hasn't connected their account on your service to Zapier. Also note that `authentication.oauth2Config.getAccessToken` has access to the users return values in `rawRequest` and `cleanedRequest` should you need to extract other values (for example from the query string).
861
+ For OAuth2, `authentication.oauth2Config.authorizeUrl`, `authentication.oauth2Config.getAccessToken`, and `authentication.oauth2Config.refreshAccessToken` have fields like `redirect_uri` and `state` in `bundle.inputData`. After the code is exchanged for an access token and/or refresh token, those tokens are stored in `bundle.authData` for the connection.
862
+
863
+ Also, `authentication.oauth2Config.getAccessToken` has access to the additional return values in `rawRequest` and `cleanedRequest` should you need to extract other values (for example, from the query string).
859
864
 
860
865
 
861
866
  ## Resources
@@ -895,7 +900,7 @@ This will generate the resource file and add the necessary statements to the `in
895
900
  A resource has a few basic properties. The first is the `key`, which allows Zapier to identify the resource on our backend.
896
901
  The second is the `noun`, the user-friendly name of the resource that is presented to users throughout the Zapier UI.
897
902
 
898
- > Example App: Check out https://github.com/zapier/zapier-platform/tree/master/example-apps/resource for a working example app using resources.
903
+ > Check out [this working example app](https://github.com/zapier/zapier-platform/tree/master/example-apps/resource) to see resources in action.
899
904
 
900
905
  After those, there is a set of optional properties that tell Zapier what methods can be performed on the resource.
901
906
  The complete list of available methods can be found in the [Resource Schema Docs](https://github.com/zapier/zapier-platform/blob/master/packages/schema/docs/build/schema.md#resourceschema).
@@ -1000,14 +1005,9 @@ const App = {
1000
1005
  You can find more details on the definition for each by looking at the [Trigger Schema](https://github.com/zapier/zapier-platform/blob/master/packages/schema/docs/build/schema.md#triggerschema),
1001
1006
  [Search Schema](https://github.com/zapier/zapier-platform/blob/master/packages/schema/docs/build/schema.md#searchschema), and [Create Schema](https://github.com/zapier/zapier-platform/blob/master/packages/schema/docs/build/schema.md#createschema).
1002
1007
 
1003
- > Example App: Check out https://github.com/zapier/zapier-platform/tree/master/example-apps/trigger for a working example app using triggers.
1004
-
1005
- > Example App: Check out https://github.com/zapier/zapier-platform/tree/master/example-apps/rest-hooks for a working example app using REST hook triggers.
1006
-
1007
- > Example App: Check out https://github.com/zapier/zapier-platform/tree/master/example-apps/search for a working example app using searches.
1008
-
1009
- > Example App: Check out https://github.com/zapier/zapier-platform/tree/master/example-apps/create for a working example app using creates.
1008
+ > To create a new integration with a premade trigger, search, or create, run `zapier init [your app name]` and select from the list that appears. You can also check out our working example apps [here](https://github.com/zapier/zapier-platform/tree/master/example-apps).
1010
1009
 
1010
+ > 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".
1011
1011
  ### Return Types
1012
1012
 
1013
1013
  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:
@@ -1034,7 +1034,7 @@ In cases where Zapier needs to show an example record to the user, but we are un
1034
1034
 
1035
1035
  On each trigger, search, or create in the `operation` directive - you can provide an array of objects as fields under the `inputFields`. Input Fields are what your users would see in the main Zapier user interface. For example, you might have a "Create Contact" action with fields like "First name", "Last name", "Email", etc. These fields will be able to accept input from previous steps in a Zap, for example:
1036
1036
 
1037
- ![gif of setting up an action field in Zap Editor](https://cdn.zapier.com/storage/photos/6bd938f7cad7e34c75ba1c1d3be75ac5.gif)
1037
+ ![gif of setting up an action field in Zap Editor](https://cdn.zappy.app/52721a3cb202446b7c298e303b710471.gif)
1038
1038
 
1039
1039
  You can find more details about setting action fields from a user perspective in [our help documentation](https://zapier.com/help/creating-zap/#set-up-action-template).
1040
1040
 
@@ -1073,7 +1073,7 @@ You can find more details on the different field schema options at [our Field Sc
1073
1073
 
1074
1074
  ### Custom/Dynamic Fields
1075
1075
 
1076
- In some cases, it might be necessary to provide fields that are dynamically generated - especially for custom fields. This is a common pattern for CRMs, form software, databases and more. Basically - you can provide a function instead of a field and we'll evaluate that function - merging the dynamic fields with the static fields.
1076
+ In some cases, you may need to provide dynamically-generated fields - especially for custom ones. This is common functionality for CRMs, form software, databases, and other highly-customizable platforms. Instead of an explicit field definition, you can provide a function we'll evaluate to return a list of fields - merging the dynamic with the static fields.
1077
1077
 
1078
1078
  > You should see `bundle.inputData` partially filled in as users provide data - even in field retrieval. This allows you to build hierarchical relationships into fields (e.g. only show issues from the previously selected project).
1079
1079
 
@@ -1083,7 +1083,7 @@ In some cases, it might be necessary to provide fields that are dynamically gene
1083
1083
  const recipeFields = async (z, bundle) => {
1084
1084
  const response = await z.request('https://example.com/api/v2/fields.json');
1085
1085
 
1086
- // Call reponse.throwForStatus() if you're using core v9 or older
1086
+ // Call response.throwForStatus() if you're using zapier-platform-core v9 or older
1087
1087
 
1088
1088
  // Should return an array like [{"key":"field_1"},{"key":"field_2"}]
1089
1089
  return response.data; // response.json if you're using core v9 or older
@@ -1118,7 +1118,7 @@ const App = {
1118
1118
 
1119
1119
  ```
1120
1120
 
1121
- Additionally, if there is a field that affects the generation of dynamic fields, you can set the `altersDynamicFields: true` property. This informs the Zapier UI that whenever the value of that field changes, fields need to be recomputed. An example could be a static dropdown of "dessert type" that will change whether the function that generates dynamic fields includes a field "with sprinkles." If your field affects others, this is an important property to set.
1121
+ Additionally, if there is a field that affects the generation of dynamic fields, you can set the property `altersDynamicFields: true`. This informs the Zapier UI whenever the value of that field changes, the input fields need to be recomputed. For example, imagine the selection on a static dropdown called "Dessert Type" determining whether the function generating dynamic fields includes the field "With Sprinkles?" or not. If the value in one input field affects others, this is an important property to set.
1122
1122
 
1123
1123
  ```js
1124
1124
  module.exports = {
@@ -1156,8 +1156,8 @@ module.exports = {
1156
1156
  When using dynamic fields, the fields will be retrieved in three different contexts:
1157
1157
 
1158
1158
  * Whenever the value of a field with `altersDynamicFields` is changed, as described above.
1159
- * Whenever Zap Editor opens the "Set up" section for the trigger or action.
1160
- * Whenever the Refresh Fields button is used on the trigger or action.
1159
+ * Whenever the Zap Editor opens the "Set up" section for the trigger or action.
1160
+ * Whenever the "Refresh fields" button at the bottom of the Editor's "Set up" section is clicked.
1161
1161
 
1162
1162
  Be sure to set up your code accordingly - for example, don't rely on any input fields already having a value, since they won't have one the first time the "Set up" section loads.
1163
1163
 
@@ -1216,7 +1216,7 @@ In the above code example the dynamic property makes reference to a trigger with
1216
1216
  ```
1217
1217
 
1218
1218
  The dynamic dropdown would look something like this.
1219
- ![screenshot of dynamic dropdown in Zap Editor](https://cdn.zapier.com/storage/photos/dd31fa761e0cf9d0abc9b50438f95210.png)
1219
+ ![screenshot of dynamic dropdown in Zap Editor](https://cdn.zappy.app/6a90fcc532704f6c14b91586f5cd1d5b.png)
1220
1220
 
1221
1221
  In the first code example the dynamic dropdown is powered by a trigger. You can also use a resource to power a dynamic dropdown. To do this combine the resource key and the resource method using camel case.
1222
1222
 
@@ -1723,7 +1723,7 @@ This object holds the user's auth details and the data for the API requests.
1723
1723
 
1724
1724
  ### `bundle.inputData`
1725
1725
 
1726
- `bundle.inputData` is user-provided data for this particular run of the trigger/search/create, as defined by the `inputFields`. For example:
1726
+ `bundle.inputData` is user-provided data for this particular run of the trigger/search/create, as defined by the [`inputFields`](#input-fields). For example:
1727
1727
 
1728
1728
  ```js
1729
1729
  {
@@ -1977,22 +1977,22 @@ const App = {
1977
1977
 
1978
1978
  ## Making HTTP Requests
1979
1979
 
1980
- There are two primary ways to make HTTP requests in the Zapier platform:
1980
+ There are two ways to make HTTP requests:
1981
1981
 
1982
- 1. **Shorthand HTTP Requests** - these are simple object literals that make it easy to define simple requests.
1983
- 2. **Manual HTTP Requests** - you use `z.request([url], options)` to make the requests and control the response. Use this when you need to change options for certain requests (for all requests, use middleware).
1982
+ 1. [**Shorthand HTTP Requests**](#shorthand-http-requests) - Easy to use, but limits what you can control. Best for simple requests.
1983
+ 2. [**Manual HTTP Requests**](#manual-http-requests) - Gives you full control over the request and response.
1984
1984
 
1985
- There are also a few helper constructs you can use to reduce boilerplate:
1985
+ Use these helper constructs to reduce boilerplate:
1986
1986
 
1987
- 1. `requestTemplate` which is an shorthand HTTP request that will be merged with every request.
1988
- 2. `beforeRequest` middleware which is an array of functions to mutate a request before it is sent.
1989
- 3. `afterResponse` middleware which is an array of functions to mutate a response before it is completed.
1987
+ 1. `requestTemplate` - an object literal of [HTTP request options](#http-request-options) that will be merged with every request.
1988
+ 2. `beforeRequest` - [middleware](#using-http-middleware) that mutates every request before it is sent.
1989
+ 3. `afterResponse` - [middleware](#using-http-middleware) that mutates every response before it is completed.
1990
1990
 
1991
1991
  > Note: you can install any HTTP client you like - but this is greatly discouraged as you lose [automatic HTTP logging](#http-logging) and middleware.
1992
1992
 
1993
1993
  ### Shorthand HTTP Requests
1994
1994
 
1995
- For simple HTTP requests that do not require special pre or post processing, you can specify the HTTP options as an object literal in your app definition.
1995
+ For simple HTTP requests that do not require special pre- or post-processing, you can specify the [HTTP request options](#http-request-options) as an object literal in your app definition.
1996
1996
 
1997
1997
  This features:
1998
1998
 
@@ -2002,8 +2002,8 @@ This features:
2002
2002
 
2003
2003
  ```js
2004
2004
  const triggerShorthandRequest = {
2005
- method: 'GET',
2006
2005
  url: 'https://{{bundle.authData.subdomain}}.example.com/v2/api/recipes.json',
2006
+ method: 'GET',
2007
2007
  params: {
2008
2008
  sort_by: 'id',
2009
2009
  sort_order: 'DESC',
@@ -2025,28 +2025,36 @@ const App = {
2025
2025
 
2026
2026
  ```
2027
2027
 
2028
- In the URL above, `{{bundle.authData.subdomain}}` is automatically replaced with the live value from the bundle. If the call returns a non 2xx return code, an error is automatically raised. The response body is automatically parsed as JSON and returned.
2028
+ In the URL above, `{{bundle.authData.subdomain}}` is automatically replaced with the live value from the bundle. If the call returns a non 2xx return code, an error is automatically raised. The response body is automatically parsed as JSON or form-encoded and returned.
2029
2029
 
2030
2030
  An error will be raised if the response cannot be parsed as JSON or form-encoded. To use shorthand requests with other response types, add [middleware](#using-http-middleware) that sets `response.data` to the parsed response.
2031
2031
 
2032
2032
  ### Manual HTTP Requests
2033
2033
 
2034
- When you need to do custom processing of the response, or need to process non-JSON responses, you can make manual HTTP requests. This approach does not perform any magic - no status code checking, no automatic JSON parsing. Use this method when you need more control. Manual requests do perform lazy `{{curly}}` replacement.
2034
+ Use this when you need full control over the request/response. For example:
2035
+
2036
+ 1. To do processing (usually involving [`bundle.inputData`](#bundleinputdata)) before a request is made
2037
+ 2. To do processing of an API's response before you return data to Zapier
2038
+ 3. To process an unusual response type, such as XML
2035
2039
 
2036
- To make a manual HTTP request, use the `request` method of the `z` object:
2040
+ To make a manual request, pass your [request options](#http-request-options) to `z.request()` then use the resulting [response object](#http-response-object) to return the data you want:
2037
2041
 
2038
2042
  ```js
2039
- const listExample = async (z, bundle) => {
2040
- const customHttpOptions = {
2041
- url: 'https://example.com/api/v2/recipes.json',
2042
- headers: {
2043
- 'my-header': 'from zapier',
2043
+ const listRecipes = async (z, bundle) => {
2044
+ // Custom processing of bundle.inputData would go here...
2045
+
2046
+ const httpRequestOptions = {
2047
+ url: 'https://{{bundle.authData.subdomain}}.example.com/v2/api/recipes.json',
2048
+ method: 'GET',
2049
+ params: {
2050
+ cuisine: bundle.inputData.cuisine,
2044
2051
  },
2045
2052
  };
2046
- const response = await z.request(customHttpOptions);
2053
+ const response = await z.request(httpRequestOptions);
2054
+ const recipes = response.data;
2055
+
2056
+ // Custom processing of recipes would go here...
2047
2057
 
2048
- const recipes = response.data; // or response.json if you're using core v9 or older
2049
- // You can do any custom processing of recipes here...
2050
2058
  return recipes;
2051
2059
  };
2052
2060
 
@@ -2057,7 +2065,7 @@ const App = {
2057
2065
  // ...
2058
2066
  operation: {
2059
2067
  // ...
2060
- perform: listExample,
2068
+ perform: listRecipes,
2061
2069
  },
2062
2070
  },
2063
2071
  },
@@ -2065,6 +2073,8 @@ const App = {
2065
2073
 
2066
2074
  ```
2067
2075
 
2076
+ Manual requests perform lazy `{{curly}}` replacement. In the URL above, `{{bundle.authData.subdomain}}` is automatically replaced with the live value from the bundle.
2077
+
2068
2078
  #### POST and PUT Requests
2069
2079
 
2070
2080
  To POST or PUT data to your API you can do this:
@@ -2113,11 +2123,9 @@ const App = {
2113
2123
 
2114
2124
  ### Using HTTP middleware
2115
2125
 
2116
- If you need to process all HTTP requests in a certain way, you may be able to use one of utility HTTP middleware functions.
2126
+ To process all HTTP requests in a certain way, use the `beforeRequest` and `afterResponse` middleware functions.
2117
2127
 
2118
- > Example App: check out https://github.com/zapier/zapier-platform/tree/master/example-apps/middleware for a working example app using HTTP middleware.
2119
-
2120
- Try putting them in your app definition:
2128
+ Middleware functions go in your app definition:
2121
2129
 
2122
2130
  ```js
2123
2131
  const addHeader = (request, z, bundle) => {
@@ -2174,6 +2182,8 @@ Here is the full request lifecycle when you call `z.request({...})`:
2174
2182
 
2175
2183
  The resulting response object is returned from `z.request()`.
2176
2184
 
2185
+ > Example App: check out https://github.com/zapier/zapier-platform/tree/master/example-apps/middleware for a working example app using HTTP middleware.
2186
+
2177
2187
  #### Error Response Handling
2178
2188
 
2179
2189
  Since `v10.0.0`, `z.request()` calls `response.throwForStatus()` before it returns a response. You can disable automatic error throwing by setting `skipThrowForStatus` on the request object:
@@ -2219,13 +2229,13 @@ This behavior has changed periodically across major versions, which changes how/
2219
2229
 
2220
2230
  ![](https://cdn.zappy.app/e835d9beca1b6489a065d51a381613f3.png)
2221
2231
 
2222
- Ensure you're handling errors correctly for your platform version. The latest released version is **12.0.2**.
2232
+ Ensure you're handling errors correctly for your platform version. The latest released version is **12.0.3**.
2223
2233
 
2224
2234
  ### HTTP Request Options
2225
2235
 
2226
- Shorthand requests and manual `z.request([url], options)` calls support the following HTTP `options`:
2236
+ [Shorthand requests](#shorthand-http-requests) and [manual requests](#manual-http-requests) support the following HTTP `options`:
2227
2237
 
2228
- * `url`: HTTP url, you can provide it both `z.request(url, options)` or `z.request({url: url, ...})`.
2238
+ * `url`: HTTP url, you can provide it as a separate argument (`z.request(url, options)`) or as part of the `options` object (`z.request({url: url, ...})`).
2229
2239
  * `method`: HTTP method, default is `GET`.
2230
2240
  * `headers`: request headers object, format `{'header-key': 'header-value'}`.
2231
2241
  * `params`: URL query params object, format `{'query-key': 'query-value'}`.
@@ -2286,7 +2296,7 @@ const response = await z.request({
2286
2296
  // options
2287
2297
  });
2288
2298
 
2289
- // A bunch of examples lines for cherry picking
2299
+ // A bunch of examples for demonstration
2290
2300
  response.status;
2291
2301
  response.headers['Content-Type'];
2292
2302
  response.getHeader('content-type');
@@ -2502,7 +2512,7 @@ module.exports = App;
2502
2512
 
2503
2513
  ```
2504
2514
 
2505
- > Example App: check out https://github.com/zapier/zapier-platform/tree/master/example-apps/files for a working example app using files.
2515
+ > To create a new integration for handling files, run `zapier init [your app name] --template files`. You can also check out our working example app [here](https://github.com/zapier/zapier-platform/tree/master/example-apps/files).
2506
2516
 
2507
2517
 
2508
2518
  ## Logging
@@ -2669,14 +2679,14 @@ const yourAfterResponse = (resp) => {
2669
2679
 
2670
2680
  ## Testing
2671
2681
 
2672
- You can write unit tests for your Zapier app that run locally, outside of the Zapier editor.
2682
+ You can write unit tests for your Zapier integration that run locally, outside of the Zapier editor.
2673
2683
  You can run these tests in a CI tool like [Travis](https://travis-ci.com/).
2674
2684
 
2675
2685
  ### Writing Unit Tests
2676
2686
 
2677
- Since v10, we recommend using the [Jest](https://jestjs.io/) testing framework. After running `zapier init` you should find an example test to start from in the `test` directory.
2687
+ From v10 of `zapier-platform-cli`, we recommend using the [Jest](https://jestjs.io/) testing framework. After running `zapier init` you should find an example test to start from in the `test` directory.
2678
2688
 
2679
- > Note: On v9, the recommendation was [Mocha](https://mochajs.org/). You can still use it if you prefer Mocha.
2689
+ > Note: On v9, the recommendation was [Mocha](https://mochajs.org/). You can still use Mocha if you prefer.
2680
2690
 
2681
2691
  ```js
2682
2692
  /* globals describe, expect, test */
@@ -2693,7 +2703,7 @@ const appTester = zapier.createAppTester(App);
2693
2703
  zapier.tools.env.inject();
2694
2704
 
2695
2705
  describe('triggers', () => {
2696
- test('load recipes', async () => {
2706
+ test('new recipe', async () => {
2697
2707
  const bundle = {
2698
2708
  inputData: {
2699
2709
  style: 'mediterranean',
@@ -2701,7 +2711,7 @@ describe('triggers', () => {
2701
2711
  };
2702
2712
 
2703
2713
  const results = await appTester(
2704
- App.triggers.species.operation.perform,
2714
+ App.triggers.recipe.operation.perform,
2705
2715
  bundle
2706
2716
  );
2707
2717
  expect(results.length).toBeGreaterThan(1);
@@ -2727,8 +2737,8 @@ const App = require('../index');
2727
2737
  const appTester = zapier.createAppTester(App);
2728
2738
 
2729
2739
  describe('triggers', () => {
2730
- test('load recipes', async () => {
2731
- const adHodResult = await appTester(
2740
+ test('new recipe', async () => {
2741
+ const adHocResult = await appTester(
2732
2742
  // your in-line function takes the same [z, bundle] arguments as normal
2733
2743
  async (z, bundle) => {
2734
2744
  // requests are made using your integration's actual middleware
@@ -2759,8 +2769,8 @@ describe('triggers', () => {
2759
2769
  }
2760
2770
  );
2761
2771
 
2762
- expect(adHodResult.someHash).toEqual('a5beb6624e092adf7be31176c3079e64');
2763
- expect(adHodResult.data).toEqual({ whatever: true });
2772
+ expect(adHocResult.someHash).toEqual('a5beb6624e092adf7be31176c3079e64');
2773
+ expect(adHocResult.data).toEqual({ whatever: true });
2764
2774
 
2765
2775
  // ... rest of test
2766
2776
  });
@@ -2770,7 +2780,7 @@ describe('triggers', () => {
2770
2780
 
2771
2781
  ### Mocking Requests
2772
2782
 
2773
- While testing, it's useful to test your code without actually hitting any external services. [Nock](https://github.com/node-nock/nock) is a node.js utility that intercepts requests before they ever leave your computer. You can specify a response code, body, headers, and more. It works out of the box with `z.request` by setting up your `nock` before calling `appTester`.
2783
+ It's useful to test your code without actually hitting any external services. [Nock](https://github.com/node-nock/nock) is a Node.js utility that intercepts requests before they ever leave your computer. You can specify a response code, body, headers, and more. It works out of the box with `z.request` by setting up your `nock` before calling `appTester`.
2774
2784
 
2775
2785
  ```js
2776
2786
  /* globals describe, expect, test */
@@ -2783,7 +2793,7 @@ const appTester = zapier.createAppTester(App);
2783
2793
  const nock = require('nock');
2784
2794
 
2785
2795
  describe('triggers', () => {
2786
- test('load recipes', async () => {
2796
+ test('new recipe', async () => {
2787
2797
  const bundle = {
2788
2798
  inputData: {
2789
2799
  style: 'mediterranean',
@@ -2814,7 +2824,7 @@ describe('triggers', () => {
2814
2824
 
2815
2825
  ```
2816
2826
 
2817
- There's more info about nock and its usage in its [readme](https://github.com/node-nock/nock/blob/master/README.md).
2827
+ Here's more info about nock and its usage in the [README](https://github.com/node-nock/nock/blob/master/README.md).
2818
2828
 
2819
2829
  ### Running Unit Tests
2820
2830
 
@@ -2857,11 +2867,11 @@ zapier test
2857
2867
 
2858
2868
  ### Testing in Your CI
2859
2869
 
2860
- Whether you use Travis, Circle, Jenkins, or anything else, we aim to make it painless to test in an automated environment.
2870
+ Whether you use Travis, Circle, Jenkins, or another service, we aim to make it painless to test in an automated environment.
2861
2871
 
2862
- Behind the scenes `zapier test` is doing a pretty standard `npm test`, which could be [Jest](https://jestjs.io/) or [Mocha](https://mochajs.org/), based on your project setup.
2872
+ Behind the scenes `zapier test` does a standard `npm test`, which could be [Jest](https://jestjs.io/) or [Mocha](https://mochajs.org/), based on your project setup.
2863
2873
 
2864
- This makes it pretty straightforward to integrate into your testing interface. If you'd like to test with [Travis CI](https://travis-ci.com/) for example - the `.travis.yml` would look something like this:
2874
+ This makes it straightforward to integrate into your testing interface. For example, if you want to test with [Travis CI](https://travis-ci.com/), the `.travis.yml` would look something like this:
2865
2875
 
2866
2876
  ```yaml
2867
2877
  language: node_js
@@ -2871,13 +2881,13 @@ before_script: npm install -g zapier-platform-cli
2871
2881
  script: CLIENT_ID=1234 CLIENT_SECRET=abcd zapier test
2872
2882
  ```
2873
2883
 
2874
- You can substitute `zapier test` with `npm test`, or a direct call to `node_modules/.bin/jest`. Also, we generally recommend putting the environment variables into whatever configuration screen Jenkins or Travis provides!
2884
+ You can substitute `zapier test` with `npm test`, or a direct call to `node_modules/.bin/jest`. We recommend putting environment variables directly into the configuration screens Jenkins, Travis, or other services provide.
2875
2885
 
2876
- As an alternative to reading the deploy key from root (the default location), you may set the `ZAPIER_DEPLOY_KEY` environment variable to run privileged commands without the human input needed for `zapier login`. We suggest encrypting your deploy key in whatever manner you CI provides (such as [these instructions](https://docs.travis-ci.com/user/environment-variables/#Defining-encrypted-variables-in-.travis.yml), for Travis).
2886
+ Alternatively to reading the deploy key from root (the default location), you may set the `ZAPIER_DEPLOY_KEY` environment variable to run privileged commands without the human input needed for `zapier login`. We suggest encrypting your deploy key in the manner your CI provides (such as [these instructions](https://docs.travis-ci.com/user/environment-variables/#Defining-encrypted-variables-in-.travis.yml), for Travis).
2877
2887
 
2878
2888
  ### Debugging Tests
2879
2889
 
2880
- Sometimes tests aren't enough and you may want to step through your code and set breakpoints. The testing suite is a regular Node.js process, so debugging it doesn't take anything special. Because we recommend `jest` for testing, these instructions will outline steps for debugging w/ jest, but other test runners will work similarly. You can also refer to [Jest's own docs on the subject](https://jestjs.io/docs/en/troubleshooting#tests-are-failing-and-you-dont-know-why).
2890
+ Sometimes tests aren't enough, and you may want to step through your code and set breakpoints. The testing suite is a regular Node.js process, so debugging it doesn't take anything special. Because we recommend `jest` for testing, these instructions will outline steps for debugging w/ jest, but other test runners will work similarly. You can also refer to [Jest's own docs on the subject](https://jestjs.io/docs/en/troubleshooting#tests-are-failing-and-you-dont-know-why).
2881
2891
 
2882
2892
  To start, add the following line to the `scripts` section of your `package.json`:
2883
2893
 
@@ -2928,7 +2938,7 @@ After a few seconds, you'll see your code, the `debugger` statement, and info ab
2928
2938
 
2929
2939
  ![](https://cdn.zappy.app/4bfdfe079a344ab7aced64ad7728bc6a.png)
2930
2940
 
2931
- Using debugging in combination with thorough unit tests, you will hopefully be able to keep your Zapier integration in smooth working order.
2941
+ Debugging combined with thorough unit tests will hopefully equip you in keeping your Zapier integration in smooth working order.
2932
2942
 
2933
2943
  ## Using `npm` Modules
2934
2944
 
@@ -3049,7 +3059,7 @@ zapier push
3049
3059
 
3050
3060
  There are a lot of details left out - check out the full example app for a working setup.
3051
3061
 
3052
- > Example App: Check out https://github.com/zapier/zapier-platform/tree/master/example-apps/babel for a working example app using Babel.
3062
+ > To create a new integration with Babel, run `zapier init [your app name] --template babel`. You can also check out our working example app [here](https://github.com/zapier/zapier-platform/tree/master/example-apps/babel).
3053
3063
 
3054
3064
  ## FAQs
3055
3065
 
@@ -3083,7 +3093,7 @@ Not natively, but it can! Users have reported that the following `npm` modules a
3083
3093
  * [xml2js](https://github.com/Leonidas-from-XIV/node-xml2js)
3084
3094
  * [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser)
3085
3095
 
3086
- Since core v10, it's possible for [shorthand requests](shorthand-http-requests) to parse XML. Use an `afterResponse` [middleware](using-http-middleware) that sets `response.data` to the parsed XML:
3096
+ Since core v10, it's possible for [shorthand requests](#shorthand-http-requests) to parse XML. Use an `afterResponse` [middleware](#using-http-middleware) that sets `response.data` to the parsed XML:
3087
3097
 
3088
3098
  ```js
3089
3099
  const xml = require('pixl-xml');
@@ -3205,7 +3215,7 @@ To understand search-powered fields, we have to have a good understanding of dyn
3205
3215
 
3206
3216
  When users are selecting specific resources (for instance, a Google Sheet), it's important they're able to select the exact sheet they want. Instead of referencing the sheet by name (which may change), we match via `id` instead. Rather than directing the user copy and paste an id for every item they might encounter, there is the notion of a **dynamic dropdown**. A dropdown is a trigger that returns a list of resources. It can pull double duty and use its results to power another trigger, search, or action in the same app. It provides a list of ids with labels that show the item's name:
3207
3217
 
3208
- ![](https://cdn.zapier.com/storage/photos/fb56bdc2aab91504be0e51800bec4d64.png)
3218
+ ![](https://cdn.zappy.app/2d7eeda63ff34b70f1d1788de0117181.png)
3209
3219
 
3210
3220
  The field's value reaches your app as an id. You define this connection with the `dynamic` property, which is a string: `trigger_key.id_key.label_key`. This approach works great if the user setting up the Zap always wants the Zap to use the same spreadsheet. They specify the id during setup and the Zap runs happily.
3211
3221
 
@@ -3218,7 +3228,7 @@ The field's value reaches your app as an id. You define this connection with the
3218
3228
 
3219
3229
  If the connection between steps 3 and 4 is a common one, you can indicate that in your field by specifying `search` as a `search_key.id_key`. When paired **with a dynamic dropdown**, this will add a button to the editor that will add the search step to the user's Zap and map the id field correctly.
3220
3230
 
3221
- ![](https://cdn.zapier.com/storage/photos/d263fd3a56cf8108cb89195163e7c9aa.png)
3231
+ ![](https://cdn.zappy.app/081e63141ff05c131dadb8ebbea727b0.png)
3222
3232
 
3223
3233
  This is paired most often with "update" actions, where a required parameter will be a resource id.
3224
3234
 
@@ -3404,7 +3414,7 @@ Broadly speaking, all releases will continue to work indefinitely. While you nev
3404
3414
  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).
3405
3415
 
3406
3416
  <!-- TODO: if we decouple releases, change this -->
3407
- The most recently released version of `cli` and `core` is **12.0.2**. You can see the versions you're working with by running `zapier -v`.
3417
+ The most recently released version of `cli` and `core` is **12.0.3**. You can see the versions you're working with by running `zapier -v`.
3408
3418
 
3409
3419
  To update `cli`, run `npm install -g zapier-platform-cli`.
3410
3420