{ "kind": "open-flow-workflow", "version": 1, "name": "Monitor Website Status in Google Sheets", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_6ad3cf5e6a9950a7": { "name": "Every six hours UTC", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "0 */6 * * *", "timezone": "UTC" } ] }, "n_4ac37d62f70c9c8d": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_SPREADSHEET_ID" }, "range": { "kind": "value", "value": "'dashboard'!A:Z" }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "UNFORMATTED_VALUE" } }, "name": "Read the site dashboard", "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 the site dashboard", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_1b026de286085ad5": { "inputs": { "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4ac37d62f70c9c8d", "output": "values" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1b026de286085ad5", "output": "state" } ] } }, "name": "Prepare the next site", "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" }, { "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": "more" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "site" }, { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "name": "Prepare the next site", "moduleId": "n_27b453182f57b95e", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_b1a94e2f91873b4b": { "inputs": {}, "name": "More sites?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "check", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_1b026de286085ad5", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_b7ef2f1bffa7a6b2": { "inputs": { "initialUrl": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1b026de286085ad5", "output": "url" } ] }, "index": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1b026de286085ad5", "output": "count" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_835b0e1f95aa203f", "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_896cf2f8b18d924f", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_86d45065e9c5de01": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b7ef2f1bffa7a6b2", "output": "url" } ] } }, "name": "Check the website", "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": "Check the website", "executor": { "method": "GET", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "none", "authentication": { "type": "none" }, "responseFormat": "text", "statusPolicy": "all", "kind": "http-request" } } }, "n_835b0e1f95aa203f": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b7ef2f1bffa7a6b2", "output": "url" } ] }, "index": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1b026de286085ad5", "output": "count" } ] }, "status": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_86d45065e9c5de01", "output": "statusCode" } ] }, "headers": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_86d45065e9c5de01", "output": "headers" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_835b0e1f95aa203f", "output": "state" } ] } }, "name": "Evaluate the HTTP 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": false, "handle": "status" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" }, { "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" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "up" } ], "name": "Evaluate the HTTP response", "moduleId": "n_f56b01b1985caa2b", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_5e6b93a05614bc54": { "inputs": {}, "name": "Follow a redirect?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "follow", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_835b0e1f95aa203f", "output": "redirect" } }, "operator": "isTrue" } ] } ] } ] }, "n_af9f9701bb2c2abf": { "inputs": { "site": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1b026de286085ad5", "output": "site" } ] }, "up": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_835b0e1f95aa203f", "output": "up" } ] }, "now": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6ad3cf5e6a9950a7", "output": "scheduledAt" } ] } }, "name": "Prepare the observation", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "site" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "up" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "now" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to append values after." }, "nullable": false, "handle": "logRange" }, { "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 to append." }, "nullable": false, "handle": "logValues" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to write to." }, "nullable": false, "handle": "statusRange" }, { "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 to write into the range." }, "nullable": false, "handle": "statusValues" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "alert" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "subject" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "message" } ], "name": "Prepare the observation", "moduleId": "n_91f4e6ae38684ff2", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_9ca6b72783077849": { "inputs": {}, "name": "Send an alert?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "send", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_af9f9701bb2c2abf", "output": "alert" } }, "operator": "isTrue" } ] } ] } ] }, "n_27868329af14bf91": { "inputs": { "to": { "kind": "value", "value": "YOUR_ALERT_EMAIL" }, "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_af9f9701bb2c2abf", "output": "subject" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_af9f9701bb2c2abf", "output": "message" } ] }, "isHtml": { "kind": "value", "value": false } }, "name": "Email the alert", "kind": "task", "task": { "inputs": [ { "description": "Primary recipient email address.", "jsonSchema": { "type": "string", "description": "Primary recipient email address." }, "nullable": true, "handle": "to", "value": null }, { "description": "Additional To recipients.", "jsonSchema": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "Additional To recipients." }, "nullable": true, "handle": "extraRecipients", "value": null }, { "description": "Cc recipients.", "jsonSchema": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "A union variant." } ], "description": "Cc recipients." }, "nullable": true, "handle": "cc", "value": null }, { "description": "Bcc recipients.", "jsonSchema": { "anyOf": [ { "type": "string", "description": "A union variant." }, { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "A union variant." } ], "description": "Bcc recipients." }, "nullable": true, "handle": "bcc", "value": null }, { "description": "Email subject line.", "jsonSchema": { "type": "string", "description": "Email subject line." }, "nullable": true, "handle": "subject", "value": null }, { "description": "Email body content.", "jsonSchema": { "type": "string", "description": "Email body content." }, "nullable": true, "handle": "body", "value": null }, { "description": "Set to true when the body content is already HTML.", "jsonSchema": { "type": "boolean", "description": "Set to true when the body content is already HTML." }, "nullable": true, "handle": "isHtml", "value": null }, { "description": "Attachments and CID inline images. Up to 25,000,000 decoded bytes in total and 35,000,000 MIME bytes.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "filename": { "type": "string", "description": "Attachment filename; omit for an unnamed attachment." }, "mimeType": { "type": "string", "description": "MIME type without parameters; defaults to application/octet-stream." }, "contentBase64": { "type": "string", "description": "Standard padded Base64 content. Empty string represents a zero-byte file. Supply this or fileUrl." }, "fileUrl": { "type": "string", "format": "uri", "description": "Public URL to download the attachment. Supply this or contentBase64." }, "contentId": { "type": "string", "minLength": 1, "description": "Bare Content-ID without cid: or angle brackets; reference it as cid: in HTML." }, "disposition": { "type": "string", "enum": [ "inline", "attachment" ], "description": "Defaults to inline when contentId is set, otherwise attachment. Inline requires contentId." } }, "additionalProperties": false, "description": "An attachment or CID inline resource.", "oneOf": [ { "required": [ "contentBase64" ] }, { "required": [ "fileUrl" ] } ] }, "maxItems": 100, "description": "Attachments and CID inline images. Up to 25,000,000 decoded bytes in total and 35,000,000 MIME bytes." }, "nullable": true, "handle": "attachments", "value": null }, { "description": "Verified Gmail send-as alias to use in the From header.", "jsonSchema": { "type": "string", "description": "Verified Gmail send-as alias to use in the From header." }, "nullable": true, "handle": "fromEmail", "value": null } ], "outputs": [ { "description": "Gmail message ID.", "jsonSchema": { "type": "string", "description": "Gmail message ID." }, "nullable": false, "handle": "messageId" }, { "description": "Gmail thread ID.", "jsonSchema": { "type": "string", "description": "Gmail thread ID." }, "nullable": true, "handle": "threadId" } ], "name": "Email the alert", "executor": { "kind": "connector", "action": "gmail.send_email" } } }, "n_b627912fc1c22ac0": { "inputs": { "channelId": { "kind": "value", "value": "YOUR_SLACK_CHANNEL_ID" }, "text": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_af9f9701bb2c2abf", "output": "message" } ] } }, "name": "Send the Slack alert", "kind": "task", "task": { "inputs": [ { "description": "The Slack conversation or channel ID.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The Slack conversation or channel ID." }, "nullable": false, "handle": "channelId" }, { "description": "Plain text message content. When blocks are provided, Slack uses this as notification and accessibility fallback text.", "jsonSchema": { "type": "string", "description": "Plain text message content. When blocks are provided, Slack uses this as notification and accessibility fallback text." }, "nullable": true, "handle": "text", "value": null }, { "description": "Slack Block Kit blocks to render in the message.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Slack Block Kit block object. Pass the block exactly as Slack documents it." }, "minItems": 1, "description": "Slack Block Kit blocks to render in the message." }, "nullable": true, "handle": "blocks", "value": null }, { "description": "Slack legacy attachments to include in the message.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "A Slack legacy attachment object. Prefer blocks for new messages when possible." }, "minItems": 1, "description": "Slack legacy attachments to include in the message." }, "nullable": true, "handle": "attachments", "value": null }, { "description": "Whether Slack should unfurl links in the message.", "jsonSchema": { "type": "boolean", "description": "Whether Slack should unfurl links in the message." }, "nullable": true, "handle": "unfurlLinks", "value": null }, { "description": "Whether Slack should unfurl media in the message.", "jsonSchema": { "type": "boolean", "description": "Whether Slack should unfurl media in the message." }, "nullable": true, "handle": "unfurlMedia", "value": null }, { "description": "Slack message metadata to attach to the message.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Slack message metadata to attach to the message." }, "nullable": true, "handle": "metadata", "value": null } ], "outputs": [ { "description": "The timestamp identifier of the posted message.", "jsonSchema": { "type": "string", "description": "The timestamp identifier of the posted message." }, "nullable": false, "handle": "ts" }, { "description": "The channel ID where the message was posted.", "jsonSchema": { "type": "string", "description": "The channel ID where the message was posted." }, "nullable": false, "handle": "channelId" } ], "name": "Send the Slack alert", "executor": { "kind": "connector", "action": "slack.post_message" } } }, "n_4ff34871b9d9f99a": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_SPREADSHEET_ID" }, "range": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_af9f9701bb2c2abf", "output": "logRange" } ] }, "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_af9f9701bb2c2abf", "output": "logValues" } ] }, "valueInputOption": { "kind": "value", "value": "RAW" }, "insertDataOption": { "kind": "value", "value": "INSERT_ROWS" } }, "name": "Log the observation", "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 append values after.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to append values after." }, "nullable": false, "handle": "range" }, { "description": "The data to append.", "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 to append." }, "nullable": false, "handle": "values" }, { "description": "The major dimension of the values array.", "jsonSchema": { "type": "string", "enum": [ "ROWS", "COLUMNS" ], "description": "The major dimension of the values array." }, "nullable": true, "handle": "majorDimension", "value": null }, { "description": "How the data should be inserted.", "jsonSchema": { "type": "string", "enum": [ "OVERWRITE", "INSERT_ROWS" ], "description": "How the data should be inserted." }, "nullable": true, "handle": "insertDataOption", "value": null }, { "description": "How the input data should be interpreted.", "jsonSchema": { "type": "string", "enum": [ "RAW", "USER_ENTERED" ], "description": "How the input data should be interpreted." }, "nullable": true, "handle": "valueInputOption", "value": null }, { "description": "Whether to include the appended values in the response.", "jsonSchema": { "type": "boolean", "description": "Whether to include the appended values in the response." }, "nullable": true, "handle": "includeValuesInResponse", "value": null }, { "description": "How values in the response should be rendered.", "jsonSchema": { "type": "string", "enum": [ "FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA" ], "description": "How values in the response should be rendered." }, "nullable": true, "handle": "responseValueRenderOption", "value": null }, { "description": "How dates in the response should be rendered.", "jsonSchema": { "type": "string", "enum": [ "SERIAL_NUMBER", "FORMATTED_STRING" ], "description": "How dates in the response should be rendered." }, "nullable": true, "handle": "responseDateTimeRenderOption", "value": null } ], "outputs": [ { "description": "The unique ID of the spreadsheet.", "jsonSchema": { "type": "string", "description": "The unique ID of the spreadsheet." }, "nullable": false, "handle": "spreadsheetId" }, { "description": "The A1 notation of the range that was updated.", "jsonSchema": { "type": "string", "description": "The A1 notation of the range that was updated." }, "nullable": false, "handle": "updatedRange" }, { "description": "The number of rows that were updated.", "jsonSchema": { "type": "integer", "description": "The number of rows that were updated." }, "nullable": false, "handle": "updatedRows" }, { "description": "The number of columns that were updated.", "jsonSchema": { "type": "integer", "description": "The number of columns that were updated." }, "nullable": false, "handle": "updatedColumns" }, { "description": "The number of cells that were updated.", "jsonSchema": { "type": "integer", "description": "The number of cells that were updated." }, "nullable": false, "handle": "updatedCells" }, { "description": "The values of the cells after the update.", "jsonSchema": { "type": "object", "properties": { "range": { "type": "string", "description": "The A1 notation of the range." }, "majorDimension": { "type": "string", "description": "The major dimension of the values (ROWS or COLUMNS)." }, "values": { "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." } }, "required": [ "range", "majorDimension", "values" ], "additionalProperties": false, "description": "The values of the cells after the update." }, "nullable": true, "handle": "updatedData" }, { "description": "The A1 notation of the table range that data was appended to.", "jsonSchema": { "type": "string", "description": "The A1 notation of the table range that data was appended to." }, "nullable": false, "handle": "tableRange" } ], "name": "Log the observation", "executor": { "kind": "connector", "action": "googlesheets.spreadsheets_values_append" } } }, "n_53513590bde06393": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_SPREADSHEET_ID" }, "range": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_af9f9701bb2c2abf", "output": "statusRange" } ] }, "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_af9f9701bb2c2abf", "output": "statusValues" } ] }, "valueInputOption": { "kind": "value", "value": "RAW" } }, "name": "Update the site status", "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 write to.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to write to." }, "nullable": false, "handle": "range" }, { "description": "The data to write into 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 to write into the range." }, "nullable": false, "handle": "values" }, { "description": "The major dimension of the values array.", "jsonSchema": { "type": "string", "enum": [ "ROWS", "COLUMNS" ], "description": "The major dimension of the values array." }, "nullable": true, "handle": "majorDimension", "value": null }, { "description": "How the input data should be interpreted.", "jsonSchema": { "type": "string", "enum": [ "RAW", "USER_ENTERED" ], "description": "How the input data should be interpreted." }, "nullable": true, "handle": "valueInputOption", "value": null }, { "description": "Whether to include the written values in the response.", "jsonSchema": { "type": "boolean", "description": "Whether to include the written values in the response." }, "nullable": true, "handle": "includeValuesInResponse", "value": null }, { "description": "How values in the response should be rendered.", "jsonSchema": { "type": "string", "enum": [ "FORMATTED_VALUE", "UNFORMATTED_VALUE", "FORMULA" ], "description": "How values in the response should be rendered." }, "nullable": true, "handle": "responseValueRenderOption", "value": null }, { "description": "How dates in the response should be rendered.", "jsonSchema": { "type": "string", "enum": [ "SERIAL_NUMBER", "FORMATTED_STRING" ], "description": "How dates in the response should be rendered." }, "nullable": true, "handle": "responseDateTimeRenderOption", "value": null }, { "description": "Whether to auto-expand the sheet to fit the written data.", "jsonSchema": { "type": "boolean", "description": "Whether to auto-expand the sheet to fit the written data." }, "nullable": true, "handle": "autoExpandSheet", "value": null } ], "outputs": [ { "description": "The unique ID of the spreadsheet.", "jsonSchema": { "type": "string", "description": "The unique ID of the spreadsheet." }, "nullable": false, "handle": "spreadsheetId" }, { "description": "The A1 notation of the range that was updated.", "jsonSchema": { "type": "string", "description": "The A1 notation of the range that was updated." }, "nullable": false, "handle": "updatedRange" }, { "description": "The number of rows that were updated.", "jsonSchema": { "type": "integer", "description": "The number of rows that were updated." }, "nullable": false, "handle": "updatedRows" }, { "description": "The number of columns that were updated.", "jsonSchema": { "type": "integer", "description": "The number of columns that were updated." }, "nullable": false, "handle": "updatedColumns" }, { "description": "The number of cells that were updated.", "jsonSchema": { "type": "integer", "description": "The number of cells that were updated." }, "nullable": false, "handle": "updatedCells" }, { "description": "The values of the cells after the update.", "jsonSchema": { "type": "object", "properties": { "range": { "type": "string", "description": "The A1 notation of the range." }, "majorDimension": { "type": "string", "description": "The major dimension of the values (ROWS or COLUMNS)." }, "values": { "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." } }, "required": [ "range", "majorDimension", "values" ], "additionalProperties": false, "description": "The values of the cells after the update." }, "nullable": true, "handle": "updatedData" } ], "name": "Update the site status", "executor": { "kind": "connector", "action": "googlesheets.values_update" } } }, "n_c08c56a2895d4544": { "inputs": { "updatedCells": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_53513590bde06393", "output": "updatedCells" } ] } }, "name": "Confirm the status update", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "integer", "description": "The number of cells that were updated." }, "nullable": false, "handle": "updatedCells" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "ok" } ], "name": "Confirm the status update", "moduleId": "n_50282b1b7045f278", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_0d6bb56b132bd45e": { "name": "Complete", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "integer", "minimum": 0 }, "nullable": false, "handle": "count" } ], "outputMappings": { "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_1b026de286085ad5", "output": "count" } ] } } } }, "edges": [ { "source": "n_6ad3cf5e6a9950a7", "target": "n_4ac37d62f70c9c8d" }, { "source": "n_4ac37d62f70c9c8d", "target": "n_1b026de286085ad5" }, { "source": "n_1b026de286085ad5", "target": "n_b1a94e2f91873b4b" }, { "source": "n_b1a94e2f91873b4b", "target": "n_b7ef2f1bffa7a6b2", "sourceHandle": "check" }, { "source": "n_b7ef2f1bffa7a6b2", "target": "n_86d45065e9c5de01" }, { "source": "n_86d45065e9c5de01", "target": "n_835b0e1f95aa203f" }, { "source": "n_835b0e1f95aa203f", "target": "n_5e6b93a05614bc54" }, { "source": "n_5e6b93a05614bc54", "target": "n_b7ef2f1bffa7a6b2", "sourceHandle": "follow" }, { "source": "n_5e6b93a05614bc54", "target": "n_af9f9701bb2c2abf", "sourceHandle": "otherwise" }, { "source": "n_af9f9701bb2c2abf", "target": "n_9ca6b72783077849" }, { "source": "n_9ca6b72783077849", "target": "n_27868329af14bf91", "sourceHandle": "send" }, { "source": "n_27868329af14bf91", "target": "n_b627912fc1c22ac0" }, { "source": "n_b627912fc1c22ac0", "target": "n_4ff34871b9d9f99a" }, { "source": "n_9ca6b72783077849", "target": "n_4ff34871b9d9f99a", "sourceHandle": "otherwise" }, { "source": "n_4ff34871b9d9f99a", "target": "n_53513590bde06393" }, { "source": "n_53513590bde06393", "target": "n_c08c56a2895d4544" }, { "source": "n_c08c56a2895d4544", "target": "n_1b026de286085ad5" }, { "source": "n_b1a94e2f91873b4b", "target": "n_0d6bb56b132bd45e", "sourceHandle": "otherwise" } ] } }, "modules": { "n_27b453182f57b95e": { "name": "Prepare the next site", "imports": [], "source": "export default function nextDashboard({values,previousState}) {\n if(!Array.isArray(values))throw Error('Invalid dashboard');\n const index=previousState?.index??0;if(!Number.isInteger(index)||index<0)throw Error('Invalid index');\n if(!values.length)return{state:{index},more:false,url:'https://unused.invalid',site:{},count:index};\n const head=values[0];if(!Array.isArray(head)||['Property','Status','Log Sheet'].some(x=>head.filter(v=>v===x).length!==1))throw Error('Dashboard requires unique Property, Status and Log Sheet headers');\n const p=head.indexOf('Property'),s=head.indexOf('Status'),l=head.indexOf('Log Sheet');const rows=values.slice(1).map((v,i)=>({v,row:i+2})).filter(x=>Array.isArray(x.v)&&x.v.some(y=>y!==''&&y!=null));const seen=new Set();\n for(const {v} of rows){if(typeof v[p]!=='string'||!/^https?:\\/\\//i.test(v[p])||/[\\r\\n]/.test(v[p])||seen.has(v[p]))throw Error('Invalid or duplicate Property');seen.add(v[p]);if(!['UP','DOWN'].includes(v[s]))throw Error('Invalid Status');if(typeof v[l]!=='string'||!v[l].trim()||/[\\[\\]*?:/\\\\]/.test(v[l]))throw Error('Invalid Log Sheet');}\n if(index>=rows.length)return{state:{index},more:false,url:'https://unused.invalid',site:{},count:index};const x=rows[index];return{state:{index:index+1},more:true,url:x.v[p],site:{property:x.v[p],status:x.v[s],logSheet:x.v[l],row:x.row,statusColumn:s},count:index+1};\n}\n" }, "n_896cf2f8b18d924f": { "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_f56b01b1985caa2b": { "name": "Evaluate the HTTP response", "imports": [], "source": "export default function assessDashboard({url,index,status,headers,previousState}) {\n const prior=previousState?.index===index?previousState.hops:0;if(!Number.isInteger(prior)||prior<0)throw Error('Invalid redirect state');\n if(Number.isInteger(status)&&status>=300&&status<400&&typeof headers?.location==='string'&&headers.location){if(prior>=21)throw Error('Redirect limit exceeded');let target;try{target=new URL(headers.location,url)}catch{throw Error('Invalid redirect URL')}if(!['http:','https:'].includes(target.protocol)||target.username||target.password)throw Error('Invalid redirect URL');return{state:{index,hops:prior+1,nextUrl:target.toString()},redirect:true,up:false};}\n if(!Number.isInteger(status)||status<100||status>599)throw Error('Invalid HTTP status');return{state:{index,hops:prior,nextUrl:''},redirect:false,up:status<400};\n}\n" }, "n_91f4e6ae38684ff2": { "name": "Prepare the observation", "imports": [], "source": "export default function dashboardFields({site,up,now}) {\n if(!site||typeof site.property!=='string'||!['UP','DOWN'].includes(site.status)||typeof site.logSheet!=='string'||!Number.isInteger(site.row)||!Number.isInteger(site.statusColumn)||typeof up!=='boolean')throw Error('Invalid site');\n const time=new Date(now);if(Number.isNaN(time.getTime()))throw Error('Invalid time');const stamp=time.toISOString(),status=up?'UP':'DOWN',event=`${status}_FROM_${site.status}`,quote=x=>\"'\"+x.replaceAll(\"'\",\"''\")+\"'\";let col=site.statusColumn+1,label='';while(col){col--;label=String.fromCharCode(65+col%26)+label;col=Math.floor(col/26)}\n const message=`From: n8n uptime\\nDate: ${stamp}\\n\\n${site.property} is ${status}`;return{logRange:quote(site.logSheet)+'!A:G',logValues:[[stamp,stamp.slice(0,7),site.property,event==='UP_FROM_UP',event==='DOWN_FROM_DOWN',event==='UP_FROM_DOWN',event==='DOWN_FROM_UP']],statusRange:`'dashboard'!${label}${site.row}`,statusValues:[[status]],alert:event!=='UP_FROM_UP',subject:`n8n uptime: ${site.property} is ${status}`,message};\n}\n" }, "n_50282b1b7045f278": { "name": "Confirm the status update", "imports": [], "source": "export default function confirm({updatedCells}){if(updatedCells!==1)throw Error('Status update was not confirmed');return{ok:true};}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }