Mechanic is a development and ecommerce automation platform for Shopify. :)
When products are created, this task will auto-create smart collections by product type and/or vendor, if such collections don't already exist. Additionally, configuring one or more exact sales channel names will enable publishing of any newly created collections by this task to those sales channels.
Runs Occurs whenever a product is created and Occurs when a user manually triggers the task. Configuration includes create collections by product type, create collections by vendor, names of sales channels to publish collections to, and run daily.
When products are created, this task will auto-create smart collections by product type and/or vendor, if such collections don't already exist. Additionally, configuring one or more exact sales channel names will enable publishing of any newly created collections by this task to those sales channels.
For example:
A new product is added with a vendor of "ACME". If a collection with that exact title does not already exist, then the task will create it with a title of "ACME" and add a rule of "vendor = ACME", which will allow Shopify to auto-populate the collection.
Optionally, choose to run the task daily, and it will query for all of the product types and/or vendors in your shop, and make the same decisions on whether to create new collections and publish them. Running the task manually has the same effect as a daily run.
Mechanic is designed to benefit everybody: merchants, customers, developers, agencies, Shopifolks, everybody.
That’s why we make it easy to configure automation without code, why we make it easy to tweak the underlying code once tasks are installed, and why we publish it all here for everyone to learn from.
(By the way, have you seen our documentation? Have you joined the Slack community?)
shopify/products/create
mechanic/user/trigger
{% if options.names_of_sales_channels_to_publish_collections_to__array != blank %}
mechanic/actions/perform
{% endif %}
{% if options.run_daily__boolean %}
mechanic/scheduler/daily
{% endif %}{% assign create_collections_by_product_type = options.create_collections_by_product_type__boolean %}
{% assign create_collections_by_vendor = options.create_collections_by_vendor__boolean %}
{% assign sales_channel_names = options.names_of_sales_channels_to_publish_collections_to__array %}
{% unless create_collections_by_product_type or create_collections_by_vendor %}
{% error "Choose at least one 'Create collections by' option" %}
{% endunless %}
{% assign product_types = array %}
{% assign vendors = array %}
{% if event.topic == "mechanic/user/trigger" or event.topic contains "mechanic/scheduler/" or event.topic == "shopify/products/create" %}
{% if event.topic == "mechanic/user/trigger" or event.topic contains "mechanic/scheduler/" %}
{% comment %}
-- get all product types and vendors in the shop
{% endcomment %}
{% assign cursor = nil %}
{% for n in (1..10) %}
{% capture query %}
query {
productTypes(
first: 1000
after: {{ cursor | json }}
) {
pageInfo {
hasNextPage
endCursor
}
nodes
}
}
{% endcapture %}
{% assign result = query | shopify %}
{% if event.preview %}
{% capture result_json %}
{
"data": {
"productTypes": {
"nodes": [
"Widget"
]
}
}
}
{% endcapture %}
{% assign result = result_json | parse_json %}
{% endif %}
{% assign product_types = product_types | concat: result.data.productTypes.nodes %}
{% if result.data.productTypes.pageInfo.hasNextPage %}
{% assign cursor = result.data.productTypes.pageInfo.endCursor %}
{% else %}
{% break %}
{% endif %}
{% endfor %}
{% assign cursor = nil %}
{% for n in (1..10) %}
{% capture query %}
query {
productVendors(
first: 1000
after: {{ cursor | json }}
) {
pageInfo {
hasNextPage
endCursor
}
nodes
}
}
{% endcapture %}
{% assign result = query | shopify %}
{% if event.preview %}
{% capture result_json %}
{
"data": {
"productVendors": {
"nodes": [
"ACME"
]
}
}
}
{% endcapture %}
{% assign result = result_json | parse_json %}
{% endif %}
{% assign vendors = vendors | concat: result.data.productVendors.nodes %}
{% if result.data.productVendors.pageInfo.hasNextPage %}
{% assign cursor = result.data.productVendors.pageInfo.endCursor %}
{% else %}
{% break %}
{% endif %}
{% endfor %}
{% elsif event.topic == "shopify/products/create" %}
{% unless product.product_type == blank %}
{% assign product_types[0] = product.product_type %}
{% endunless %}
{% unless product.vendor == blank %}
{% assign vendors[0] = product.vendor %}
{% endunless %}
{% endif %}
{% if event.preview %}
{% assign product_types[0] = "Widget" %}
{% assign vendors[0] = "ACME" %}
{% endif %}
{% if sales_channel_names != blank %}
{% comment %}
-- check if the configured sales channels exist in this shop by name; save the publication IDs for lookup later
{% endcomment %}
{% capture query %}
query {
publications(
first: 250
catalogType:APP
) {
nodes {
id
catalog {
... on AppCatalog {
apps(first: 1) {
nodes {
title
}
}
}
}
}
}
}
{% endcapture %}
{% assign result = query | shopify %}
{% if event.preview %}
{% capture result_json %}
{
"data": {
"publications": {
"nodes": [
{
"id": "gid://shopify/Publication/1234567890",
"catalog": {
"apps": {
"nodes": [
{
"title": {{ sales_channel_names.first | json }}
}
]
}
}
}
]
}
}
}
{% endcapture %}
{% assign result = result_json | parse_json %}
{% endif %}
{% assign publication_ids = array %}
{% assign available_sales_channel_names = array %}
{% for publication in result.data.publications.nodes %}
{% assign publication_name = publication.catalog.apps.nodes.first.title %}
{% assign available_sales_channel_names = available_sales_channel_names | push: publication_name %}
{% if sales_channel_names contains publication_name %}
{% assign publication_ids = publication_ids | push: publication.id %}
{% endif %}
{% endfor %}
{% unless event.preview %}
{% if publication_ids == blank %}
{% error
message: "None of the sales channel configured in this task exist in the shop. Check the list of available channels and verify each configured channel exists.",
configured_sales_channel_names: sales_channel_names,
available_sales_channel_names: available_sales_channel_names
%}
{% break %}
{% elsif publication_ids.size != sales_channel_names.size %}
{% comment %}
-- using action error here so the task will continue with any other configured and matched sales channels
{% endcomment %}
{% action "echo"
__error: "One or more configured sales channel names do not match any of the publication names available in this shop.",
configured_sales_channel_names: sales_channel_names,
available_sales_channel_names: available_sales_channel_names
%}
{% endif %}
{% endunless %}
{% endif %}
{% comment %}
-- get all collection titles in the shop
{% endcomment %}
{% assign cursor = nil %}
{% assign collection_titles = array %}
{% for n in (1..100) %}
{% capture query %}
query {
collections(
first: 250
after: {{ cursor | json }}
query: {{ search_query | json }}
) {
pageInfo {
hasNextPage
endCursor
}
nodes {
title
}
}
}
{% endcapture %}
{% assign result = query | shopify %}
{% if event.preview %}
{% capture result_json %}
{
"data": {
"collections": {
"nodes": [
{
"title": "Gizmo"
}
]
}
}
}
{% endcapture %}
{% assign result = result_json | parse_json %}
{% endif %}
{% assign collection_titles = collection_titles | concat: result.data.collections.nodes | map: "title" | uniq %}
{% if result.data.collections.pageInfo.hasNextPage %}
{% assign cursor = result.data.collections.pageInfo.endCursor %}
{% else %}
{% break %}
{% endif %}
{% endfor %}
{% assign existing_collections = array %}
{% if create_collections_by_product_type %}
{% unless event.topic == "shopify/products/create" %}
{% log product_types_in_shop: product_types %}
{% endunless %}
{% for product_type in product_types %}
{% if collection_titles contains product_type %}
{% assign existing_collections = existing_collections | push: product_type %}
{% continue %}
{% endif %}
{% assign meta = hash %}
{% assign meta["publication_ids"] = publication_ids %}
{% action "shopify", __meta: meta %}
mutation {
collectionCreate(
input: {
title: {{ product_type | json }}
ruleSet: {
appliedDisjunctively: false
rules: [
{
column: TYPE
relation: EQUALS
condition: {{ product_type | json }}
}
]
}
}
) {
collection {
id
title
handle
}
userErrors {
field
message
}
}
}
{% endaction %}
{% endfor %}
{% endif %}
{% if create_collections_by_vendor %}
{% unless event.topic == "shopify/products/create" %}
{% log vendors_in_shop: vendors %}
{% endunless %}
{% for vendor in vendors %}
{% if collection_titles contains vendor %}
{% assign existing_collections = existing_collections | push: vendor %}
{% continue %}
{% endif %}
{% assign meta = hash %}
{% assign meta["publication_ids"] = publication_ids %}
{% action "shopify", __meta: meta %}
mutation {
collectionCreate(
input: {
title: {{ vendor | json }}
ruleSet: {
appliedDisjunctively: false
rules: [
{
column: VENDOR
relation: EQUALS
condition: {{ vendor | json }}
}
]
}
}
) {
collection {
id
title
handle
}
userErrors {
field
message
}
}
}
{% endaction %}
{% endfor %}
{% endif %}
{% if existing_collections != blank %}
{% log
message: "These collections with title matching product types and/or vendors already existed as of this task run, and will not be recreated or modified",
existing_collections: existing_collections
%}
{% endif %}
{% elsif event.topic == "mechanic/actions/perform" %}
{% comment %}
-- only respond to successful creation of collections
{% endcomment %}
{% unless action.type == "shopify" and action.run.ok and action.run.result.data.collectionCreate %}
{% break %}
{% endunless %}
{% assign collection_id = action.run.result.data.collectionCreate.collection.id %}
{% assign publication_ids = action.meta.publication_ids %}
{% comment %}
-- publish the new collection to all of the valid publications configured in the task
{% endcomment %}
{% assign mutations = array %}
{% for publication_id in publication_ids %}
{% capture mutation %}
publishablePublish{{ forloop.index }}: publishablePublish(
id: {{ collection_id | json }}
input: {
publicationId: {{ publication_id | json }}
}
) {
publishable {
... on Collection {
id
title
handle
}
}
userErrors {
field
message
}
}
{% endcapture %}
{% assign mutations = mutations | push: mutation %}
{% endfor %}
{% if mutations != blank %}
{% action "shopify", __perform_event: false %}
mutation {
{{ mutations | join: newline }}
}
{% endaction %}
{% endif %}
{% endif %}