{ "kind": "open-flow-workflow", "version": 1, "name": "Ping Configured URLs", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_950abea322ce7e37": { "name": "Every 15 minutes", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "*/15 * * * *", "timezone": "UTC" } ] }, "n_ce4238768a6e5d2c": { "inputs": { "urls": { "kind": "value", "value": [ "https://example.com" ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ce4238768a6e5d2c", "output": "state" } ] }, "previousResult": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_cfd9b86cc9c636f4", "output": "state" } ] } }, "name": "Prepare the next URL", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "string" } }, "nullable": false, "handle": "urls" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousResult" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "name": "Prepare the next URL", "moduleId": "n_ac0f5ae8aa5dce5f", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_a5879dd601787b5c": { "inputs": {}, "name": "More URLs?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "check", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_ce4238768a6e5d2c", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_82b20948a61b79ce": { "inputs": { "initialUrl": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ce4238768a6e5d2c", "output": "url" } ] }, "index": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ce4238768a6e5d2c", "output": "count" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_cfd9b86cc9c636f4", "output": "state" } ] } }, "name": "Prepare the HTTP target", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "initialUrl" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "index" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" } ], "name": "Prepare the HTTP target", "moduleId": "n_eeabf0979559b61b", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_3ed330d836435a08": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_82b20948a61b79ce", "output": "url" } ] } }, "name": "Ping the URL", "onError": "error", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" } ], "name": "Ping the URL", "executor": { "method": "GET", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "none", "authentication": { "type": "none" }, "responseFormat": "text", "statusPolicy": "all", "kind": "http-request" } } }, "n_cfd9b86cc9c636f4": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_82b20948a61b79ce", "output": "url" } ] }, "index": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ce4238768a6e5d2c", "output": "count" } ] }, "status": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_3ed330d836435a08", "output": "statusCode" } ] }, "headers": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_3ed330d836435a08", "output": "headers" } ] }, "error": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_3ed330d836435a08", "output": "error", "system": true } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_cfd9b86cc9c636f4", "output": "state" } ] } }, "name": "Check the response", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "index" }, { "jsonSchema": { "type": "integer" }, "nullable": true, "handle": "status" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": true, "handle": "headers" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "error" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "redirect" } ], "name": "Check the response", "moduleId": "n_940edaffa3b0c8bd", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_3e305a9d0b615b59": { "inputs": {}, "name": "Follow redirect?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "follow", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_cfd9b86cc9c636f4", "output": "redirect" } }, "operator": "isTrue" } ] } ] } ] }, "n_79c2d9fc31f41c1a": { "name": "Completed URL count", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "outputMappings": { "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ce4238768a6e5d2c", "output": "count" } ] } } } }, "edges": [ { "source": "n_950abea322ce7e37", "target": "n_ce4238768a6e5d2c" }, { "source": "n_ce4238768a6e5d2c", "target": "n_a5879dd601787b5c" }, { "source": "n_a5879dd601787b5c", "target": "n_82b20948a61b79ce", "sourceHandle": "check" }, { "source": "n_82b20948a61b79ce", "target": "n_3ed330d836435a08" }, { "source": "n_3ed330d836435a08", "target": "n_cfd9b86cc9c636f4" }, { "source": "n_cfd9b86cc9c636f4", "target": "n_3e305a9d0b615b59" }, { "source": "n_3e305a9d0b615b59", "target": "n_82b20948a61b79ce", "sourceHandle": "follow" }, { "source": "n_3e305a9d0b615b59", "target": "n_ce4238768a6e5d2c", "sourceHandle": "otherwise" }, { "source": "n_a5879dd601787b5c", "target": "n_79c2d9fc31f41c1a", "sourceHandle": "otherwise" } ] } }, "modules": { "n_ac0f5ae8aa5dce5f": { "name": "Prepare the next URL", "imports": [], "source": "export default function next({urls,previousState,previousResult}) {\n if(!Array.isArray(urls)) throw Error('URLs must be a list');\n if(!previousState) for(const url of urls) if(typeof url!=='string'||!/^https?:\\/\\/[^\\/?#@\\s]+(?:[\\/?#][^\\s]*)?$/i.test(url)) throw Error('Every URL must be HTTP or HTTPS without credentials');\n const index=previousState?.index??0,failed=!!previousState?.failed||!!previousResult?.failed;\n if(!Number.isInteger(index)||index<0) throw Error('Invalid state');\n if(index>=urls.length)return {state:{index,failed},more:false,url:'https://unused.invalid',count:index};\n return {state:{index:index+1,failed},more:true,url:urls[index],count:index+1};\n}\n" }, "n_eeabf0979559b61b": { "name": "Prepare the HTTP target", "imports": [], "source": "export default function serverHop({initialUrl,index,previousState}) {\n return {url:previousState?.index===index&&previousState?.nextUrl?previousState.nextUrl:initialUrl};\n}\n" }, "n_940edaffa3b0c8bd": { "name": "Check the response", "imports": [], "source": "export default function assess({url,index,status,headers,error,previousState}) {\n const prior=previousState?.index===index?previousState.hops:0;\n if(!Number.isInteger(prior)||prior<0)throw Error('Invalid redirect state');\n if(error||typeof status!=='number')return {state:{index,hops:prior,nextUrl:'',failed:true},redirect:false};\n if(status>=300&&status<400&&typeof headers?.location==='string'&&headers.location){\n try{const target=new URL(headers.location,url);if(prior>=21||!['http:','https:'].includes(target.protocol)||target.username||target.password)throw Error();return {state:{index,hops:prior+1,nextUrl:target.toString(),failed:false},redirect:true};}catch{return {state:{index,hops:prior,nextUrl:'',failed:true},redirect:false};}\n }\n return {state:{index,hops:prior,nextUrl:'',failed:status<200||status>=300},redirect:false};\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }