GraphQL variable support for the "shopify" action

The "shopify" action has a new, optional syntax that supports GraphQL variables. By providing a JSON hash of options, consisting of a "query" key and a "variables" key, you may now separate your inputs from the query itself, allowing both for query re-use and for variables in excess of Shopify's 50k-character limit for query strings.

This syntax is available in block form:

{% capture query %}
  mutation DeleteProduct($productId: ID!) {
      input: {
        id: $productId
    ) {
      userErrors {
{% endcapture %}

{% action "shopify" %}
    "query": {{ query | json }},
    "variables": {
      "productId": {{ product.admin_graphql_id | json }}
{% endaction %}

… and in tag form:

{% assign variables = hash %}
{% assign variables["productId"] = product.admin_graphql_api_id %}

{% action "shopify", query: query, variables: variables %}

This change does not impact existing uses of the "shopify" action.

Read more

New Liquid filter: parse_xml

Use parse_xml to transform an XML string into a nested hash, keyed by XML tag name. Useful for processing output from third-party APIs, either by responding to "http" actions, or by parsing content from inbound webhooks.

Read more

New Liquid filters: in_groups, in_groups_of

Borrowing from Rails, our Liquid implementation now supports in_groups and in_groups_of. These are particularly useful when performing work in batches, splitting (for example) an unknown number of mutations into batches of a specific size.

Read more

Updates to Mechanic's cache feature

  • All cache commands now support a "ttl" option, allowing users to define an expiration period other than the default (and maximum) of 60 days.
  • The "setex" command has been deprecated, in favor of using "set" with a "ttl" option.
  • The default expiration period behavior is now defined for all commands, not just "set" (and formerly "setex") – specifically, the expiration period is reset to 60 days whenever a key is updated, deferring to the "ttl" value if given.
  • Tasks may now iterate through all stored keys and values, using {% for keyval in cache %} to receive a tuple of each key and value pair.
  • Cached values are now limited to a size of 256KB each.

Read more

New Liquid filter: "img_url"

New Liquid filter: "parse_jsonl"

Useful when preparing stub data for bulk operations, the parse_jsonl filter accepts a multiline string in which each line contains a complete JSON object. It returns an array of parsed objects.

{% capture jsonl_string %}
{% endcapture %}

{% assign json_objects = jsonl_string | parse_jsonl %}

{{ json_objects | map: "email" | join: ", " }}
foo@bar.baz, bar@baz.qux

Read more

New "error_on_5xx" option for "http" actions

As a rule, Mechanic's "http" action considers any valid HTTP response to be a success. However, because HTTP responses with a 5xx status code should often be considered an error worthy of a retry, we now support a new "error_on_5xx" option. Set this option to true to have Mechanic mark 5xx responses as action errors, retrying the request for a total of up 5 attempts.

Read more

Automatic retries are now limited

When a run encounters an internal error (e.g. anything that isn't immediately surfaced to the user), Mechanic has historically retried the run (using an exponential backoff) until it either succeeds or reaches a known permanent error state.

As of today, Mechanic will now attempt a run at most 5 times.

Read more

Pending action runs for disabled or deleted tasks will now fail

Previously, a task's status did not affect its outstanding action runs. For example, a task in the middle of tagging 1000 products would continue tagging products, despite the task being disabled or deleted at only 500 products in.

Now, rather than continuing to perform pending action runs for deleted or disabled tasks, outstanding action runs will fail with a descriptive message.

However, action runs that were started before the task was deleted or disabled will be completed, rather than being aborted mid-performance.

Mechanic now detects and prevents Shopify update loops

In some cases, it's possible for an update to a Shopify resource to trigger an update event (e.g. "shopify/products/update"), resulting in a task run that performs a "shopify" action updating that same Shopify resource, resulting in an identical task run and an identical "shopify" action.

This infinite loop can – and should! – be prevented by the task developer, by detecting when a resource should have an update applied, and when it should not.

However, in cases when a loop does emerge, Mechanic will now step in and pause the offending actions.

Read more