{ "kind": "open-flow-workflow", "version": 1, "name": "Track Amazon Prices from Sheets", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_af46827db2bf1313": { "name": "Every hour", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "0 * * * *", "timezone": "UTC" } ] }, "n_e03bdeb2d1a9bb62": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_SPREADSHEET_ID" }, "productsSheet": { "kind": "value", "value": "Products to Monitor" }, "historySheet": { "kind": "value", "value": "Price History" }, "recipient": { "kind": "value", "value": "YOUR_ALERT_EMAIL" } }, "name": "Configure the sheets and alerts", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "spreadsheetId" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "productsSheet" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "historySheet" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "recipient" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "spreadsheetId" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "productsSheet" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "productsRange" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "historyRange" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "recipient" } ], "name": "Configure the sheets and alerts", "moduleId": "n_b1f2969fe8d4d0f1", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_0a6d7556baad9383": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e03bdeb2d1a9bb62", "output": "spreadsheetId" } ] }, "range": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e03bdeb2d1a9bb62", "output": "productsRange" } ] }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "UNFORMATTED_VALUE" } }, "name": "Read monitored products", "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 monitored products", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_eff0a14634d0421f": { "inputs": { "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0a6d7556baad9383", "output": "values" } ] }, "sheetName": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e03bdeb2d1a9bb62", "output": "productsSheet" } ] } }, "name": "Validate product 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" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "sheetName" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "items" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "sheetName" } ], "name": "Validate product rows", "moduleId": "n_109d828394aab8fb", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_2142498952d31c16": { "inputs": { "items": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_eff0a14634d0421f", "output": "items" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2142498952d31c16", "output": "state" } ] }, "sheetName": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_eff0a14634d0421f", "output": "sheetName" } ] } }, "name": "Select the next product", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "items" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "sheetName" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "asin" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "url" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "item" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "rowRange" } ], "name": "Select the next product", "moduleId": "n_e72fa78b15b84d41", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_8865d682d2ef3397": { "inputs": {}, "name": "More products?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_2142498952d31c16", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_7a4339ef9145a8a6": { "inputs": { "url": { "kind": "value", "value": "https://proxy.scrapeops.io/v1/" }, "query.url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2142498952d31c16", "output": "url" } ] }, "query.auto_extract": { "kind": "value", "value": "amazon" }, "auth.token": { "kind": "unset" } }, "name": "Read the Amazon product", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": { "anyOf": [ { "type": [ "string", "number", "boolean" ] }, { "type": "array", "items": { "type": [ "string", "number", "boolean" ] } } ] }, "nullable": true, "handle": "query.url" }, { "jsonSchema": { "anyOf": [ { "type": [ "string", "number", "boolean" ] }, { "type": "array", "items": { "type": [ "string", "number", "boolean" ] } } ] }, "nullable": true, "handle": "query.auto_extract" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "auth.token" } ], "outputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" } ], "name": "Read the Amazon product", "executor": { "method": "GET", "query": { "mode": "fields", "names": [ "url", "auto_extract" ] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "none", "authentication": { "type": "apiKey", "name": "api_key", "in": "query" }, "responseFormat": "json", "statusPolicy": "success", "kind": "http-request" } } }, "n_19cd43802e378e9e": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_7a4339ef9145a8a6", "output": "body" } ] }, "asin": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2142498952d31c16", "output": "asin" } ] } }, "name": "Validate the product price", "kind": "task", "task": { "inputs": [ { "jsonSchema": {}, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "asin" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "valid" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "product" } ], "name": "Validate the product price", "moduleId": "n_a696b9b88d170dcb", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_cfddce972cc51858": { "inputs": {}, "name": "Is a price available?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_19cd43802e378e9e", "output": "valid" } }, "operator": "isTrue" } ] } ] } ] }, "n_24910ebfe7cebebd": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e03bdeb2d1a9bb62", "output": "spreadsheetId" } ] }, "range": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2142498952d31c16", "output": "rowRange" } ] }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "UNFORMATTED_VALUE" } }, "name": "Recheck the product row", "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": "Recheck the product row", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_8c561cb0e3985b60": { "inputs": { "item": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2142498952d31c16", "output": "item" } ] }, "product": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_19cd43802e378e9e", "output": "product" } ] }, "checked": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_24910ebfe7cebebd", "output": "values" } ] }, "sheetName": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_eff0a14634d0421f", "output": "sheetName" } ] } }, "name": "Prepare the price update", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "item" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "product" }, { "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": "checked" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "sheetName" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "range": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to write to." }, "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 to write into the range." }, "majorDimension": { "type": "string", "enum": [ "ROWS", "COLUMNS" ], "description": "The major dimension of the values array." } }, "required": [ "range", "values" ], "additionalProperties": false, "description": "A batch values update item." }, "minItems": 1, "description": "The list of value ranges to update." }, "nullable": false, "handle": "data" }, { "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": "history" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "alert" }, { "jsonSchema": { "type": "string", "description": "Email subject line." }, "nullable": true, "handle": "subject" }, { "jsonSchema": { "type": "string", "description": "Email body content." }, "nullable": true, "handle": "body" } ], "name": "Prepare the price update", "moduleId": "n_ae7b499cc981a3de", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_063034b376b72a13": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e03bdeb2d1a9bb62", "output": "spreadsheetId" } ] }, "data": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8c561cb0e3985b60", "output": "data" } ] }, "valueInputOption": { "kind": "value", "value": "RAW" } }, "name": "Update the product cells", "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 list of value ranges to update.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "range": { "type": "string", "minLength": 1, "description": "The A1 notation of the range to write to." }, "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 to write into the range." }, "majorDimension": { "type": "string", "enum": [ "ROWS", "COLUMNS" ], "description": "The major dimension of the values array." } }, "required": [ "range", "values" ], "additionalProperties": false, "description": "A batch values update item." }, "minItems": 1, "description": "The list of value ranges to update." }, "nullable": false, "handle": "data" }, { "description": "How the input data should be interpreted.", "jsonSchema": { "type": "string", "enum": [ "INPUT_VALUE_OPTION_UNSPECIFIED", "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 } ], "outputs": [ { "description": "The unique ID of the spreadsheet.", "jsonSchema": { "type": "string", "description": "The unique ID of the spreadsheet." }, "nullable": false, "handle": "spreadsheetId" }, { "description": "The total number of rows updated across all ranges.", "jsonSchema": { "type": "integer", "description": "The total number of rows updated across all ranges." }, "nullable": false, "handle": "totalUpdatedRows" }, { "description": "The total number of columns updated across all ranges.", "jsonSchema": { "type": "integer", "description": "The total number of columns updated across all ranges." }, "nullable": false, "handle": "totalUpdatedColumns" }, { "description": "The total number of cells updated across all ranges.", "jsonSchema": { "type": "integer", "description": "The total number of cells updated across all ranges." }, "nullable": false, "handle": "totalUpdatedCells" }, { "description": "The per-range update responses.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "updatedRange": { "type": "string", "description": "The A1 notation of the range that was updated." }, "updatedRows": { "type": "integer", "description": "The number of rows that were updated." }, "updatedColumns": { "type": "integer", "description": "The number of columns that were updated." }, "updatedCells": { "type": "integer", "description": "The number of cells that were updated." }, "updatedData": { "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." } }, "required": [ "updatedRange", "updatedRows", "updatedColumns", "updatedCells" ], "additionalProperties": false, "description": "The batch update item schema." }, "description": "The per-range update responses." }, "nullable": false, "handle": "responses" } ], "name": "Update the product cells", "executor": { "kind": "connector", "action": "googlesheets.update_values_batch" } } }, "n_073fbf84ff2df24d": { "inputs": { "totalUpdatedCells": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_063034b376b72a13", "output": "totalUpdatedCells" } ] }, "history": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8c561cb0e3985b60", "output": "history" } ] }, "alert": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8c561cb0e3985b60", "output": "alert" } ] }, "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8c561cb0e3985b60", "output": "subject" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8c561cb0e3985b60", "output": "body" } ] } }, "name": "Confirm the product update", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "integer", "description": "The total number of cells updated across all ranges." }, "nullable": false, "handle": "totalUpdatedCells" }, { "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": "history" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "alert" }, { "jsonSchema": { "type": "string", "description": "Email subject line." }, "nullable": true, "handle": "subject" }, { "jsonSchema": { "type": "string", "description": "Email body content." }, "nullable": true, "handle": "body" } ], "outputs": [ { "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": "history" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "alert" }, { "jsonSchema": { "type": "string", "description": "Email subject line." }, "nullable": true, "handle": "subject" }, { "jsonSchema": { "type": "string", "description": "Email body content." }, "nullable": true, "handle": "body" } ], "name": "Confirm the product update", "moduleId": "n_77e51c032040c48b", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_af0a17c7cc5781e4": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e03bdeb2d1a9bb62", "output": "spreadsheetId" } ] }, "range": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e03bdeb2d1a9bb62", "output": "historyRange" } ] }, "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_073fbf84ff2df24d", "output": "history" } ] }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueInputOption": { "kind": "value", "value": "RAW" }, "insertDataOption": { "kind": "value", "value": "INSERT_ROWS" } }, "name": "Append the price history", "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": "Append the price history", "executor": { "kind": "connector", "action": "googlesheets.spreadsheets_values_append" } } }, "n_d9d1955e8f07dd87": { "inputs": { "updatedRows": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_af0a17c7cc5781e4", "output": "updatedRows" } ] }, "alert": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_073fbf84ff2df24d", "output": "alert" } ] }, "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_073fbf84ff2df24d", "output": "subject" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_073fbf84ff2df24d", "output": "body" } ] } }, "name": "Confirm the history entry", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "integer", "description": "The number of rows that were updated." }, "nullable": false, "handle": "updatedRows" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "alert" }, { "jsonSchema": { "type": "string", "description": "Email subject line." }, "nullable": true, "handle": "subject" }, { "jsonSchema": { "type": "string", "description": "Email body content." }, "nullable": true, "handle": "body" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "alert" }, { "jsonSchema": { "type": "string", "description": "Email subject line." }, "nullable": true, "handle": "subject" }, { "jsonSchema": { "type": "string", "description": "Email body content." }, "nullable": true, "handle": "body" } ], "name": "Confirm the history entry", "moduleId": "n_9f4ce0c5ae5279f2", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_77ff6e4f791dd6ce": { "inputs": {}, "name": "Threshold crossed?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_d9d1955e8f07dd87", "output": "alert" } }, "operator": "isTrue" } ] } ] } ] }, "n_79980128607a0888": { "inputs": { "to": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e03bdeb2d1a9bb62", "output": "recipient" } ] }, "subject": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d9d1955e8f07dd87", "output": "subject" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_d9d1955e8f07dd87", "output": "body" } ] }, "isHtml": { "kind": "value", "value": false } }, "name": "Email the price 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 price alert", "executor": { "kind": "connector", "action": "gmail.send_email" } } } }, "edges": [ { "source": "n_af46827db2bf1313", "target": "n_e03bdeb2d1a9bb62" }, { "source": "n_e03bdeb2d1a9bb62", "target": "n_0a6d7556baad9383" }, { "source": "n_0a6d7556baad9383", "target": "n_eff0a14634d0421f" }, { "source": "n_eff0a14634d0421f", "target": "n_2142498952d31c16" }, { "source": "n_2142498952d31c16", "target": "n_8865d682d2ef3397" }, { "source": "n_8865d682d2ef3397", "target": "n_7a4339ef9145a8a6", "sourceHandle": "yes" }, { "source": "n_7a4339ef9145a8a6", "target": "n_19cd43802e378e9e" }, { "source": "n_19cd43802e378e9e", "target": "n_cfddce972cc51858" }, { "source": "n_cfddce972cc51858", "target": "n_24910ebfe7cebebd", "sourceHandle": "yes" }, { "source": "n_cfddce972cc51858", "target": "n_2142498952d31c16", "sourceHandle": "otherwise" }, { "source": "n_24910ebfe7cebebd", "target": "n_8c561cb0e3985b60" }, { "source": "n_8c561cb0e3985b60", "target": "n_063034b376b72a13" }, { "source": "n_063034b376b72a13", "target": "n_073fbf84ff2df24d" }, { "source": "n_073fbf84ff2df24d", "target": "n_af0a17c7cc5781e4" }, { "source": "n_af0a17c7cc5781e4", "target": "n_d9d1955e8f07dd87" }, { "source": "n_d9d1955e8f07dd87", "target": "n_77ff6e4f791dd6ce" }, { "source": "n_77ff6e4f791dd6ce", "target": "n_79980128607a0888", "sourceHandle": "yes" }, { "source": "n_77ff6e4f791dd6ce", "target": "n_2142498952d31c16", "sourceHandle": "otherwise" }, { "source": "n_79980128607a0888", "target": "n_2142498952d31c16" } ] } }, "modules": { "n_b1f2969fe8d4d0f1": { "name": "Configure the sheets and alerts", "imports": [], "source": "export default function config({spreadsheetId,productsSheet,historySheet,recipient}){for(const [k,v]of Object.entries({spreadsheetId,productsSheet,historySheet,recipient}))if(typeof v!=='string'||!v.trim()||v.startsWith('YOUR_'))throw Error('Configure '+k);if(productsSheet===historySheet||/[!\\r\\n]/.test(productsSheet+historySheet)||!/^[^\\s@<>]+@[^\\s@<>]+\\.[^\\s@<>]+$/.test(recipient)||/[\\x00-\\x1f\\x7f]/.test(recipient))throw Error('Invalid sheet names or recipient');const quote=s=>\"'\"+s.replace(/'/g,\"''\")+\"'\";return{spreadsheetId,productsSheet,recipient,productsRange:quote(productsSheet)+'!A:ZZZ',historyRange:quote(historySheet)+'!A:C'}}\n" }, "n_109d828394aab8fb": { "name": "Validate product rows", "imports": [], "source": "export default function rows({values,sheetName}){if(typeof sheetName!=='string'||!sheetName.trim()||sheetName.includes('!'))throw Error('Configure the products sheet name');if(!Array.isArray(values))throw Error('Invalid sheet data');if(!values.length)return{items:[],sheetName};const h=values[0],required=['asin','name','pricing','product_url','alert_status','last_updated','price_change','average_rating','percent_change','alert_threshold_low','alert_threshold_high'];if(!Array.isArray(h)||required.some(k=>h.indexOf(k)<0||h.lastIndexOf(k)!==h.indexOf(k)))throw Error('Sheet requires unique product headers');const indexes=Object.fromEntries(required.map(k=>[k,h.indexOf(k)])),items=[],ids=new Set();for(let i=1;ix!==null&&x!==''))continue;const asin=String(r[indexes.asin]??'').trim();if(!/^[A-Z0-9]{10}$/.test(asin)||ids.has(asin))throw Error('Each product requires a unique 10-character ASIN');ids.add(asin);const low=Number(r[indexes.alert_threshold_low]),high=Number(r[indexes.alert_threshold_high]);if(r[indexes.alert_threshold_low]===''||r[indexes.alert_threshold_low]==null||r[indexes.alert_threshold_high]===''||r[indexes.alert_threshold_high]==null||!Number.isFinite(low)||!Number.isFinite(high)||low>=high)throw Error('Invalid alert thresholds');const prior=r[indexes.pricing];if(prior!==''&&prior!=null&&(!Number.isFinite(Number(prior))||Number(prior)<0))throw Error('Invalid previous price');items.push({asin,index:i+1,raw:r,indexes,low,high,prior:prior==null||prior===''?null:Number(prior)})}return{items,sheetName:sheetName.trim()}}\n" }, "n_e72fa78b15b84d41": { "name": "Select the next product", "imports": [], "source": "export default function next({items,previous,sheetName}){const index=previous?.index??0;if(index>=items.length)return{state:{index},more:false,asin:'UNUSED0000',url:'https://www.amazon.com/dp/UNUSED0000',item:{},rowRange:\"'unused'!A1:ZZZ1\"};const item=items[index],name=\"'\"+sheetName.replace(/'/g,\"''\")+\"'\";return{state:{index:index+1},more:true,asin:item.asin,url:'https://www.amazon.com/dp/'+item.asin,item,rowRange:name+'!A'+item.index+':ZZZ'+item.index}}\n" }, "n_a696b9b88d170dcb": { "name": "Validate the product price", "imports": [], "source": "export default function parse({body,asin}){if(!body||typeof body!=='object'||!body.data||typeof body.data!=='object')throw Error('Invalid ScrapeOps response');const d=body.data;if(typeof asin!=='string'||!/^[A-Z0-9]{10}$/.test(asin)||d.product_information?.ASIN!==asin)throw Error('Returned product ASIN does not match request');if(d.pricing===null||d.pricing===undefined||d.pricing==='')return{valid:false,product:{}};if(typeof d.pricing!=='string'||!/^\\$?\\s*(?:\\d{1,3}(?:,\\d{3})+|\\d+)(?:\\.\\d{1,2})?$/.test(d.pricing.trim()))throw Error('Ambiguous US dollar price');const price=Number(d.pricing.replace(/[$,\\s]/g,''));if(!Number.isFinite(price)||price<=0)return{valid:false,product:{}};if(typeof d.name!=='string'||!d.name.trim()||d.name.length>300||/[\\x00-\\x1f\\x7f]/.test(d.name))throw Error('Missing product name');const rating=d.average_rating;if(rating!=null&&rating!==''&&(!Number.isFinite(Number(rating))||Number(rating)<0||Number(rating)>5))throw Error('Invalid product rating');return{valid:true,product:{price,name:d.name.trim(),rating:rating==null?'':rating}}}\n" }, "n_ae7b499cc981a3de": { "name": "Prepare the price update", "imports": [], "source": "export default function plan({item,product,checked,sheetName}){if(!Array.isArray(checked)||checked.length!==1||JSON.stringify(checked[0])!==JSON.stringify(item.raw))throw Error('Product row changed during lookup');const q=\"'\"+sheetName.replace(/'/g,\"''\")+\"'\",col=n=>{let x=n+1,s='';while(x){x--;s=String.fromCharCode(65+x%26)+s;x=Math.floor(x/26)}return s},at=k=>q+'!'+col(item.indexes[k])+item.index,delta=item.prior===null?0:product.price-item.prior,ratio=item.prior===null||item.prior===0?0:delta/item.prior,alert=ratio>item.high?'High':ratio({range:at(k),values:[[v]]}));return{data,history:[[item.asin,product.price,timestamp]],alert:!!alert,subject:'Amazon price alert: '+product.name,body:'ASIN: '+item.asin+'\\nPrevious price: '+(item.prior===null?'not available':item.prior)+'\\nCurrent price: '+product.price+'\\nChange: '+delta+' ('+(ratio*100).toFixed(2)+'%)\\n'+url}}\n" }, "n_77e51c032040c48b": { "name": "Confirm the product update", "imports": [], "source": "export default function confirm({totalUpdatedCells,history,alert,subject,body}){if(totalUpdatedCells!==8)throw Error('Sheet update not fully confirmed');return{history,alert,subject,body}}\n" }, "n_9f4ce0c5ae5279f2": { "name": "Confirm the history entry", "imports": [], "source": "export default function confirm({updatedRows,alert,subject,body}){if(updatedRows!==1)throw Error('History append not confirmed');return{alert,subject,body}}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }