-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathauto-create-collections-by-metafield-values.json
59 lines (59 loc) · 19.7 KB
/
auto-create-collections-by-metafield-values.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
{
"docs": "Running on a schedule or manually, this task will create automated collections based on the validation values of the configured product metafield definitions. Additionally, configuring one or more exact sales channel names will enable publishing of any newly created collections by this task to those sales channels.\n\nTo configure the 'Product metafields and collection title prefixes' field, use the left-hand side to enter metafield definition identifiers in the format of *namespace.key* (e.g. \"custom.product_colors\"), and the right-hand side to enter the optional title prefix (e.g. \"Color: \") for any collections created for that metafield's validation values.\n\nUpon creation by this task, an automated collection will have a single condition set which auto-includes any products that use the paired metafield definition and value. All other aspects of the collection (e.g. title, handle, sort order, additional conditions, sales channels, etc.) can be manually changed after creation if needed.\n\nTo work with this task, product metafields will need to be [activated](https://help.shopify.com/en/manual/custom-data/metafields/automated-collections#activating-automated-collections) as a collection condition.\n\nNotes:\n- This task only supports metafields of type \"Single line text\" or \"Single line text (List)\".\n- Before creating a new collection, this task will verify that there are no existing collections that use that specific metafield value as a product metafield condition.\n- This task will log any collections found that use an outdated value from a metafield definition.",
"halt_action_run_sequence_on_error": false,
"name": "Auto create collections by metafield values",
"online_store_javascript": null,
"options": {
"product_metafields_and_collection_title_prefixes__keyval_required": {
"custom.product_color": "Color: "
},
"names_of_sales_channels_to_publish_collections_to__array": [
"Online Store"
],
"run_daily__boolean": null,
"run_hourly__boolean": null,
"test_mode__boolean": true
},
"order_status_javascript": null,
"perform_action_runs_in_sequence": false,
"preview_event_definitions": [
{
"description": "New collection created",
"event_attributes": {
"data": {
"meta": {
"publication_ids": [
"gid://shopify/Publication/1234567890"
]
},
"run": {
"ok": true,
"result": {
"data": {
"collectionCreate": {
"collection": {
"id": "gid://shopify/Collection/1234567890"
}
}
}
}
},
"type": "shopify"
},
"topic": "mechanic/actions/perform"
}
}
],
"script": "{% assign product_metafields_and_collection_title_prefixes = options.product_metafields_and_collection_title_prefixes__keyval_required %}\n{% assign sales_channel_names = options.names_of_sales_channels_to_publish_collections_to__array %}\n{% assign run_daily = options.run_daily__boolean %}\n{% assign run_hourly = options.run_hourly__boolean %}\n{% assign test_mode = options.test_mode__boolean %}\n\n{% if event.preview %}\n {% assign product_metafields_and_collection_title_prefixes = hash %}\n {% assign product_metafields_and_collection_title_prefixes[\"custom.product_colors\"] = \"Color: \" %}\n{% endif %}\n\n{% assign product_metafields = product_metafields_and_collection_title_prefixes | keys %}\n\n{% if event.topic == \"mechanic/user/trigger\" or event.topic contains \"mechanic/scheduler/\" %}\n {% if sales_channel_names != blank %}\n {% comment %}\n -- check if the configured sales channels exist in this shop by name; save the publication IDs for lookup later\n {% endcomment %}\n\n {% capture query %}\n query {\n publications(\n first: 250\n catalogType:APP\n ) {\n nodes {\n id\n catalog {\n ... on AppCatalog {\n apps(first: 1) {\n nodes {\n title\n }\n }\n }\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = query | shopify %}\n\n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"publications\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/Publication/1234567890\",\n \"catalog\": {\n \"apps\": {\n \"nodes\": [\n {\n \"title\": {{ sales_channel_names.first | json }}\n }\n ]\n }\n }\n }\n ]\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% assign publication_ids = array %}\n {% assign available_sales_channel_names = array %}\n\n {% for publication in result.data.publications.nodes %}\n {% assign publication_name = publication.catalog.apps.nodes.first.title %}\n\n {% assign available_sales_channel_names = available_sales_channel_names | push: publication_name %}\n\n {% if sales_channel_names contains publication_name %}\n {% assign publication_ids = publication_ids | push: publication.id %}\n {% endif %}\n {% endfor %}\n\n {% unless event.preview %}\n {% if publication_ids == blank %}\n {% error\n 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.\",\n configured_sales_channel_names: sales_channel_names,\n available_sales_channel_names: available_sales_channel_names\n %}\n\n {% break %}\n\n {% elsif publication_ids.size != sales_channel_names.size %}\n {% comment %}\n -- using action error here so the task will continue with any other configured and matched sales channels\n {% endcomment %}\n\n {% action \"echo\"\n __error: \"One or more configured sales channel names do not match any of the publication names available in this shop.\",\n configured_sales_channel_names: sales_channel_names,\n available_sales_channel_names: available_sales_channel_names\n %}\n {% endif %}\n {% endunless %}\n {% endif %}\n\n {% comment %}\n -- loop through all configured metafields to get their metafield definition and validation values to be used as collection conditions\n {% endcomment %}\n\n {% assign metafield_definitions = array %}\n\n {% for product_metafield in product_metafields %}\n {% capture query %}\n query {\n metafieldDefinitions(\n first: 1\n ownerType: PRODUCT\n namespace: {{ product_metafield | split: \".\" | first | json }}\n key: {{ product_metafield | split: \".\" | last | json }}\n ) {\n nodes {\n id\n name\n namespace\n key\n ownerType\n type {\n name\n }\n useAsCollectionCondition\n validations {\n value\n }\n }\n }\n }\n {% endcapture %}\n\n {% assign result = query | shopify %}\n\n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"metafieldDefinitions\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/MetafieldDefinition/1234567890\",\n \"name\": \"Product Colors\",\n \"namespace\": \"custom\",\n \"key\": \"product_colors\",\n \"ownerType\": \"PRODUCT\",\n \"type\": {\n \"name\": \"list.single_line_text_field\"\n },\n \"useAsCollectionCondition\": true,\n \"validations\": [\n {\n \"value\": \"[\\\"Blue\\\",\\\"Red\\\"]\"\n }\n ]\n }\n ]\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% assign metafield_definition = result.data.metafieldDefinitions.nodes.first %}\n {% assign metafield_values\n = metafield_definition.validations.first.value\n | default: \"[]\"\n | parse_json\n %}\n\n {% comment %}\n -- validate that the metafield definition exists and is configured correctly\n -- using action errors for validation so the task will continue running and check all configured metafields\n {% endcomment %}\n\n {% if metafield_definition == blank %}\n {% action \"echo\"\n __error: \"Metafield definition not found for this configured metafield and owner type (PRODUCT).\",\n product_metafield: product_metafield\n %}\n {% continue %}\n {% endif %}\n\n {% unless metafield_definition.type.name contains \"single_line_text_field\" %}\n {% action \"echo\"\n __error: \"Metafield definition for this configured metafield must be a type of 'Single line text' or 'Single line text (List)'.\",\n metafield_definition: metafield_definition,\n product_metafield: product_metafield\n %}\n {% continue %}\n {% endunless %}\n\n {% unless metafield_definition.useAsCollectionCondition %}\n {% action \"echo\"\n __error: \"Metafield definition for this configured metafield must be set for use by 'Automated collections'.\",\n metafield_definition: metafield_definition,\n product_metafield: product_metafield\n %}\n {% continue %}\n {% endunless %}\n\n {% if metafield_values == blank %}\n {% action \"echo\"\n __error: \"Metafield definition validation values not found for this configured metafield.\",\n metafield_definition: metafield_definition,\n product_metafield: product_metafield\n %}\n {% continue %}\n {% endif %}\n\n {% comment %}\n -- all validation checks passed; extend the metafield definition object to store collection data\n {% endcomment %}\n\n {% assign metafield_definition[\"metafield_values\"] = metafield_values %}\n {% assign metafield_definition[\"collection_title_prefix\"] = product_metafields_and_collection_title_prefixes[product_metafield] %}\n {% assign metafield_definition[\"collections_by_value\"] = hash %}\n\n {% for metafield_value in metafield_values %}\n {% assign metafield_definition[\"collections_by_value\"][metafield_value] = array %}\n {% endfor %}\n\n {% assign metafield_definitions = metafield_definitions | push: metafield_definition %}\n {% endfor %}\n\n {% if metafield_definitions == blank %}\n {% log \"None of the configured metafields meet all of the necessary conditions for usage by this task.\" %}\n {% break %}\n {% endif %}\n\n {% comment %}\n -- query automated collections; save collection details for any that use a metafield configured in this task as a condition\n {% endcomment %}\n\n {% assign cursor = nil %}\n\n {% for n in (1..100) %}\n {% capture query %}\n query {\n collections(\n first: 250\n after: {{ cursor | json }}\n query: \"collection_type:smart\"\n ) {\n pageInfo {\n hasNextPage\n endCursor\n }\n nodes {\n id\n title\n handle\n templateSuffix\n sortOrder\n ruleSet {\n appliedDisjunctively\n rules {\n column\n relation\n condition\n conditionObject {\n __typename\n ... on CollectionRuleMetafieldCondition {\n metafieldDefinition {\n id\n }\n }\n }\n }\n }\n }\n }\n\n }\n {% endcapture %}\n\n {% assign result = query | shopify %}\n\n {% if event.preview %}\n {% capture result_json %}\n {\n \"data\": {\n \"collections\": {\n \"nodes\": [\n {\n \"id\": \"gid://shopify/Collection/1234567890\",\n \"ruleSet\": {\n \"rules\": [\n {\n \"column\": \"PRODUCT_METAFIELD_DEFINITION\",\n \"relation\": \"EQUALS\",\n \"condition\": \"Blue\",\n \"conditionObject\": {\n \"metafieldDefinition\": {\n \"id\": \"gid://shopify/MetafieldDefinition/1234567890\"\n }\n }\n }\n ]\n }\n },\n {\n \"id\": \"gid://shopify/Collection/2345678901\",\n \"ruleSet\": {\n \"rules\": [\n {\n \"column\": \"PRODUCT_METAFIELD_DEFINITION\",\n \"relation\": \"EQUALS\",\n \"condition\": \"Grey\",\n \"conditionObject\": {\n \"metafieldDefinition\": {\n \"id\": \"gid://shopify/MetafieldDefinition/1234567890\"\n }\n }\n }\n ]\n }\n }\n ]\n }\n }\n }\n {% endcapture %}\n\n {% assign result = result_json | parse_json %}\n {% endif %}\n\n {% for collection in result.data.collections.nodes %}\n {% if collection.ruleSet == blank %}\n {% continue %}\n {% endif %}\n\n {% for rule in collection.ruleSet.rules %}\n {% unless rule.column == \"PRODUCT_METAFIELD_DEFINITION\" and rule.relation == \"EQUALS\" %}\n {% continue %}\n {% endunless %}\n\n {% assign matched_metafield_definition\n = metafield_definitions\n | where: \"id\", rule.conditionObject.metafieldDefinition.id\n | first\n %}\n\n {% unless matched_metafield_definition %}\n {% continue %}\n {% endunless %}\n\n {% unless matched_metafield_definition.metafield_values contains rule.condition %}\n {% unless event.preview %}\n {% log\n message: \"Found collection using outdated metafield value condition for the matched metafield definition.\",\n collection: collection,\n matched_metafield_definition: matched_metafield_definition\n %}\n {% endunless %}\n\n {% continue %}\n {% endunless %}\n\n {% assign matched_metafield_definition[\"collections_by_value\"][rule.condition]\n = matched_metafield_definition[\"collections_by_value\"][rule.condition]\n | push: collection.id\n %}\n {% endfor %}\n {% endfor %}\n\n {% if result.data.collections.pageInfo.hasNextPage %}\n {% assign cursor = result.data.collections.pageInfo.endCursor %}\n {% else %}\n {% break %}\n {% endif %}\n {% endfor %}\n\n {% unless event.preview %}\n {% log metafield_definitions_with_extended_data: metafield_definitions %}\n {% endunless %}\n\n {% comment %}\n -- check all extended metafield definition objects to see if any new collections need to be made on this task run\n {% endcomment %}\n\n {% assign collections_to_create = array %}\n\n {% for metafield_definition in metafield_definitions %}\n {% for metafield_value in metafield_definition.metafield_values %}\n {% if metafield_definition.collections_by_value[metafield_value] == blank %}\n {% assign product_metafield = metafield_definition.namespace | append: \".\" | append: metafield_definition.key %}\n\n {% assign collection_to_create = hash %}\n {% assign collection_to_create[\"product_metafield\"] = product_metafield %}\n {% assign collection_to_create[\"collection_title\"] = metafield_value | prepend: metafield_definition.collection_title_prefix %}\n {% assign collection_to_create[\"metafield_value\"] = metafield_value %}\n {% assign collection_to_create[\"metafield_definition_id\"] = metafield_definition.id %}\n {% assign collections_to_create = collections_to_create | push: collection_to_create %}\n {% endif %}\n {% endfor %}\n {% endfor %}\n\n {% unless event.preview %}\n {% log collections_to_create: collections_to_create %}\n {% endunless %}\n\n {% if test_mode %}\n {% log \"This task is set for 'Test mode' and no new collections will be created.\" %}\n {% break %}\n {% endif %}\n\n {% for collection_to_create in collections_to_create %}\n {% capture mutation %}\n mutation {\n collectionCreate(\n input: {\n title: {{ collection_to_create.collection_title | json }}\n ruleSet: {\n appliedDisjunctively: false\n rules: [\n {\n column: PRODUCT_METAFIELD_DEFINITION\n relation: EQUALS\n condition: {{ collection_to_create.metafield_value | json }}\n conditionObjectId: {{ collection_to_create.metafield_definition_id | json }}\n }\n ]\n }\n }\n ) {\n collection {\n id\n title\n handle\n }\n userErrors {\n field\n message\n }\n }\n }\n {% endcapture %}\n\n {% action %}\n {\n \"type\": \"shopify\",\n \"options\": {{ mutation | json }},\n \"meta\": {\n \"publication_ids\": {{ publication_ids | json }}\n }\n }\n {% endaction %}\n {% endfor %}\n\n{% elsif event.topic == \"mechanic/actions/perform\" %}\n {% comment %}\n -- only respond to successful creation of collections\n {% endcomment %}\n\n {% unless action.type == \"shopify\" and action.run.ok and action.run.result.data.collectionCreate %}\n {% break %}\n {% endunless %}\n\n {% assign collection_id = action.run.result.data.collectionCreate.collection.id %}\n {% assign publication_ids = action.meta.publication_ids %}\n\n {% comment %}\n -- publish the new collection to all of the valid publications configured in the task\n {% endcomment %}\n\n {% assign mutations = array %}\n\n {% for publication_id in publication_ids %}\n {% capture mutation %}\n publishablePublish{{ forloop.index }}: publishablePublish(\n id: {{ collection_id | json }}\n input: {\n publicationId: {{ publication_id | json }}\n }\n ) {\n publishable {\n ... on Collection {\n id\n title\n handle\n }\n }\n userErrors {\n field\n message\n }\n }\n {% endcapture %}\n\n {% assign mutations = mutations | push: mutation %}\n {% endfor %}\n\n {% if mutations != blank %}\n {% action \"shopify\" %}\n mutation {\n {{ mutations | join: newline }}\n }\n {% endaction %}\n {% endif %}\n{% endif %}\n",
"subscriptions": [
"mechanic/user/trigger",
"mechanic/actions/perform"
],
"subscriptions_template": "{% if options.run_hourly__boolean %}\n mechanic/scheduler/hourly\n{% elsif options.run_daily__boolean %}\n mechanic/scheduler/daily\n{% endif %}\nmechanic/user/trigger \nmechanic/actions/perform",
"tags": [
"Collections",
"Metafields",
"Products",
"Publish"
]
}