diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 648e72883..c73715e46 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -29,7 +29,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [16.x, 18.x] + node-version: [18.x, 20.x] steps: - uses: actions/checkout@v3 @@ -83,7 +83,7 @@ jobs: - ubuntu-latest # TODO: Fix tests on Windows # - windows-latest - node-version: [16.x, 18.x] + node-version: [18.x, 20.x] steps: - uses: actions/checkout@v3 @@ -108,7 +108,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [16.x, 18.x] + node-version: [18.x, 20.x] steps: - uses: actions/checkout@v3 @@ -142,7 +142,7 @@ jobs: - ubuntu-latest # TODO: Fix tests on Windows # - windows-latest - node-version: [16.x, 18.x] + node-version: [18.x, 20.x] steps: - uses: actions/checkout@v3 diff --git a/docs/cli.html b/docs/cli.html index f8105cb12..aee092834 100644 --- a/docs/cli.html +++ b/docs/cli.html @@ -754,13 +754,24 @@

invoke

Invoke an auth operation, a trigger, or a create/search action locally.

-

Usage: zapier invoke [ACTIONTYPE] [ACTIONKEY]

This command emulates how Zapier production environment would invoke your integration. It runs code locally, so you can use this command to quickly test your integration without deploying it to Zapier. This is especially useful for debugging and development.

This command loads authData from the .env file in the current directory. Create a .env file with the necessary auth data before running this command. Each line in .env should be in the format authData_FIELD_KEY=VALUE. For example, an OAuth2 integration might have a .env file like this:

+

Usage: zapier invoke [ACTIONTYPE] [ACTIONKEY]

This command emulates how Zapier production environment would invoke your integration. It runs code locally, so you can use this command to quickly test your integration without deploying it to Zapier. This is especially useful for debugging and development.

This command loads environment variables and authData from the .env file in the current directory. If you don't have an .env file yet, you can use the zapier invoke auth start command to help you initialize it, or you can manually create it.

The zapier invoke auth start subcommand will prompt you for the necessary auth fields and save them to the .env file.

Each line in the .env file should follow one of these formats:

For example, a .env file for an OAuth2 integration might look like this:


-authData_access_token=1234567890
+CLIENT_ID='your_client_id'
+
+CLIENT_SECRET='your_client_secret'
 
-authData_other_auth_field=abcdef
+authData_access_token='1234567890'
+
+authData_refresh_token='abcdefg'
+
+authData_account_name='zapier'
 
 
@@ -782,7 +793,7 @@

invoke

-

Then you can test an trigger, a search, or a create action. For example, this is how you invoke a trigger with key new_recipe:

+

Once you have the correct auth data, you can test an trigger, a search, or a create action. For example, here's how you invoke a trigger with the key new_recipe:


@@ -794,9 +805,7 @@ 

invoke

-

To add input data, use the --inputData flag. The input data can come from the command directly, a file, or stdin. See EXAMPLES below.

The following are current limitations and may be supported in the future:

    -
  • zapier invoke auth start to help you initialize the auth data in .env

    -
  • +

    To add input data, use the --inputData flag. The input data can come from the command directly, a file, or stdin. See EXAMPLES below.

    When you miss any command arguments, such as ACTIONTYPE or ACTIONKEY, the command will prompt you interactively. If you don't want to get interactive prompts, use the --non-interactive flag.

    The --debug flag will show you the HTTP request logs and any console logs you have in your code.

    The following is a non-exhaustive list of current limitations and may be supported in the future:

    • zapier invoke auth refresh to refresh the auth data in .env

    • Hook triggers, including REST hook subscribe/unsubscribe

      @@ -813,21 +822,33 @@

      invoke

    • Buffered create actions

    • +
    • Search-or-create actions

      +
    • +
    • Search-powered fields

      +
    • +
    • Field choices

      +
    • +
    • autoRefresh for OAuth2 and session auth

      +

    Arguments

    • actionType | The action type you want to invoke.
    • -
    • actionKey | The trigger/action key you want to invoke. If ACTIONTYPE is "auth", this can be "test" or "label".
    • +
    • actionKey | The trigger/action key you want to invoke. If ACTIONTYPE is "auth", this can be "label", "start", or "test".

    Flags

    • -i, --inputData | The input data to pass to the action. Must be a JSON-encoded object. The data can be passed from the command directly like '{"key": "value"}', read from a file like @file.json, or read from stdin like @-.
    • -
    • --isLoadingSample | Set bundle.meta.isLoadingSample to true. When true in production, this run is initiated by the user in the Zap editor trying to pull a sample.
    • --isFillingDynamicDropdown | Set bundle.meta.isFillingDynamicDropdown to true. Only makes sense for a polling trigger. When true in production, this poll is being used to populate a dynamic dropdown.
    • +
    • --isLoadingSample | Set bundle.meta.isLoadingSample to true. When true in production, this run is initiated by the user in the Zap editor trying to pull a sample.
    • --isPopulatingDedupe | Set bundle.meta.isPopulatingDedupe to true. Only makes sense for a polling trigger. When true in production, the results of this poll will be used initialize the deduplication list rather than trigger a Zap. This happens when a user enables a Zap.
    • --limit | Set bundle.meta.limit. Only makes sense for a trigger. When used in production, this indicates the number of items you should fetch. -1 means no limit. Defaults to -1.
    • -p, --page | Set bundle.meta.page. Only makes sense for a trigger. When used in production, this indicates which page of items you should fetch. First page is 0.
    • -
    • -z, --timezone | Set the default timezone for datetime fields. If not set, defaults to America/Chicago, which matches Zapier production behavior. Find the list timezone names at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. Defaults to America/Chicago.
    • +
    • --non-interactive | Do not show interactive prompts.
    • +
    • -z, --timezone | Set the default timezone for datetime field interpretation. If not set, defaults to America/Chicago, which matches Zapier production behavior. Find the list timezone names at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. Defaults to America/Chicago.
    • +
    • --redirect-uri | The redirect URI that will be passed to the OAuth2 authorization URL. Usually this should match the one configured in your server's OAuth2 application settings. A local HTTP server will be started to listen for the OAuth2 callback. If your server requires a non-localhost or HTTPS address for the redirect URI, you can set up port forwarding to route the non-localhost or HTTPS address to localhost. Defaults to http://localhost:9000.
    • -d, --debug | Show extra debugging output.

    Examples

    • zapier invoke
    • +
    • zapier invoke auth start
    • zapier invoke auth test
    • +
    • zapier invoke auth label
    • zapier invoke trigger new_recipe
    • zapier invoke create add_recipe --inputData '{"title": "Pancakes"}'
    • zapier invoke search find_recipe -i @file.json
    • diff --git a/docs/cli.md b/docs/cli.md index f72986a17..290718edf 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -318,13 +318,29 @@ This command also checks the current directory for a linked integration. This command emulates how Zapier production environment would invoke your integration. It runs code locally, so you can use this command to quickly test your integration without deploying it to Zapier. This is especially useful for debugging and development. -This command loads `authData` from the `.env` file in the current directory. Create a `.env` file with the necessary auth data before running this command. Each line in `.env` should be in the format `authData_FIELD_KEY=VALUE`. For example, an OAuth2 integration might have a `.env` file like this: +This command loads environment variables and `authData` from the `.env` file in the current directory. If you don't have an `.env` file yet, you can use the `zapier invoke auth start` command to help you initialize it, or you can manually create it. + +The `zapier invoke auth start` subcommand will prompt you for the necessary auth fields and save them to the `.env` file. + +Each line in the `.env` file should follow one of these formats: + +* `VAR_NAME=VALUE` for environment variables + +* `authData_FIELD_KEY=VALUE` for auth data fields + +For example, a `.env` file for an OAuth2 integration might look like this: ``` -authData_access_token=1234567890 +CLIENT_ID='your_client_id' + +CLIENT_SECRET='your_client_secret' + +authData_access_token='1234567890' + +authData_refresh_token='abcdefg' -authData_other_auth_field=abcdef +authData_account_name='zapier' ``` @@ -338,7 +354,7 @@ zapier invoke auth label # invokes authentication.test and renders connection l ``` -Then you can test an trigger, a search, or a create action. For example, this is how you invoke a trigger with key `new_recipe`: +Once you have the correct auth data, you can test an trigger, a search, or a create action. For example, here's how you invoke a trigger with the key `new_recipe`: ``` @@ -348,9 +364,11 @@ zapier invoke trigger new_recipe To add input data, use the `--inputData` flag. The input data can come from the command directly, a file, or stdin. See **EXAMPLES** below. -The following are current limitations and may be supported in the future: +When you miss any command arguments, such as ACTIONTYPE or ACTIONKEY, the command will prompt you interactively. If you don't want to get interactive prompts, use the `--non-interactive` flag. -- `zapier invoke auth start` to help you initialize the auth data in `.env` +The `--debug` flag will show you the HTTP request logs and any console logs you have in your code. + +The following is a non-exhaustive list of current limitations and may be supported in the future: - `zapier invoke auth refresh` to refresh the auth data in `.env` @@ -368,23 +386,35 @@ The following are current limitations and may be supported in the future: - Buffered create actions +- Search-or-create actions + +- Search-powered fields + +- Field choices + +- autoRefresh for OAuth2 and session auth + **Arguments** * `actionType` | The action type you want to invoke. -* `actionKey` | The trigger/action key you want to invoke. If ACTIONTYPE is "auth", this can be "test" or "label". +* `actionKey` | The trigger/action key you want to invoke. If ACTIONTYPE is "auth", this can be "label", "start", or "test". **Flags** * `-i, --inputData` | The input data to pass to the action. Must be a JSON-encoded object. The data can be passed from the command directly like '{"key": "value"}', read from a file like @file.json, or read from stdin like @-. -* `--isLoadingSample` | Set bundle.meta.isLoadingSample to true. When true in production, this run is initiated by the user in the Zap editor trying to pull a sample. * `--isFillingDynamicDropdown` | Set bundle.meta.isFillingDynamicDropdown to true. Only makes sense for a polling trigger. When true in production, this poll is being used to populate a dynamic dropdown. +* `--isLoadingSample` | Set bundle.meta.isLoadingSample to true. When true in production, this run is initiated by the user in the Zap editor trying to pull a sample. * `--isPopulatingDedupe` | Set bundle.meta.isPopulatingDedupe to true. Only makes sense for a polling trigger. When true in production, the results of this poll will be used initialize the deduplication list rather than trigger a Zap. This happens when a user enables a Zap. * `--limit` | Set bundle.meta.limit. Only makes sense for a trigger. When used in production, this indicates the number of items you should fetch. -1 means no limit. Defaults to `-1`. * `-p, --page` | Set bundle.meta.page. Only makes sense for a trigger. When used in production, this indicates which page of items you should fetch. First page is 0. -* `-z, --timezone` | Set the default timezone for datetime fields. If not set, defaults to America/Chicago, which matches Zapier production behavior. Find the list timezone names at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. Defaults to `America/Chicago`. +* `--non-interactive` | Do not show interactive prompts. +* `-z, --timezone` | Set the default timezone for datetime field interpretation. If not set, defaults to America/Chicago, which matches Zapier production behavior. Find the list timezone names at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. Defaults to `America/Chicago`. +* `--redirect-uri` | The redirect URI that will be passed to the OAuth2 authorization URL. Usually this should match the one configured in your server's OAuth2 application settings. A local HTTP server will be started to listen for the OAuth2 callback. If your server requires a non-localhost or HTTPS address for the redirect URI, you can set up port forwarding to route the non-localhost or HTTPS address to localhost. Defaults to `http://localhost:9000`. * `-d, --debug` | Show extra debugging output. **Examples** * `zapier invoke` +* `zapier invoke auth start` * `zapier invoke auth test` +* `zapier invoke auth label` * `zapier invoke trigger new_recipe` * `zapier invoke create add_recipe --inputData '{"title": "Pancakes"}'` * `zapier invoke search find_recipe -i @file.json` diff --git a/docs/index.html b/docs/index.html index 7db97057d..fcc89f9c1 100644 --- a/docs/index.html +++ b/docs/index.html @@ -366,6 +366,7 @@
  • Testing
  • Testing
-

You can write unit tests for your Zapier integration that run locally, outside of the Zapier editor. -You can run these tests in a CI tool like Travis.

+

There are several ways to test your Zapier integration:

    +
  • You can use the zapier invoke command to invoke a trigger, search, create, or an auth operation locally.
  • +
  • You can write unit tests for your Zapier integration that run locally, outside of the Zapier editor.
  • +
  • You can run these tests in a CI tool like Travis.
  • +
+
+
+ +
+
+
+
+
+

Using zapier invoke Command

+
+
+

Added in v15.17.0.

The zapier invoke <ACTION_TYPE> <ACTION_KEY> CLI command emulates how the Zapier production environment would invoke your app. Since it runs code locally, it's a fast way to debug and test interactively without needing to deploy the code to Zapier.

Its general execution flow invovles calling operation.inputFields of an action, resolving the input data to the expected types, and then calling the operation.perform method.

zapier invoke --help should be self-explanatory, but here's a quick rundown:

+
+
+
# Intialize auth data in .env file
+zapier invoke auth start
+
+# Test your auth data in .env
+zapier invoke auth test
+zapier invoke auth label
+
+# Invoke a polling trigger
+zapier invoke trigger new_recipe
+
+# Invoke a create action
+zapier invoke create add_recipe --inputData '{"name": "Pancakes"}'
+zapier invoke create add_recipe --inputData @file.json
+
+
+
+

Writing Unit Tests

diff --git a/packages/cli/README-source.md b/packages/cli/README-source.md index 1c0bb64d0..f79057510 100644 --- a/packages/cli/README-source.md +++ b/packages/cli/README-source.md @@ -1787,8 +1787,37 @@ For throttled requests that occur during processing of a webhook trigger's perfo ## Testing -You can write unit tests for your Zapier integration that run locally, outside of the Zapier editor. -You can run these tests in a CI tool like [Travis](https://travis-ci.com/). +There are several ways to test your Zapier integration: + +* You can use the `zapier invoke` command to invoke a trigger, search, create, or an auth operation locally. +* You can write unit tests for your Zapier integration that run locally, outside of the Zapier editor. +* You can run these tests in a CI tool like [Travis](https://travis-ci.com/). + +### Using `zapier invoke` Command + +*Added in v15.17.0.* + +The `zapier invoke ` CLI command emulates how the Zapier production environment would invoke your app. Since it runs code locally, it's a fast way to debug and test interactively without needing to deploy the code to Zapier. + +Its general execution flow invovles calling `operation.inputFields` of an action, resolving the input data to the expected types, and then calling the `operation.perform` method. + +`zapier invoke --help` should be self-explanatory, but here's a quick rundown: + +```bash +# Intialize auth data in .env file +zapier invoke auth start + +# Test your auth data in .env +zapier invoke auth test +zapier invoke auth label + +# Invoke a polling trigger +zapier invoke trigger new_recipe + +# Invoke a create action +zapier invoke create add_recipe --inputData '{"name": "Pancakes"}' +zapier invoke create add_recipe --inputData @file.json +``` ### Writing Unit Tests diff --git a/packages/cli/README.md b/packages/cli/README.md index cd5a00713..2103e299f 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -128,6 +128,7 @@ This doc describes the latest CLI version (**15.16.0**), as of this writing. If * [Stale Authentication Credentials](#stale-authentication-credentials) * [Handling Throttled Requests](#handling-throttled-requests) - [Testing](#testing) + * [Using `zapier invoke` Command](#using-zapier-invoke-command) * [Writing Unit Tests](#writing-unit-tests) * [Using the `z` Object in Tests](#using-the-z-object-in-tests) * [Mocking Requests](#mocking-requests) @@ -3167,8 +3168,37 @@ For throttled requests that occur during processing of a webhook trigger's perfo ## Testing -You can write unit tests for your Zapier integration that run locally, outside of the Zapier editor. -You can run these tests in a CI tool like [Travis](https://travis-ci.com/). +There are several ways to test your Zapier integration: + +* You can use the `zapier invoke` command to invoke a trigger, search, create, or an auth operation locally. +* You can write unit tests for your Zapier integration that run locally, outside of the Zapier editor. +* You can run these tests in a CI tool like [Travis](https://travis-ci.com/). + +### Using `zapier invoke` Command + +*Added in v15.17.0.* + +The `zapier invoke ` CLI command emulates how the Zapier production environment would invoke your app. Since it runs code locally, it's a fast way to debug and test interactively without needing to deploy the code to Zapier. + +Its general execution flow invovles calling `operation.inputFields` of an action, resolving the input data to the expected types, and then calling the `operation.perform` method. + +`zapier invoke --help` should be self-explanatory, but here's a quick rundown: + +```bash +# Intialize auth data in .env file +zapier invoke auth start + +# Test your auth data in .env +zapier invoke auth test +zapier invoke auth label + +# Invoke a polling trigger +zapier invoke trigger new_recipe + +# Invoke a create action +zapier invoke create add_recipe --inputData '{"name": "Pancakes"}' +zapier invoke create add_recipe --inputData @file.json +``` ### Writing Unit Tests diff --git a/packages/cli/docs/cli.html b/packages/cli/docs/cli.html index f8105cb12..aee092834 100644 --- a/packages/cli/docs/cli.html +++ b/packages/cli/docs/cli.html @@ -754,13 +754,24 @@

invoke

Invoke an auth operation, a trigger, or a create/search action locally.

-

Usage: zapier invoke [ACTIONTYPE] [ACTIONKEY]

This command emulates how Zapier production environment would invoke your integration. It runs code locally, so you can use this command to quickly test your integration without deploying it to Zapier. This is especially useful for debugging and development.

This command loads authData from the .env file in the current directory. Create a .env file with the necessary auth data before running this command. Each line in .env should be in the format authData_FIELD_KEY=VALUE. For example, an OAuth2 integration might have a .env file like this:

+

Usage: zapier invoke [ACTIONTYPE] [ACTIONKEY]

This command emulates how Zapier production environment would invoke your integration. It runs code locally, so you can use this command to quickly test your integration without deploying it to Zapier. This is especially useful for debugging and development.

This command loads environment variables and authData from the .env file in the current directory. If you don't have an .env file yet, you can use the zapier invoke auth start command to help you initialize it, or you can manually create it.

The zapier invoke auth start subcommand will prompt you for the necessary auth fields and save them to the .env file.

Each line in the .env file should follow one of these formats:

    +
  • VAR_NAME=VALUE for environment variables

    +
  • +
  • authData_FIELD_KEY=VALUE for auth data fields

    +
  • +

For example, a .env file for an OAuth2 integration might look like this:


-authData_access_token=1234567890
+CLIENT_ID='your_client_id'
+
+CLIENT_SECRET='your_client_secret'
 
-authData_other_auth_field=abcdef
+authData_access_token='1234567890'
+
+authData_refresh_token='abcdefg'
+
+authData_account_name='zapier'
 
 
@@ -782,7 +793,7 @@

invoke

-

Then you can test an trigger, a search, or a create action. For example, this is how you invoke a trigger with key new_recipe:

+

Once you have the correct auth data, you can test an trigger, a search, or a create action. For example, here's how you invoke a trigger with the key new_recipe:


@@ -794,9 +805,7 @@ 

invoke

-

To add input data, use the --inputData flag. The input data can come from the command directly, a file, or stdin. See EXAMPLES below.

The following are current limitations and may be supported in the future:

    -
  • zapier invoke auth start to help you initialize the auth data in .env

    -
  • +

    To add input data, use the --inputData flag. The input data can come from the command directly, a file, or stdin. See EXAMPLES below.

    When you miss any command arguments, such as ACTIONTYPE or ACTIONKEY, the command will prompt you interactively. If you don't want to get interactive prompts, use the --non-interactive flag.

    The --debug flag will show you the HTTP request logs and any console logs you have in your code.

    The following is a non-exhaustive list of current limitations and may be supported in the future:

    • zapier invoke auth refresh to refresh the auth data in .env

    • Hook triggers, including REST hook subscribe/unsubscribe

      @@ -813,21 +822,33 @@

      invoke

    • Buffered create actions

    • +
    • Search-or-create actions

      +
    • +
    • Search-powered fields

      +
    • +
    • Field choices

      +
    • +
    • autoRefresh for OAuth2 and session auth

      +

    Arguments

    • actionType | The action type you want to invoke.
    • -
    • actionKey | The trigger/action key you want to invoke. If ACTIONTYPE is "auth", this can be "test" or "label".
    • +
    • actionKey | The trigger/action key you want to invoke. If ACTIONTYPE is "auth", this can be "label", "start", or "test".

    Flags

    • -i, --inputData | The input data to pass to the action. Must be a JSON-encoded object. The data can be passed from the command directly like '{"key": "value"}', read from a file like @file.json, or read from stdin like @-.
    • -
    • --isLoadingSample | Set bundle.meta.isLoadingSample to true. When true in production, this run is initiated by the user in the Zap editor trying to pull a sample.
    • --isFillingDynamicDropdown | Set bundle.meta.isFillingDynamicDropdown to true. Only makes sense for a polling trigger. When true in production, this poll is being used to populate a dynamic dropdown.
    • +
    • --isLoadingSample | Set bundle.meta.isLoadingSample to true. When true in production, this run is initiated by the user in the Zap editor trying to pull a sample.
    • --isPopulatingDedupe | Set bundle.meta.isPopulatingDedupe to true. Only makes sense for a polling trigger. When true in production, the results of this poll will be used initialize the deduplication list rather than trigger a Zap. This happens when a user enables a Zap.
    • --limit | Set bundle.meta.limit. Only makes sense for a trigger. When used in production, this indicates the number of items you should fetch. -1 means no limit. Defaults to -1.
    • -p, --page | Set bundle.meta.page. Only makes sense for a trigger. When used in production, this indicates which page of items you should fetch. First page is 0.
    • -
    • -z, --timezone | Set the default timezone for datetime fields. If not set, defaults to America/Chicago, which matches Zapier production behavior. Find the list timezone names at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. Defaults to America/Chicago.
    • +
    • --non-interactive | Do not show interactive prompts.
    • +
    • -z, --timezone | Set the default timezone for datetime field interpretation. If not set, defaults to America/Chicago, which matches Zapier production behavior. Find the list timezone names at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. Defaults to America/Chicago.
    • +
    • --redirect-uri | The redirect URI that will be passed to the OAuth2 authorization URL. Usually this should match the one configured in your server's OAuth2 application settings. A local HTTP server will be started to listen for the OAuth2 callback. If your server requires a non-localhost or HTTPS address for the redirect URI, you can set up port forwarding to route the non-localhost or HTTPS address to localhost. Defaults to http://localhost:9000.
    • -d, --debug | Show extra debugging output.

    Examples

    • zapier invoke
    • +
    • zapier invoke auth start
    • zapier invoke auth test
    • +
    • zapier invoke auth label
    • zapier invoke trigger new_recipe
    • zapier invoke create add_recipe --inputData '{"title": "Pancakes"}'
    • zapier invoke search find_recipe -i @file.json
    • diff --git a/packages/cli/docs/cli.md b/packages/cli/docs/cli.md index f72986a17..290718edf 100644 --- a/packages/cli/docs/cli.md +++ b/packages/cli/docs/cli.md @@ -318,13 +318,29 @@ This command also checks the current directory for a linked integration. This command emulates how Zapier production environment would invoke your integration. It runs code locally, so you can use this command to quickly test your integration without deploying it to Zapier. This is especially useful for debugging and development. -This command loads `authData` from the `.env` file in the current directory. Create a `.env` file with the necessary auth data before running this command. Each line in `.env` should be in the format `authData_FIELD_KEY=VALUE`. For example, an OAuth2 integration might have a `.env` file like this: +This command loads environment variables and `authData` from the `.env` file in the current directory. If you don't have an `.env` file yet, you can use the `zapier invoke auth start` command to help you initialize it, or you can manually create it. + +The `zapier invoke auth start` subcommand will prompt you for the necessary auth fields and save them to the `.env` file. + +Each line in the `.env` file should follow one of these formats: + +* `VAR_NAME=VALUE` for environment variables + +* `authData_FIELD_KEY=VALUE` for auth data fields + +For example, a `.env` file for an OAuth2 integration might look like this: ``` -authData_access_token=1234567890 +CLIENT_ID='your_client_id' + +CLIENT_SECRET='your_client_secret' + +authData_access_token='1234567890' + +authData_refresh_token='abcdefg' -authData_other_auth_field=abcdef +authData_account_name='zapier' ``` @@ -338,7 +354,7 @@ zapier invoke auth label # invokes authentication.test and renders connection l ``` -Then you can test an trigger, a search, or a create action. For example, this is how you invoke a trigger with key `new_recipe`: +Once you have the correct auth data, you can test an trigger, a search, or a create action. For example, here's how you invoke a trigger with the key `new_recipe`: ``` @@ -348,9 +364,11 @@ zapier invoke trigger new_recipe To add input data, use the `--inputData` flag. The input data can come from the command directly, a file, or stdin. See **EXAMPLES** below. -The following are current limitations and may be supported in the future: +When you miss any command arguments, such as ACTIONTYPE or ACTIONKEY, the command will prompt you interactively. If you don't want to get interactive prompts, use the `--non-interactive` flag. -- `zapier invoke auth start` to help you initialize the auth data in `.env` +The `--debug` flag will show you the HTTP request logs and any console logs you have in your code. + +The following is a non-exhaustive list of current limitations and may be supported in the future: - `zapier invoke auth refresh` to refresh the auth data in `.env` @@ -368,23 +386,35 @@ The following are current limitations and may be supported in the future: - Buffered create actions +- Search-or-create actions + +- Search-powered fields + +- Field choices + +- autoRefresh for OAuth2 and session auth + **Arguments** * `actionType` | The action type you want to invoke. -* `actionKey` | The trigger/action key you want to invoke. If ACTIONTYPE is "auth", this can be "test" or "label". +* `actionKey` | The trigger/action key you want to invoke. If ACTIONTYPE is "auth", this can be "label", "start", or "test". **Flags** * `-i, --inputData` | The input data to pass to the action. Must be a JSON-encoded object. The data can be passed from the command directly like '{"key": "value"}', read from a file like @file.json, or read from stdin like @-. -* `--isLoadingSample` | Set bundle.meta.isLoadingSample to true. When true in production, this run is initiated by the user in the Zap editor trying to pull a sample. * `--isFillingDynamicDropdown` | Set bundle.meta.isFillingDynamicDropdown to true. Only makes sense for a polling trigger. When true in production, this poll is being used to populate a dynamic dropdown. +* `--isLoadingSample` | Set bundle.meta.isLoadingSample to true. When true in production, this run is initiated by the user in the Zap editor trying to pull a sample. * `--isPopulatingDedupe` | Set bundle.meta.isPopulatingDedupe to true. Only makes sense for a polling trigger. When true in production, the results of this poll will be used initialize the deduplication list rather than trigger a Zap. This happens when a user enables a Zap. * `--limit` | Set bundle.meta.limit. Only makes sense for a trigger. When used in production, this indicates the number of items you should fetch. -1 means no limit. Defaults to `-1`. * `-p, --page` | Set bundle.meta.page. Only makes sense for a trigger. When used in production, this indicates which page of items you should fetch. First page is 0. -* `-z, --timezone` | Set the default timezone for datetime fields. If not set, defaults to America/Chicago, which matches Zapier production behavior. Find the list timezone names at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. Defaults to `America/Chicago`. +* `--non-interactive` | Do not show interactive prompts. +* `-z, --timezone` | Set the default timezone for datetime field interpretation. If not set, defaults to America/Chicago, which matches Zapier production behavior. Find the list timezone names at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. Defaults to `America/Chicago`. +* `--redirect-uri` | The redirect URI that will be passed to the OAuth2 authorization URL. Usually this should match the one configured in your server's OAuth2 application settings. A local HTTP server will be started to listen for the OAuth2 callback. If your server requires a non-localhost or HTTPS address for the redirect URI, you can set up port forwarding to route the non-localhost or HTTPS address to localhost. Defaults to `http://localhost:9000`. * `-d, --debug` | Show extra debugging output. **Examples** * `zapier invoke` +* `zapier invoke auth start` * `zapier invoke auth test` +* `zapier invoke auth label` * `zapier invoke trigger new_recipe` * `zapier invoke create add_recipe --inputData '{"title": "Pancakes"}'` * `zapier invoke search find_recipe -i @file.json` diff --git a/packages/cli/docs/index.html b/packages/cli/docs/index.html index 7db97057d..fcc89f9c1 100644 --- a/packages/cli/docs/index.html +++ b/packages/cli/docs/index.html @@ -366,6 +366,7 @@
  • Testing
  • Testing
-

You can write unit tests for your Zapier integration that run locally, outside of the Zapier editor. -You can run these tests in a CI tool like Travis.

+

There are several ways to test your Zapier integration:

    +
  • You can use the zapier invoke command to invoke a trigger, search, create, or an auth operation locally.
  • +
  • You can write unit tests for your Zapier integration that run locally, outside of the Zapier editor.
  • +
  • You can run these tests in a CI tool like Travis.
  • +
+
+
+ +
+
+
+
+
+

Using zapier invoke Command

+
+
+

Added in v15.17.0.

The zapier invoke <ACTION_TYPE> <ACTION_KEY> CLI command emulates how the Zapier production environment would invoke your app. Since it runs code locally, it's a fast way to debug and test interactively without needing to deploy the code to Zapier.

Its general execution flow invovles calling operation.inputFields of an action, resolving the input data to the expected types, and then calling the operation.perform method.

zapier invoke --help should be self-explanatory, but here's a quick rundown:

+
+
+
# Intialize auth data in .env file
+zapier invoke auth start
+
+# Test your auth data in .env
+zapier invoke auth test
+zapier invoke auth label
+
+# Invoke a polling trigger
+zapier invoke trigger new_recipe
+
+# Invoke a create action
+zapier invoke create add_recipe --inputData '{"name": "Pancakes"}'
+zapier invoke create add_recipe --inputData @file.json
+
+
+
+

Writing Unit Tests

diff --git a/packages/cli/package.json b/packages/cli/package.json index 7560fe99f..d32f62bc7 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -65,6 +65,7 @@ "marked-terminal": "5.2.0", "minimatch": "9.0.3", "node-fetch": "2.6.7", + "open": "10.1.0", "ora": "5.4.0", "parse-gitignore": "0.5.1", "prettier": "2.8.8", diff --git a/packages/cli/src/oclif/commands/invoke.js b/packages/cli/src/oclif/commands/invoke.js index 37e8f1e9c..483efa18a 100644 --- a/packages/cli/src/oclif/commands/invoke.js +++ b/packages/cli/src/oclif/commands/invoke.js @@ -1,4 +1,6 @@ -const fs = require('node:fs'); +const crypto = require('node:crypto'); +const fs = require('node:fs/promises'); +const http = require('node:http'); const _ = require('lodash'); const { flags } = require('@oclif/command'); @@ -11,7 +13,7 @@ const { DateTime, IANAZone } = require('luxon'); const BaseCommand = require('../ZapierBaseCommand'); const { buildFlags } = require('../buildFlags'); -const { localAppCommand } = require('../../utils/local'); +const { localAppCommand, getLocalAppHandler } = require('../../utils/local'); const { startSpinner, endSpinner } = require('../../utils/display'); const ACTION_TYPE_PLURALS = { @@ -224,6 +226,13 @@ const resolveInputDataTypes = (inputData, inputFields, timezone) => { return inputData; }; +const appendEnv = async (vars, prefix = '') => { + await fs.appendFile( + '.env', + Object.entries(vars).map(([k, v]) => `${prefix}${k}='${v}'\n`) + ); +}; + const testAuth = async (authData, meta, zcacheTestObj) => { startSpinner('Invoking authentication.test'); const result = await localAppCommand({ @@ -231,7 +240,10 @@ const testAuth = async (authData, meta, zcacheTestObj) => { method: 'authentication.test', bundle: { authData, - meta, + meta: { + ...meta, + isTestingAuth: true, + }, }, zcacheTestObj, customLogger, @@ -300,7 +312,263 @@ const customLogger = (message, data) => { debug(data); }; +const formatFieldDisplay = (field) => { + const ftype = field.type || 'string'; + let result; + if (field.label) { + result = `${field.label} | ${field.key} | ${ftype}`; + } else { + result = `${field.key} | ${ftype}`; + } + if (field.required) { + result += ' | required'; + } + return result; +}; + class InvokeCommand extends BaseCommand { + async promptForAuthFields(authFields) { + const authData = {}; + for (const field of authFields) { + if (field.computed) { + continue; + } + const message = formatFieldDisplay(field) + ':'; + let value; + if (field.type === 'password') { + value = await this.promptHidden(message, true); + } else { + value = await this.prompt(message, { useStderr: true }); + } + authData[field.key] = value; + } + return authData; + } + + async startBasicAuth(authFields) { + if (this.nonInteractive) { + throw new Error( + 'The `auth start` subcommand for "basic" authentication type only works in interactive mode.' + ); + } + return this.promptForAuthFields([ + { + key: 'username', + label: 'Username', + required: true, + }, + { + key: 'password', + label: 'Password', + required: true, + }, + ]); + } + + async startCustomAuth(authFields) { + if (this.nonInteractive) { + throw new Error( + 'The `auth start` subcommand for "custom" authentication type only works in interactive mode.' + ); + } + return this.promptForAuthFields(authFields); + } + + async startOAuth2(appDefinition) { + const redirectUri = this.flags['redirect-uri']; + let port; + try { + port = parseInt(new URL(redirectUri).port); + } catch (err) { + throw new Error( + `Invalid redirect URI '${redirectUri}'. ` + + "A valid example would be 'http://localhost:8000'." + ); + } + port = parseInt(port); + if (!port) { + throw new Error( + `Could not parse port from redirect URI: '${redirectUri}'. ` + + "A valid example would be 'http://localhost:8000'." + ); + } + + const env = {}; + + if (!process.env.CLIENT_ID || !process.env.CLIENT_SECRET) { + if (this.nonInteractive) { + throw new Error( + 'CLIENT_ID and CLIENT_SECRET must be set in the .env file in non-interactive mode.' + ); + } else { + console.warn( + 'CLIENT_ID and CLIENT_SECRET are required for OAuth2, ' + + "but they are not found in the .env file. I'll prompt you for them now." + ); + } + } + + if (!process.env.CLIENT_ID) { + env.CLIENT_ID = await this.prompt('CLIENT_ID:', { useStderr: true }); + process.env.CLIENT_ID = env.CLIENT_ID; + } + if (!process.env.CLIENT_SECRET) { + env.CLIENT_SECRET = await this.prompt('CLIENT_SECRET:', { + useStderr: true, + }); + process.env.CLIENT_SECRET = env.CLIENT_SECRET; + } + + if (!_.isEmpty(env)) { + // process.env changed, so we need to reload the modules that have loaded + // the old values of process.env + getLocalAppHandler({ reload: true }); + + // Save envs so the user won't have to re-enter them if the command fails + await appendEnv(env); + console.warn('CLIENT_ID and CLIENT_SECRET saved to .env file.'); + } + + startSpinner('Invoking authentication.oauth2Config.authorizeUrl'); + + const stateParam = crypto.randomBytes(20).toString('hex'); + let authorizeUrl = await localAppCommand({ + command: 'execute', + method: 'authentication.oauth2Config.authorizeUrl', + bundle: { + inputData: { + response_type: 'code', + redirect_uri: redirectUri, + state: stateParam, + }, + }, + }); + if (!authorizeUrl.includes('&scope=')) { + const scope = appDefinition.authentication.oauth2Config.scope; + if (scope) { + authorizeUrl += `&scope=${encodeURIComponent(scope)}`; + } + } + debug('authorizeUrl:', authorizeUrl); + + endSpinner(); + startSpinner('Starting local HTTP server'); + + let resolveCode; + const codePromise = new Promise((resolve) => { + resolveCode = resolve; + }); + + const server = http.createServer((req, res) => { + // Parse the request URL to extract the query parameters + const code = new URL(req.url, redirectUri).searchParams.get('code'); + if (code) { + resolveCode(code); + debug(`Received code '${code}' from ${req.headers.referer}`); + + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end( + 'Parameter `code` received successfully. Go back to the terminal to continue.' + ); + } else { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + res.end( + 'Error: Did not receive `code` query parameter. ' + + 'Did you have the right CLIENT_ID and CLIENT_SECRET? ' + + 'Or did your server respond properly?' + ); + } + }); + + await new Promise((resolve) => { + server.listen(port, resolve); + }); + + endSpinner(); + startSpinner( + 'Opening browser to authorize (press Ctrl-C to exit on error)' + ); + + const { default: open } = await import('open'); + open(authorizeUrl); + + const code = await codePromise; + endSpinner(); + + startSpinner('Closing local HTTP server'); + await new Promise((resolve) => { + server.close(resolve); + }); + debug('Local HTTP server closed'); + + endSpinner(); + startSpinner('Invoking authentication.oauth2Config.getAccessToken'); + + const authData = await localAppCommand({ + command: 'execute', + method: 'authentication.oauth2Config.getAccessToken', + bundle: { + inputData: { + code, + redirect_uri: redirectUri, + }, + }, + }); + + endSpinner(); + return authData; + } + + async startSessionAuth(appDefinition) { + if (this.nonInteractive) { + throw new Error( + 'The `auth start` subcommand for "session" authentication type only works in interactive mode.' + ); + } + const authData = await this.promptForAuthFields( + appDefinition.authentication.fields + ); + + startSpinner('Invoking authentication.sessionConfig.perform'); + const sessionData = await localAppCommand({ + command: 'execute', + method: 'authentication.sessionConfig.perform', + bundle: { + authData, + }, + }); + endSpinner(); + + return { ...authData, ...sessionData }; + } + + async startAuth(appDefinition) { + const authentication = appDefinition.authentication; + if (!authentication) { + console.warn( + "Your integration doesn't seem to need authentication. " + + "If that isn't true, the app definition should have " + + 'an `authentication` object at the root level.' + ); + return null; + } + switch (authentication.type) { + case 'basic': + return this.startBasicAuth(authentication.fields); + case 'custom': + return this.startCustomAuth(authentication.fields); + case 'oauth2': + return this.startOAuth2(appDefinition); + case 'session': + return this.startSessionAuth(appDefinition); + default: + // TODO: Add support for 'digest' and 'oauth1' + throw new Error( + `This command doesn't support authentication type "${authentication.type}".` + ); + } + } + async promptForField( field, appDefinition, @@ -310,19 +578,7 @@ class InvokeCommand extends BaseCommand { zcacheTestObj, cursorTestObj ) { - const ftype = field.type || 'string'; - - let message; - if (field.label) { - message = `${field.label} | ${field.key} | ${ftype}`; - } else { - message = `${field.key} | ${ftype}`; - } - if (field.required) { - message += ' | required'; - } - message += ':'; - + const message = formatFieldDisplay(field) + ':'; if (field.dynamic) { // Dyanmic dropdown const [triggerKey, idField, labelField] = field.dynamic.split('.'); @@ -358,7 +614,7 @@ class InvokeCommand extends BaseCommand { }), { useStderr: true } ); - } else if (ftype === 'boolean') { + } else if (field.type === 'boolean') { const yes = await this.confirm(message, false, !field.required, true); return yes ? 'yes' : 'no'; } else { @@ -378,10 +634,10 @@ class InvokeCommand extends BaseCommand { ) { const missingFields = getMissingRequiredInputFields(inputData, inputFields); if (missingFields.length) { - if (!process.stdin.isTTY || meta.isFillingDynamicDropdown) { + if (this.nonInteractive || meta.isFillingDynamicDropdown) { throw new Error( - 'You must specify these required fields in --inputData at least when stdin is not a TTY: \n' + - missingFields.map((f) => `* ${f.key}`).join('\n') + "You're in non-interactive mode, so you must at least specify these required fields with --inputData: \n" + + missingFields.map((f) => '* ' + formatFieldDisplay(f)).join('\n') ); } for (const f of missingFields) { @@ -481,7 +737,7 @@ class InvokeCommand extends BaseCommand { zcacheTestObj, cursorTestObj ); - if (process.stdin.isTTY && !meta.isFillingDynamicDropdown) { + if (!this.nonInteractive && !meta.isFillingDynamicDropdown) { await this.promptForInputFieldEdit( inputData, inputFields, @@ -581,14 +837,22 @@ class InvokeCommand extends BaseCommand { } async perform() { - dotenv.config({ override: true }); + const dotenvResult = dotenv.config({ override: true }); + if (_.isEmpty(dotenvResult.parsed)) { + console.warn( + 'The .env file does not exist or is empty. ' + + 'You may need to set some environment variables in there if your code uses process.env.' + ); + } + + this.nonInteractive = this.flags['non-interactive'] || !process.stdin.isTTY; let { actionType, actionKey } = this.args; if (!actionType) { - if (!process.stdin.isTTY) { + if (this.nonInteractive) { throw new Error( - 'You must specify ACTIONTYPE and ACTIONKEY when stdin is not a TTY.' + 'You must specify ACTIONTYPE and ACTIONKEY in non-interactive mode.' ); } actionType = await this.promptWithList( @@ -602,11 +866,11 @@ class InvokeCommand extends BaseCommand { const appDefinition = await localAppCommand({ command: 'definition' }); if (!actionKey) { - if (!process.stdin.isTTY) { - throw new Error('You must specify ACTIONKEY when stdin is not a TTY.'); + if (this.nonInteractive) { + throw new Error('You must specify ACTIONKEY in non-interactive mode.'); } if (actionType === 'auth') { - const actionKeys = ['label', 'test']; + const actionKeys = ['label', 'start', 'test']; actionKey = await this.promptWithList( 'Which auth operation would you like to invoke?', actionKeys, @@ -644,7 +908,18 @@ class InvokeCommand extends BaseCommand { isTestingAuth: true, }; switch (actionKey) { - // TODO: Add 'start' and 'refresh' commands + // TODO: Add 'refresh' command + case 'start': { + const newAuthData = await this.startAuth(appDefinition); + if (_.isEmpty(newAuthData)) { + return; + } + await appendEnv(newAuthData, AUTH_FIELD_ENV_PREFIX); + console.warn( + 'Auth data appended to .env file. Run `zapier invoke auth test` to test it.' + ); + return; + } case 'test': { const output = await testAuth(authData, meta, zcacheTestObj); console.log(JSON.stringify(output, null, 2)); @@ -654,7 +929,7 @@ class InvokeCommand extends BaseCommand { const labelTemplate = appDefinition.authentication.connectionLabel; if (labelTemplate && labelTemplate.startsWith('$func$')) { console.warn( - 'Function-based connection label is currently not supported; will print auth test result instead.' + 'Function-based connection label is not supported yet. Printing auth test result instead.' ); const output = await testAuth(authData, meta, zcacheTestObj); console.log(JSON.stringify(output, null, 2)); @@ -674,7 +949,10 @@ class InvokeCommand extends BaseCommand { return; } default: - throw new Error(`Unknown auth action: ${actionKey}`); + throw new Error( + `Unknown auth operation "${actionKey}". ` + + 'The options are "label", "start", and "test". \n' + ); } } else { const action = appDefinition[actionTypePlural][actionKey]; @@ -746,14 +1024,14 @@ InvokeCommand.flags = buildFlags({ description: 'The input data to pass to the action. Must be a JSON-encoded object. The data can be passed from the command directly like \'{"key": "value"}\', read from a file like @file.json, or read from stdin like @-.', }), - isLoadingSample: flags.boolean({ + isFillingDynamicDropdown: flags.boolean({ description: - 'Set bundle.meta.isLoadingSample to true. When true in production, this run is initiated by the user in the Zap editor trying to pull a sample.', + 'Set bundle.meta.isFillingDynamicDropdown to true. Only makes sense for a polling trigger. When true in production, this poll is being used to populate a dynamic dropdown.', default: false, }), - isFillingDynamicDropdown: flags.boolean({ + isLoadingSample: flags.boolean({ description: - 'Set bundle.meta.isFillingDynamicDropdown to true. Only makes sense for a polling trigger. When true in production, this poll is being used to populate a dynamic dropdown.', + 'Set bundle.meta.isLoadingSample to true. When true in production, this run is initiated by the user in the Zap editor trying to pull a sample.', default: false, }), isPopulatingDedupe: flags.boolean({ @@ -772,12 +1050,21 @@ InvokeCommand.flags = buildFlags({ 'Set bundle.meta.page. Only makes sense for a trigger. When used in production, this indicates which page of items you should fetch. First page is 0.', default: 0, }), + 'non-interactive': flags.boolean({ + description: 'Do not show interactive prompts.', + default: false, + }), timezone: flags.string({ char: 'z', description: - 'Set the default timezone for datetime fields. If not set, defaults to America/Chicago, which matches Zapier production behavior. Find the list timezone names at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.', + 'Set the default timezone for datetime field interpretation. If not set, defaults to America/Chicago, which matches Zapier production behavior. Find the list timezone names at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.', default: 'America/Chicago', }), + 'redirect-uri': flags.string({ + description: + "The redirect URI that will be passed to the OAuth2 authorization URL. Usually this should match the one configured in your server's OAuth2 application settings. A local HTTP server will be started to listen for the OAuth2 callback. If your server requires a non-localhost or HTTPS address for the redirect URI, you can set up port forwarding to route the non-localhost or HTTPS address to localhost.", + default: 'http://localhost:9000', + }), }, }); @@ -790,14 +1077,15 @@ InvokeCommand.args = [ { name: 'actionKey', description: - 'The trigger/action key you want to invoke. If ACTIONTYPE is "auth", this can be "test" or "label".', + 'The trigger/action key you want to invoke. If ACTIONTYPE is "auth", this can be "label", "start", or "test".', }, ]; -InvokeCommand.skipValidInstallCheck = true; InvokeCommand.examples = [ 'zapier invoke', + 'zapier invoke auth start', 'zapier invoke auth test', + 'zapier invoke auth label', 'zapier invoke trigger new_recipe', `zapier invoke create add_recipe --inputData '{"title": "Pancakes"}'`, 'zapier invoke search find_recipe -i @file.json', @@ -807,11 +1095,23 @@ InvokeCommand.description = `Invoke an auth operation, a trigger, or a create/se This command emulates how Zapier production environment would invoke your integration. It runs code locally, so you can use this command to quickly test your integration without deploying it to Zapier. This is especially useful for debugging and development. -This command loads \`authData\` from the \`.env\` file in the current directory. Create a \`.env\` file with the necessary auth data before running this command. Each line in \`.env\` should be in the format \`authData_FIELD_KEY=VALUE\`. For example, an OAuth2 integration might have a \`.env\` file like this: +This command loads environment variables and \`authData\` from the \`.env\` file in the current directory. If you don't have an \`.env\` file yet, you can use the \`zapier invoke auth start\` command to help you initialize it, or you can manually create it. + +The \`zapier invoke auth start\` subcommand will prompt you for the necessary auth fields and save them to the \`.env\` file. + +Each line in the \`.env\` file should follow one of these formats: + +* \`VAR_NAME=VALUE\` for environment variables +* \`authData_FIELD_KEY=VALUE\` for auth data fields + +For example, a \`.env\` file for an OAuth2 integration might look like this: \`\`\` -authData_access_token=1234567890 -authData_other_auth_field=abcdef +CLIENT_ID='your_client_id' +CLIENT_SECRET='your_client_secret' +authData_access_token='1234567890' +authData_refresh_token='abcdefg' +authData_account_name='zapier' \`\`\` To test if the auth data is correct, run either one of these: @@ -821,7 +1121,7 @@ zapier invoke auth test # invokes authentication.test method zapier invoke auth label # invokes authentication.test and renders connection label \`\`\` -Then you can test an trigger, a search, or a create action. For example, this is how you invoke a trigger with key \`new_recipe\`: +Once you have the correct auth data, you can test an trigger, a search, or a create action. For example, here's how you invoke a trigger with the key \`new_recipe\`: \`\`\` zapier invoke trigger new_recipe @@ -829,9 +1129,12 @@ zapier invoke trigger new_recipe To add input data, use the \`--inputData\` flag. The input data can come from the command directly, a file, or stdin. See **EXAMPLES** below. -The following are current limitations and may be supported in the future: +When you miss any command arguments, such as ACTIONTYPE or ACTIONKEY, the command will prompt you interactively. If you don't want to get interactive prompts, use the \`--non-interactive\` flag. + +The \`--debug\` flag will show you the HTTP request logs and any console logs you have in your code. + +The following is a non-exhaustive list of current limitations and may be supported in the future: -- \`zapier invoke auth start\` to help you initialize the auth data in \`.env\` - \`zapier invoke auth refresh\` to refresh the auth data in \`.env\` - Hook triggers, including REST hook subscribe/unsubscribe - Line items @@ -840,6 +1143,10 @@ The following are current limitations and may be supported in the future: - Dynamic dropdown pagination - Function-based connection label - Buffered create actions +- Search-or-create actions +- Search-powered fields +- Field choices +- autoRefresh for OAuth2 and session auth `; module.exports = InvokeCommand; diff --git a/packages/core/package.json b/packages/core/package.json index e21eb66f4..1dbb65bb1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -61,7 +61,7 @@ "aws-sdk": "^2.1397.0", "dicer": "^0.3.1", "fs-extra": "^11.1.1", - "mock-fs": "^5.2.0", + "mock-fs": "^5.3.0", "nock": "^13.5.4", "tsd": "^0.31.1" }, diff --git a/yarn.lock b/yarn.lock index e6844b756..f9b92d9d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3301,6 +3301,13 @@ builtins@^5.0.0, builtins@^5.0.1: dependencies: semver "^7.0.0" +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + byte-size@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" @@ -4477,6 +4484,19 @@ deep-is@^0.1.3, deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -4494,6 +4514,11 @@ define-lazy-prop@^2.0.0: resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -6797,6 +6822,11 @@ is-docker@^2.1.1: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-extendable@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -6857,6 +6887,13 @@ is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-installed-globally@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" @@ -7137,6 +7174,13 @@ is-wsl@^2.1.1, is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" @@ -8447,6 +8491,11 @@ mock-fs@^5.2.0: resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-5.2.0.tgz#3502a9499c84c0a1218ee4bf92ae5bf2ea9b2b5e" integrity sha512-2dF2R6YMSZbpip1V1WHKGLNjr/k48uQClqMVb5H3MOvwc9qhYis3/IWbj02qIg/Y8MDXKFF4c5v0rxx2o6xTZw== +mock-fs@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-5.3.0.tgz#7dfc95ce5528aff8e10fa117161b91d8129e0e9e" + integrity sha512-IMvz1X+RF7vf+ur7qUenXMR7/FSKSIqS3HqFHXcyNI7G0FbpFO8L5lfsUJhl+bhK1AiulVHWKUSxebWauPA+xQ== + mock-stdin@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mock-stdin/-/mock-stdin-1.0.0.tgz#efcfaf4b18077e14541742fd758b9cae4e5365ea" @@ -9130,6 +9179,16 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" + integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" + open@^8.4.0: version "8.4.0" resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" @@ -10404,6 +10463,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + run-async@^2.0.0, run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"