{ "kind": "open-flow-workflow", "version": 1, "name": "Import Sheet Contacts to Salesforce", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_71b8d91c2ae693aa": { "name": "Import contacts", "kind": "start", "inputDefs": [] }, "n_07a69b034f36d94c": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_SPREADSHEET_ID" }, "range": { "kind": "value", "value": "'Sheet1'!A:Z" }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "UNFORMATTED_VALUE" } }, "name": "Read contact sheet", "kind": "task", "task": { "inputs": [ { "description": "The spreadsheet ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The spreadsheet ID." }, "nullable": true, "handle": "spreadsheetId", "value": null }, { "description": "The A1 notation of the range to retrieve.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to retrieve." }, "nullable": false, "handle": "range" }, { "description": "The major dimension for the returned values.", "jsonSchema": { "type": "string", "enum": [ "DIMENSION_UNSPECIFIED", "ROWS", "COLUMNS" ], "description": "The major dimension for the returned values." }, "nullable": true, "handle": "majorDimension", "value": null }, { "description": "How values should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA" ], "description": "How values should be rendered in the output." }, "nullable": true, "handle": "valueRenderOption", "value": null }, { "description": "How dates and times should be rendered in the output.", "jsonSchema": { "type": "string", "enum": [ "SERIAL_NUMBER", "FORMATTED_STRING" ], "description": "How dates and times should be rendered in the output." }, "nullable": true, "handle": "dateTimeRenderOption", "value": null } ], "outputs": [ { "description": "The A1 notation of the range.", "jsonSchema": { "type": "string", "description": "The A1 notation of the range." }, "nullable": false, "handle": "range" }, { "description": "The major dimension of the values (ROWS or COLUMNS).", "jsonSchema": { "type": "string", "description": "The major dimension of the values (ROWS or COLUMNS)." }, "nullable": false, "handle": "majorDimension" }, { "description": "The data values in the range.", "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data values in the range." }, "nullable": false, "handle": "values" } ], "name": "Read contact sheet", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_62c3d13e7d11bea4": { "inputs": { "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_07a69b034f36d94c", "output": "values" } ] } }, "name": "Validate contact rows", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "array", "items": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "number", "description": "A union variant." }, { "type": "boolean", "description": "A union variant." }, { "const": null, "description": "A union variant." } ], "description": "The cell value schema." }, "description": "An array item." }, "description": "The data values in the range." }, "nullable": false, "handle": "values" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "rows" } ], "name": "Validate contact rows", "moduleId": "n_df8ab6b93f0c2669", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_e4ea4d9145e9b023": { "inputs": { "rows": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_62c3d13e7d11bea4", "output": "rows" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_200a3ee004b0b610", "output": "state" } ] } }, "name": "Prepare next contact", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "rows" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "row" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "known" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "accountId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "soql" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "company" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "name": "Prepare next contact", "moduleId": "n_17b8ebe8d546519e", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_01b0f00593426b49": { "inputs": {}, "name": "More contacts?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_e4ea4d9145e9b023", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_428554e92d6978d8": { "inputs": { "soql": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e4ea4d9145e9b023", "output": "soql" } ] }, "knownId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e4ea4d9145e9b023", "output": "accountId" } ] }, "company": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e4ea4d9145e9b023", "output": "company" } ] } }, "name": "Find account", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "soql" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "knownId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "company" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "found" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "id" } ], "name": "Find account", "moduleId": "n_d944b3656f730e7c", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "salesforce.query" } ] } ] } }, "n_55952501c6ec78ea": { "inputs": {}, "name": "Account found?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "found", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_428554e92d6978d8", "output": "found" } }, "operator": "isTrue" } ] } ] } ] }, "n_faa1706095b5ebee": { "inputs": { "row": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e4ea4d9145e9b023", "output": "row" } ] } }, "name": "Create account", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "row" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "id" } ], "name": "Create account", "moduleId": "n_7d9c509cca084a80", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "salesforce.create_record" } ] } ] } }, "n_4581ada30daa8c52": { "inputs": { "id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_428554e92d6978d8", "output": "id" } ] } }, "name": "Use found account", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "id" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "id" } ], "name": "Use found account", "moduleId": "n_892153c0195691c0", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_f5bbcbd85f88114e": { "inputs": { "row": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e4ea4d9145e9b023", "output": "row" } ] }, "id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4581ada30daa8c52", "output": "id" }, { "kind": "node", "nodeId": "n_faa1706095b5ebee", "output": "id" } ] } }, "name": "Upsert contact", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "row" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "id" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "ok" } ], "name": "Upsert contact", "moduleId": "n_e0413e638e82d0f1", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [ { "action": "salesforce.upsert_record" } ] } ] } }, "n_200a3ee004b0b610": { "inputs": { "state": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e4ea4d9145e9b023", "output": "state" } ] }, "row": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e4ea4d9145e9b023", "output": "row" } ] }, "id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4581ada30daa8c52", "output": "id" }, { "kind": "node", "nodeId": "n_faa1706095b5ebee", "output": "id" } ] } }, "name": "Remember account", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "row" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "id" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" } ], "name": "Remember account", "moduleId": "n_a1120d51cc587051", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_5d30f5501633a6e0": { "name": "Imported contact count", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "outputMappings": { "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e4ea4d9145e9b023", "output": "count" } ] } } } }, "edges": [ { "source": "n_71b8d91c2ae693aa", "target": "n_07a69b034f36d94c" }, { "source": "n_07a69b034f36d94c", "target": "n_62c3d13e7d11bea4" }, { "source": "n_62c3d13e7d11bea4", "target": "n_e4ea4d9145e9b023" }, { "source": "n_e4ea4d9145e9b023", "target": "n_01b0f00593426b49" }, { "source": "n_01b0f00593426b49", "target": "n_428554e92d6978d8", "sourceHandle": "more" }, { "source": "n_01b0f00593426b49", "target": "n_5d30f5501633a6e0", "sourceHandle": "otherwise" }, { "source": "n_428554e92d6978d8", "target": "n_55952501c6ec78ea" }, { "source": "n_55952501c6ec78ea", "target": "n_4581ada30daa8c52", "sourceHandle": "found" }, { "source": "n_4581ada30daa8c52", "target": "n_f5bbcbd85f88114e" }, { "source": "n_55952501c6ec78ea", "target": "n_faa1706095b5ebee", "sourceHandle": "otherwise" }, { "source": "n_faa1706095b5ebee", "target": "n_f5bbcbd85f88114e" }, { "source": "n_f5bbcbd85f88114e", "target": "n_200a3ee004b0b610" }, { "source": "n_200a3ee004b0b610", "target": "n_e4ea4d9145e9b023" } ] } }, "modules": { "n_df8ab6b93f0c2669": { "name": "Validate contact rows", "imports": [], "source": "export default function({values}){if(!Array.isArray(values))throw Error('Invalid sheet data');if(!values.length)return {rows:[]};const header=values[0],names=['Company Name','First Name','Last Name','Email'];if(!Array.isArray(header))throw Error('Invalid header');const cols=names.map(n=>{const i=header.indexOf(n);if(i<0||header.lastIndexOf(n)!==i)throw Error('Expected unique '+n+' header');return i});const rows=values.slice(1).filter(r=>Array.isArray(r)&&r.some(x=>x!==''&&x!=null)).map(r=>{const [company,first,last,email]=cols.map(i=>r[i]);if(![company,first,last,email].every(x=>typeof x==='string'&&x.trim())||/[\\r\\n\\x00]/.test(company)||!/^\\S+@\\S+\\.\\S+$/.test(email))throw Error('Every row needs company, first name, last name and email');return {company,first,last,email}});return{rows}}\n" }, "n_17b8ebe8d546519e": { "name": "Prepare next contact", "imports": [], "source": "export default function({rows,previousState}){const state=previousState??{index:0,accounts:{}};if(!Array.isArray(rows)||!Number.isInteger(state.index)||state.index<0||state.index>rows.length||!state.accounts||typeof state.accounts!=='object')throw Error('Invalid import state');if(state.index===rows.length)return {state,row:{},more:false,known:false,accountId:'',soql:'SELECT Id FROM Account LIMIT 1',company:'',count:state.index};const row=rows[state.index],id=Object.hasOwn(state.accounts,row.company)?state.accounts[row.company]:'',escaped=row.company.replace(/\\\\/g,'\\\\\\\\').replace(/'/g,\"\\\\'\");return {state,row,more:true,known:!!id,accountId:id,company:row.company,soql:\"SELECT Id, Name FROM Account WHERE Name = '\"+escaped+\"' LIMIT 2\",count:state.index}}\n" }, "n_d944b3656f730e7c": { "name": "Find account", "imports": [], "source": "export default async function({soql,knownId,company},ctx){const r=await ctx.actions.call('salesforce.query',{soql});if(!r||!Array.isArray(r.records)||r.done!==true||r.records.length>1)throw Error('Account name is ambiguous or query incomplete');if(!r.records.length)return {found:!!knownId,id:knownId};if(typeof r.records[0]?.Name!=='string')throw Error('Invalid account name');if(r.records[0].Name!==company)return {found:!!knownId,id:knownId};const id=r.records[0]?.Id;if(typeof id!=='string'||!/^[A-Za-z0-9]{15}(?:[A-Za-z0-9]{3})?$/.test(id))throw Error('Invalid account ID');if(knownId&&knownId!==id)throw Error('Account changed during import');return {found:true,id}}\n" }, "n_7d9c509cca084a80": { "name": "Create account", "imports": [], "source": "export default async function({row},ctx){const r=await ctx.actions.call('salesforce.create_record',{objectName:'Account',fields:{Name:row.company}});if(r?.success!==true||typeof r.id!=='string'||!/^[A-Za-z0-9]{15}(?:[A-Za-z0-9]{3})?$/.test(r.id))throw Error('Account creation was not confirmed');return{id:r.id}}\n" }, "n_892153c0195691c0": { "name": "Use found account", "imports": [], "source": "export default function({id}){return {id}}" }, "n_e0413e638e82d0f1": { "name": "Upsert contact", "imports": [], "source": "export default async function({row,id},ctx){if(typeof id!=='string'||!/^[A-Za-z0-9]{15}(?:[A-Za-z0-9]{3})?$/.test(id))throw Error('Invalid account ID');const r=await ctx.actions.call('salesforce.upsert_record',{objectName:'Contact',externalIdField:'Email',externalId:row.email,fields:{FirstName:row.first,LastName:row.last,AccountId:id}});if(r?.success!==true)throw Error('Contact upsert was not confirmed');return{ok:true}}\n" }, "n_a1120d51cc587051": { "name": "Remember account", "imports": [], "source": "export default function({state,row,id}){return {state:{index:state.index+1,accounts:{...state.accounts,[row.company]:id}}}}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }