{ "kind": "open-flow-workflow", "version": 1, "name": "Sync Trustpilot Reviews to Sheets", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_3413fbf3348148e0": { "name": "Daily at midnight UTC", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "0 0 * * *", "timezone": "UTC" } ] }, "n_e5d8e3819b168f58": { "name": "Run manually", "kind": "start", "inputDefs": [] }, "n_51913720c939f35e": { "inputs": { "businessUnitId": { "kind": "value", "value": "YOUR_BUSINESS_UNIT_ID" }, "spreadsheetId": { "kind": "value", "value": "YOUR_SPREADSHEET_ID" }, "generalSheet": { "kind": "value", "value": "Reviews" }, "helpfulSheet": { "kind": "value", "value": "HelpfulCrowd" }, "maxPages": { "kind": "value", "value": 100 } }, "name": "Configure the review export", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "businessUnitId" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "spreadsheetId" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "generalSheet" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "helpfulSheet" }, { "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 1000 }, "nullable": false, "handle": "maxPages" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "businessUnitId" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "spreadsheetId" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "generalSheet" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "helpfulSheet" }, { "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 1000 }, "nullable": false, "handle": "maxPages" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "generalRange" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "helpfulRange" } ], "name": "Configure the review export", "moduleId": "n_bfa1b88c89964857", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_a1203110f35556e7": { "inputs": { "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a1203110f35556e7", "output": "state" } ] }, "businessUnitId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "businessUnitId" } ] }, "maxPages": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "maxPages" } ] }, "response": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4f76e9ef2adde597", "output": "body" } ] } }, "name": "Collect review pages", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" }, { "jsonSchema": {}, "nullable": true, "handle": "response" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "businessUnitId" }, { "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 1000 }, "nullable": false, "handle": "maxPages" } ], "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": "url" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "reviews" } ], "name": "Collect review pages", "moduleId": "n_9a315cb9d71edb77", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_88e32dec252b9b85": { "inputs": {}, "name": "More review pages?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_a1203110f35556e7", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_a664bb954111cfcb": { "inputs": {}, "name": "Wait before the next request", "kind": "wait", "inputDefinitions": [], "prompt": "Wait before the next request", "autoContinue": { "kind": "after", "durationMs": 5000 } }, "n_4f76e9ef2adde597": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a1203110f35556e7", "output": "url" } ] }, "auth.token": { "kind": "unset" } }, "name": "Read a Trustpilot review page", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "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 a Trustpilot review page", "executor": { "method": "GET", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "none", "authentication": { "type": "apiKey", "name": "apikey", "in": "header" }, "responseFormat": "json", "statusPolicy": "success", "kind": "http-request" } } }, "n_0f3b483a42ed1cdb": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "spreadsheetId" } ] }, "range": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "generalRange" } ] }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "UNFORMATTED_VALUE" } }, "name": "Read the general 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 the general sheet", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_8d62a58ba4310533": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "spreadsheetId" } ] }, "range": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "helpfulRange" } ] }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "UNFORMATTED_VALUE" } }, "name": "Read the HelpfulCrowd 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 the HelpfulCrowd sheet", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_758f26abd53fc38b": { "inputs": { "reviews": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a1203110f35556e7", "output": "reviews" } ] }, "generalValues": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0f3b483a42ed1cdb", "output": "values" } ] }, "helpfulValues": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8d62a58ba4310533", "output": "values" } ] }, "generalSheet": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "generalSheet" } ] }, "helpfulSheet": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "helpfulSheet" } ] } }, "name": "Match reviews to sheet rows", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "reviews" }, { "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": "generalValues" }, { "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": "helpfulValues" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "generalSheet" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "helpfulSheet" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "tasks" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "generalSheet" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "helpfulSheet" } ], "name": "Match reviews to sheet rows", "moduleId": "n_5bfa3f3052734942", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_269c28d599952c19": { "inputs": { "tasks": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_758f26abd53fc38b", "output": "tasks" } ] }, "previous": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_269c28d599952c19", "output": "state" } ] }, "generalSheet": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_758f26abd53fc38b", "output": "generalSheet" } ] }, "helpfulSheet": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_758f26abd53fc38b", "output": "helpfulSheet" } ] }, "confirmation": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_9b2c5008e146ec25", "output": "totalUpdatedCells" } ] } }, "name": "Select the next review", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "tasks" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previous" }, { "jsonSchema": { "type": "integer", "description": "The total number of cells updated across all ranges." }, "nullable": true, "handle": "confirmation" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "generalSheet" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "helpfulSheet" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "item" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "generalRange" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "helpfulRange" } ], "name": "Select the next review", "moduleId": "n_f58ec266fb3b814c", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_f8e4bfc48ce65e81": { "inputs": {}, "name": "More reviews?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "yes", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_269c28d599952c19", "output": "more" } }, "operator": "isTrue" } ] } ] } ] }, "n_654d93c88da82e7a": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "spreadsheetId" } ] }, "range": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_269c28d599952c19", "output": "generalRange" } ] }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "UNFORMATTED_VALUE" } }, "name": "Recheck the general 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 general row", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_6a3e220e9c1d5acc": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "spreadsheetId" } ] }, "range": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_269c28d599952c19", "output": "helpfulRange" } ] }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueRenderOption": { "kind": "value", "value": "UNFORMATTED_VALUE" } }, "name": "Recheck the HelpfulCrowd 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 HelpfulCrowd row", "executor": { "kind": "connector", "action": "googlesheets.values_get" } } }, "n_0bf0b6e742023ee7": { "inputs": { "item": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_269c28d599952c19", "output": "item" } ] }, "generalChecked": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_654d93c88da82e7a", "output": "values" } ] }, "helpfulChecked": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_6a3e220e9c1d5acc", "output": "values" } ] }, "generalSheet": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "generalSheet" } ] }, "helpfulSheet": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "helpfulSheet" } ] } }, "name": "Prepare the review cells", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "item" }, { "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": "generalChecked" }, { "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": "helpfulChecked" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "generalSheet" }, { "jsonSchema": { "type": "string", "minLength": 1 }, "nullable": false, "handle": "helpfulSheet" } ], "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" } ], "name": "Prepare the review cells", "moduleId": "n_40b1e1cc6d460699", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_9b2c5008e146ec25": { "inputs": { "spreadsheetId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_51913720c939f35e", "output": "spreadsheetId" } ] }, "data": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_0bf0b6e742023ee7", "output": "data" } ] }, "valueInputOption": { "kind": "value", "value": "RAW" } }, "name": "Write both review rows", "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": "Write both review rows", "executor": { "kind": "connector", "action": "googlesheets.update_values_batch" } } } }, "edges": [ { "source": "n_3413fbf3348148e0", "target": "n_51913720c939f35e" }, { "source": "n_e5d8e3819b168f58", "target": "n_51913720c939f35e" }, { "source": "n_51913720c939f35e", "target": "n_a1203110f35556e7" }, { "source": "n_a1203110f35556e7", "target": "n_88e32dec252b9b85" }, { "source": "n_88e32dec252b9b85", "target": "n_a664bb954111cfcb", "sourceHandle": "yes" }, { "source": "n_a664bb954111cfcb", "target": "n_4f76e9ef2adde597", "sourceHandle": "continue" }, { "source": "n_4f76e9ef2adde597", "target": "n_a1203110f35556e7" }, { "source": "n_88e32dec252b9b85", "target": "n_0f3b483a42ed1cdb", "sourceHandle": "otherwise" }, { "source": "n_0f3b483a42ed1cdb", "target": "n_8d62a58ba4310533" }, { "source": "n_8d62a58ba4310533", "target": "n_758f26abd53fc38b" }, { "source": "n_758f26abd53fc38b", "target": "n_269c28d599952c19" }, { "source": "n_269c28d599952c19", "target": "n_f8e4bfc48ce65e81" }, { "source": "n_f8e4bfc48ce65e81", "target": "n_654d93c88da82e7a", "sourceHandle": "yes" }, { "source": "n_654d93c88da82e7a", "target": "n_6a3e220e9c1d5acc" }, { "source": "n_6a3e220e9c1d5acc", "target": "n_0bf0b6e742023ee7" }, { "source": "n_0bf0b6e742023ee7", "target": "n_9b2c5008e146ec25" }, { "source": "n_9b2c5008e146ec25", "target": "n_269c28d599952c19" } ] } }, "modules": { "n_bfa1b88c89964857": { "name": "Configure the review export", "imports": [], "source": "export default function config({businessUnitId,spreadsheetId,generalSheet,helpfulSheet,maxPages}){if(!Number.isInteger(maxPages)||maxPages<1||maxPages>1000)throw Error('Set maxPages between 1 and 1000');if(typeof businessUnitId!=='string'||!/^[0-9a-f]{24}$/i.test(businessUnitId))throw Error('Configure a Trustpilot business unit ID');for(const [key,value] of Object.entries({spreadsheetId,generalSheet,helpfulSheet}))if(typeof value!=='string'||!value.trim()||value.startsWith('YOUR_'))throw Error('Configure '+key);if(generalSheet===helpfulSheet||/[!\\r\\n]/.test(generalSheet+helpfulSheet))throw Error('Choose distinct sheet names');const q=x=>\"'\"+x.replace(/'/g,\"''\")+\"'\";return{businessUnitId,spreadsheetId,generalSheet,helpfulSheet,maxPages,generalRange:q(generalSheet)+'!A:ZZZ',helpfulRange:q(helpfulSheet)+'!A:ZZZ'}}\n" }, "n_9a315cb9d71edb77": { "name": "Collect review pages", "imports": [], "source": "export default function collect({previous,response,businessUnitId,maxPages}){const s=previous??{reviews:[],count:0,token:null,seen:[],started:false};const base='https://api.trustpilot.com/v1/business-units/'+businessUnitId+'/all-reviews';if(!s.started)return{state:{...s,started:true},more:true,url:base,reviews:[]};if(!response||typeof response!=='object'||!Array.isArray(response.reviews))throw Error('Invalid Trustpilot response');const reviews=[...s.reviews,...response.reviews],token=response.nextPageToken;if(token==null)return{state:{...s,reviews},more:false,url:base,reviews};if(typeof token!=='string'||!token||s.seen.includes(token))throw Error('Invalid Trustpilot page token');if(s.count+1>=maxPages)return{state:{...s,reviews},more:false,url:base,reviews};return{state:{...s,reviews,count:s.count+1,token,seen:[...s.seen,token]},more:true,url:base+'?pageToken='+encodeURIComponent(token),reviews:[]}}\n" }, "n_5bfa3f3052734942": { "name": "Match reviews to sheet rows", "imports": [], "source": "export default function plan({reviews,generalValues,helpfulValues,generalSheet,helpfulSheet}){if(!Array.isArray(reviews))throw Error('Invalid reviews');const gf=['Date','Author','Body','Heading','Rating','Location','review_id'],hf=['title','comment','rating*','status*','verified','feedback*','review_id','product_id*','review_date*','customer_email','customer_name*'];function sheet(values,fields){if(!Array.isArray(values)||!values.length||!Array.isArray(values[0]))throw Error('Sheet needs headers');const header=values[0];if(fields.some(x=>header.indexOf(x)<0||header.indexOf(x)!==header.lastIndexOf(x)))throw Error('Sheet needs unique required headers');const indexes=Object.fromEntries(fields.map(x=>[x,header.indexOf(x)])),map=new Map();for(let i=1;i5||typeof r.isVerified!=='boolean'||typeof r.createdAt!=='string'||!/^\\d{4}-\\d{2}-\\d{2}T/.test(r.createdAt)||!Number.isFinite(Date.parse(r.createdAt)))throw Error('Invalid review fields');if(!r.consumer||typeof r.consumer.displayName!=='string'||!r.consumer.displayName.trim())throw Error('Missing reviewer name');for(const k of ['title','text'])if(r[k]!=null&&typeof r[k]!=='string')throw Error('Invalid review text');if(r.consumer.displayLocation!=null&&typeof r.consumer.displayLocation!=='string')throw Error('Invalid reviewer location');const get=(x)=>x.map.get(r.id)??{index:x.next++,raw:[]},gt=get(g),ht=get(h),title=r.title??'',text=r.text??'',name=r.consumer.displayName;tasks.push({id:r.id,general:{...gt,indexes:g.indexes,fields:{Date:r.createdAt,Author:name,Body:text,Heading:title,Rating:r.stars,Location:r.consumer.displayLocation??'',review_id:r.id}},helpful:{...ht,indexes:h.indexes,fields:{title,comment:'','rating*':r.stars,'status*':'published',verified:r.isVerified?'yes':'no','feedback*':text,review_id:r.id,'product_id*':'','review_date*':r.createdAt.slice(0,10).replace(/-/g,'/'),customer_email:'','customer_name*':name}}})}return{tasks,generalSheet,helpfulSheet}}\n" }, "n_f58ec266fb3b814c": { "name": "Select the next review", "imports": [], "source": "export default function next({tasks,previous,confirmation,generalSheet,helpfulSheet}){const s=previous??{index:0,waiting:false};if(s.waiting){if(confirmation!==18)throw Error('Both sheet updates were not confirmed');s.index++;s.waiting=false}if(s.index>=tasks.length)return{state:s,more:false,item:{},generalRange:\"'unused'!A1:ZZZ1\",helpfulRange:\"'unused'!A1:ZZZ1\"};const item=tasks[s.index];if(!item||!Number.isSafeInteger(item.general?.index)||!Number.isSafeInteger(item.helpful?.index))throw Error('Invalid review task');const range=(sheet,index)=>\"'\"+sheet.replace(/'/g,\"''\")+\"'!A\"+index+':ZZZ'+index;return{state:{...s,waiting:true},more:true,item,generalRange:range(generalSheet,item.general.index),helpfulRange:range(helpfulSheet,item.helpful.index)}}\n" }, "n_40b1e1cc6d460699": { "name": "Prepare the review cells", "imports": [], "source": "export default function update({item,generalChecked,helpfulChecked,generalSheet,helpfulSheet}){const checked=(value,raw)=>Array.isArray(value)&&value.length<=1&&JSON.stringify(value[0]??[])===JSON.stringify(raw);if(!checked(generalChecked,item.general.raw)||!checked(helpfulChecked,item.helpful.raw))throw Error('Review sheet row changed');const col=n=>{let x=n+1,s='';while(x){x--;s=String.fromCharCode(65+x%26)+s;x=Math.floor(x/26)}return s},data=[];for(const [sheet,part]of [[generalSheet,item.general],[helpfulSheet,item.helpful]]){let q=\"'\"+sheet.replace(/'/g,\"''\")+\"'\";for(const [key,value]of Object.entries(part.fields))data.push({range:q+'!'+col(part.indexes[key])+part.index,values:[[value]]})}return{data}}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }