Generally speaking, tasks only run in response to events – an order being created, a "Run task" button being pressed, etc. However, Mechanic does preview runs of each task as well, for two purposes:

  1. To show merchants what they can expect the task to do
  2. To show Mechanic what permissions the task will need, when it comes time to do real work

For developers

When a task script is rendered in preview mode, Mechanic sets the event.preview variable to true. You can use this value to determine whether or not to render a preview action.

There are two approaches to adding preview actions: adding dummy actions, and working support for preview mode into your existing actions. We suggest the latter, because it involves exercising your entire task script during preview. This is useful for ensuring that your task script is doing exactly what you intend it to do, before it handles any real events.

Adding dummy actions is simple enough:

{% if event.preview %}
  {% action "echo" "This is a dummy action, and will only be rendered during preview mode." %}
{% endif %}

When talking about adding preview actions to an existing task, it's useful to work by example. Say we're working with this task script:

{% if order.email contains "@example.com" %}
  {% action "shopify" %}
    mutation {
      tagsAdd(
        id: {{ order.admin_graphql_api_id | json }}
        tags: "example.com"
      ) {
        userErrors {
          field
          message
        }
      }
    }
  {% endaction %}
{% endif %}

If the task script looks exactly like this, paired with a shopify/orders/create subscription, Mechanic will show a message resembling this:

To resolve this issue, we might be tempted to simply change our if statement like this:

{% if event.preview or order.email contains "@example.com" %}

However, this sets us down the path of making if-else choices in the rest of our script, based on whether or not we're in preview mode. It's an improvement over using a dummy action, but we can do better. :)

Instead, we might add this to the top of the task script:

{% if event.preview %}
  {% assign order = '{"email": "test@example.com", "admin_graphql_api_id": "gid://shopify/Order/1234567890"}' | parse_json %}
{% endif %}

This approach allows us to leave the rest of the task script completely untouched. In preview mode, the rest of the script will now have an object resembling an order, stored in the order variable, allowing it to eventually generate an action that's perfectly suited for preview mode. No further changes are necessary; our task script is now complete.

Note the addition of the "admin_graphql_api_id" property. This is uniquely important for the tagsAdd and tagsRemove mutations, which are not intrinsically connected to any specific Shopify resource. When using these mutations, we need to ensure that our preview events include an appropriate ID for input, so that Mechanic knows what kind of resource this action will be tagging. Skipping this step will likely result in "message": "TagsAdd access denied" errors.

One more note about GraphQL

More advanced tasks may use GraphQL queries, via the "shopify" filter, to pull data from Shopify. In these cases, we can use the approach of building in preview-friendly data.

For example, say we're working on this task, which scans the first 250 open orders (see another example that paginates), and sends an email to each customer:

{% capture query %}
  query {
    orders(
      first: 250
      query: "status:open"
    ) {
      edges {
        node {
          name
          email
        }
      }
    }
  }
{% endcapture %}

{% assign result = query | shopify %}

{% for order_edge in result.data.orders.edges %}
  {% assign order_node = order_edge.node %}

  {% if order_node.email == blank %}
    {% continue %}
  {% endif %}

  {% action "email" %}
    {
      "to": {{ order_node.email | json }},
      "subject": {{ "We're still working on " | append: order_node.name | json }},
      "body": "Thanks for your patience!"
    }
  {% endaction %}
{% endfor %}

To make this task script preview friendly, we might add this line directly below the {% assign result = query | shopify %}:

{% if event.preview %}
  {% capture result_json %}
    {
      "data": {
        "orders": {
          "edges": [
            {
              "node": {
                "name": "#1135",
                "email": "isaac@example.com"
              }
            }
          ]
        }
      }
    }
  {% endcapture %}

  {% assign result = result_json | parse_json %}
{% endif %}

By setting up a new result variable based on preview-friendly data, our task now is able to exercise the rest of its code, rendering a preview action using the same code that will be used by a real event. 🎉

Tip: you can quickly generate JSON samples, like the one above, by using Shopify's hosted GraphiQL.

Did this answer your question?