{ "kind": "open-flow-workflow", "version": 1, "name": "Log Shopify Order Campaigns in Baserow", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_a452e6624d0a508a": { "name": "Every day at midnight UTC", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "0 0 * * *", "timezone": "UTC" } ] }, "n_ecf978577d7454f4": { "inputs": { "scheduledAt": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a452e6624d0a508a", "output": "scheduledAt" } ] }, "overrideDay": { "kind": "value", "value": "" } }, "name": "Choose yesterday’s orders", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "scheduledAt" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "overrideDay" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "search" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "variables" } ], "name": "Choose yesterday’s orders", "moduleId": "n_a365d5ae42b4a7bb", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_695098ae750baf6f": { "inputs": { "query": { "kind": "value", "value": "query Orders($query:String!){orders(first:100,query:$query){pageInfo{hasNextPage} edges{node{id name totalReceivedSet{shopMoney{amount currencyCode}} customerJourneySummary{ready firstVisit{utmParameters{campaign content medium source}}}}}}}" }, "variables": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ecf978577d7454f4", "output": "variables" } ] } }, "name": "Read Shopify order journeys", "kind": "task", "task": { "inputs": [ { "description": "The GraphQL document to execute.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The GraphQL document to execute." }, "nullable": false, "handle": "query" }, { "description": "GraphQL variables keyed by variable name.", "jsonSchema": { "type": "object", "additionalProperties": { "description": "A variable." }, "description": "GraphQL variables keyed by variable name." }, "nullable": true, "handle": "variables", "value": null } ], "outputs": [ { "description": "The raw object returned by Shopify Admin GraphQL.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The raw object returned by Shopify Admin GraphQL." }, "nullable": false, "handle": "data" }, { "description": "The raw object returned by Shopify Admin GraphQL.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The raw object returned by Shopify Admin GraphQL." }, "nullable": true, "handle": "extensions" } ], "name": "Read Shopify order journeys", "executor": { "kind": "connector", "action": "shopify.execute_graphql" } } }, "n_4cf74d82fb4ab1f3": { "inputs": { "data": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_695098ae750baf6f", "output": "data" } ] } }, "name": "Select campaign orders", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The raw object returned by Shopify Admin GraphQL." }, "nullable": false, "handle": "data" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "rows" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "truncated" } ], "name": "Select campaign orders", "moduleId": "n_758ac68683108d2d", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_61784ab51b90a11f": { "inputs": { "rows": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4cf74d82fb4ab1f3", "output": "rows" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_61784ab51b90a11f", "output": "state" } ] } }, "name": "Prepare the next Baserow row", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "rows" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Baserow row payload keyed by field name or field ID." }, "nullable": false, "handle": "row" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "count" } ], "name": "Prepare the next Baserow row", "moduleId": "n_fa4247aee83ac754", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_fe9bda193e1383e4": { "inputs": {}, "name": "More campaign orders?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "write", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_61784ab51b90a11f", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_efd66966177cfb47": { "inputs": { "tableId": { "kind": "unset" }, "userFieldNames": { "kind": "value", "value": true }, "row": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_61784ab51b90a11f", "output": "row" } ] } }, "name": "Create the Baserow row", "kind": "task", "task": { "inputs": [ { "description": "The Baserow table ID.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The Baserow table ID." }, "nullable": true, "handle": "tableId", "value": null }, { "description": "Whether Baserow should use user-facing field names instead of field IDs.", "jsonSchema": { "type": "boolean", "description": "Whether Baserow should use user-facing field names instead of field IDs." }, "nullable": true, "handle": "userFieldNames", "value": null }, { "description": "A Baserow row payload keyed by field name or field ID.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Baserow row payload keyed by field name or field ID." }, "nullable": false, "handle": "row" } ], "outputs": [ { "description": "The created Baserow row.", "jsonSchema": { "type": "object", "properties": { "id": { "type": "integer", "description": "The Baserow row ID." } }, "required": [ "id" ], "additionalProperties": true, "description": "The created Baserow row." }, "nullable": false, "handle": "row" } ], "name": "Create the Baserow row", "executor": { "kind": "connector", "action": "baserow.create_table_row" } } }, "n_40406d7e13d676b1": { "inputs": { "row": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_efd66966177cfb47", "output": "row" } ] } }, "name": "Confirm the Baserow row", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "properties": { "id": { "type": "integer", "description": "The Baserow row ID." } }, "required": [ "id" ], "additionalProperties": true, "description": "The created Baserow row." }, "nullable": false, "handle": "row" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "ok" } ], "name": "Confirm the Baserow row", "moduleId": "n_a4960080df180916", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_8feeef7afa6053ab": { "name": "Complete", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "count" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "truncated" } ], "outputMappings": { "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_61784ab51b90a11f", "output": "count" } ] }, "truncated": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4cf74d82fb4ab1f3", "output": "truncated" } ] } } } }, "edges": [ { "source": "n_a452e6624d0a508a", "target": "n_ecf978577d7454f4" }, { "source": "n_ecf978577d7454f4", "target": "n_695098ae750baf6f" }, { "source": "n_695098ae750baf6f", "target": "n_4cf74d82fb4ab1f3" }, { "source": "n_4cf74d82fb4ab1f3", "target": "n_61784ab51b90a11f" }, { "source": "n_61784ab51b90a11f", "target": "n_fe9bda193e1383e4" }, { "source": "n_fe9bda193e1383e4", "target": "n_efd66966177cfb47", "sourceHandle": "write" }, { "source": "n_efd66966177cfb47", "target": "n_40406d7e13d676b1" }, { "source": "n_40406d7e13d676b1", "target": "n_61784ab51b90a11f" }, { "source": "n_fe9bda193e1383e4", "target": "n_8feeef7afa6053ab", "sourceHandle": "otherwise" } ] } }, "modules": { "n_a365d5ae42b4a7bb": { "name": "Choose yesterday’s orders", "imports": [], "source": "export default function window({scheduledAt,overrideDay}){const n=new Date(scheduledAt);if(!Number.isFinite(n.getTime()))throw Error('Invalid schedule time');if(typeof overrideDay!=='string')throw Error('Invalid replay date');let start,end;if(overrideDay){if(!/^\\d{4}-\\d{2}-\\d{2}$/.test(overrideDay))throw Error('Replay date must be YYYY-MM-DD');start=new Date(overrideDay+'T00:00:00.000Z');if(!Number.isFinite(start.getTime())||start.toISOString().slice(0,10)!==overrideDay)throw Error('Invalid replay date');end=new Date(start.getTime()+86400000);}else{end=new Date(Date.UTC(n.getUTCFullYear(),n.getUTCMonth(),n.getUTCDate()));start=new Date(end.getTime()-86400000)}const search=\"created_at:>='\"+start.toISOString()+\"' created_at:<'\"+end.toISOString()+\"'\";return{search,variables:{query:search}};}\n" }, "n_758ac68683108d2d": { "name": "Select campaign orders", "imports": [], "source": "export default function plan({data}){\n if(!data||!data.orders||!Array.isArray(data.orders.edges)||!data.orders.pageInfo||typeof data.orders.pageInfo.hasNextPage!=='boolean')throw Error('Invalid Shopify order response');const rows=[];\n for(const edge of data.orders.edges){const o=edge?.node;if(!o||typeof o.id!=='string'||!o.id||typeof o.name!=='string'||!o.name)throw Error('Invalid Shopify order');if(!Object.hasOwn(o,'customerJourneySummary'))throw Error('Missing Shopify journey');const j=o.customerJourneySummary;if(j===null)continue;if(typeof j!=='object'||Array.isArray(j)||typeof j.ready!=='boolean'||!j.ready)throw Error('Shopify attribution is not ready');if(!Object.hasOwn(j,'firstVisit'))throw Error('Missing first visit');if(j.firstVisit===null)continue;if(typeof j.firstVisit!=='object'||Array.isArray(j.firstVisit)||!Object.hasOwn(j.firstVisit,'utmParameters'))throw Error('Invalid first visit');const u=j.firstVisit.utmParameters;if(u===null)continue;if(typeof u!=='object'||Array.isArray(u))throw Error('Invalid UTM parameters');if(!Object.hasOwn(u,'campaign')||!(typeof u.campaign==='string'||u.campaign===null))throw Error('Invalid campaign');if(u.campaign===null)continue;for(const key of ['content','medium','source'])if(!Object.hasOwn(u,key)||!(typeof u[key]==='string'||u[key]===null))throw Error('Invalid UTM '+key);const money=o.totalReceivedSet?.shopMoney;if(!money||typeof money.amount!=='string'||!/^\\d+(?:\\.\\d+)?$/.test(money.amount)||typeof money.currencyCode!=='string'||!/^[A-Z]{3}$/.test(money.currencyCode))throw Error('Invalid order amount');rows.push({order:o.name,campaign:u.campaign,content:u.content??'',medium:u.medium??'',source:u.source??'',revenue:money.amount,currency:money.currencyCode});}\n return{rows,truncated:data.orders.pageInfo.hasNextPage};\n}\n" }, "n_fa4247aee83ac754": { "name": "Prepare the next Baserow row", "imports": [], "source": "export default function next({rows,previous}){const i=previous?.index??0;if(!Array.isArray(rows)||!Number.isSafeInteger(i)||i<0||i>rows.length)throw Error('Invalid order state');if(i===rows.length)return{state:{index:i},more:false,row:{},count:i};const r=rows[i];return{state:{index:i+1},more:true,row:{Order:r.order,Campaign:r.campaign,Content:r.content,Medium:r.medium,Source:r.source,Revenue:r.revenue,Currency:r.currency},count:i};}\n" }, "n_a4960080df180916": { "name": "Confirm the Baserow row", "imports": [], "source": "export default function confirm({row}){if(!row||!Number.isSafeInteger(row.id)||row.id<=0)throw Error('Baserow did not confirm the row');return{ok:true};}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }