{ "kind": "open-flow-workflow", "version": 1, "name": "Route Course Feedback to Google Sheets", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_e47e8bb12d091770": { "name": "Receive course feedback", "kind": "integration", "config": { "formId": { "kind": "value", "value": "YOUR_TYPEFORM_FORM_ID" } }, "definition": { "configInputs": [ { "jsonSchema": { "type": "string", "minLength": 1, "maxLength": 128, "title": "Form ID" }, "nullable": false, "handle": "formId" } ], "definitionVersion": 2, "description": "Receives complete Typeform submissions through an automatically managed signed webhook.", "displayName": "Completed Form Response", "key": "typeform.on_form_response", "name": "on_form_response", "outputs": [ { "jsonSchema": { "type": "object", "properties": { "eventId": { "type": "string" }, "form_id": { "type": "string" }, "token": { "type": "string" }, "submitted_at": { "type": "string" }, "answers": { "type": "array", "items": { "type": "object" } }, "definition": { "type": "object" } }, "required": [ "eventId", "form_id", "token", "submitted_at", "answers" ] }, "nullable": false, "handle": "response" } ], "provider": "typeform", "type": "integration", "endpoint": { "body": { "allowArray": false, "allowEmpty": false, "formats": [ "json" ] }, "methods": [ "POST" ], "successStatus": 200 } } }, "n_a80257e9407c4742": { "inputs": { "response": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e47e8bb12d091770", "output": "response" } ] } }, "name": "Read the course answers", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "response" } ], "outputs": [ { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "positive" }, { "jsonSchema": { "type": "number" }, "nullable": false, "handle": "usefulness" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "opinion" }, { "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" } ], "name": "Read the course answers", "moduleId": "n_0c7b1293cae12058", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_3e1f542a16b38e81": { "inputs": {}, "name": "Usefulness at least 3?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "positive", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_a80257e9407c4742", "output": "positive" } }, "operator": "isTrue" } ] } ] } ] }, "n_a55d40fcedfec6ce": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_POSITIVE_SPREADSHEET_ID" }, "range": { "kind": "value", "value": "'positive_feedback'!A:B" }, "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a80257e9407c4742", "output": "values" } ] }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueInputOption": { "kind": "value", "value": "RAW" }, "insertDataOption": { "kind": "value", "value": "INSERT_ROWS" } }, "name": "Append positive feedback", "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 positive feedback", "executor": { "kind": "connector", "action": "googlesheets.spreadsheets_values_append" } } }, "n_b7a79310d2a20e8b": { "inputs": { "spreadsheetId": { "kind": "value", "value": "YOUR_NEGATIVE_SPREADSHEET_ID" }, "range": { "kind": "value", "value": "'negative_feedback'!A:B" }, "values": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a80257e9407c4742", "output": "values" } ] }, "majorDimension": { "kind": "value", "value": "ROWS" }, "valueInputOption": { "kind": "value", "value": "RAW" }, "insertDataOption": { "kind": "value", "value": "INSERT_ROWS" } }, "name": "Append negative feedback", "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 negative feedback", "executor": { "kind": "connector", "action": "googlesheets.spreadsheets_values_append" } } }, "n_ac4513238099e954": { "inputs": { "updatedRows": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_a55d40fcedfec6ce", "output": "updatedRows" } ] } }, "name": "Confirm positive append", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "integer", "description": "The number of rows that were updated." }, "nullable": false, "handle": "updatedRows" } ], "outputs": [], "name": "Confirm positive append", "moduleId": "n_3517d98f39677eae", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_d0812887d1911913": { "inputs": { "updatedRows": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_b7a79310d2a20e8b", "output": "updatedRows" } ] } }, "name": "Confirm negative append", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "integer", "description": "The number of rows that were updated." }, "nullable": false, "handle": "updatedRows" } ], "outputs": [], "name": "Confirm negative append", "moduleId": "n_61f974738ffdf9da", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } } }, "edges": [ { "source": "n_e47e8bb12d091770", "target": "n_a80257e9407c4742" }, { "source": "n_a80257e9407c4742", "target": "n_3e1f542a16b38e81" }, { "source": "n_3e1f542a16b38e81", "target": "n_a55d40fcedfec6ce", "sourceHandle": "positive" }, { "source": "n_3e1f542a16b38e81", "target": "n_b7a79310d2a20e8b", "sourceHandle": "otherwise" }, { "source": "n_a55d40fcedfec6ce", "target": "n_ac4513238099e954" }, { "source": "n_b7a79310d2a20e8b", "target": "n_d0812887d1911913" } ] } }, "modules": { "n_0c7b1293cae12058": { "name": "Read the course answers", "imports": [], "source": "export default function feedback({response}){const fields=response?.definition?.fields,answers=response?.answers;if(!Array.isArray(fields)||!Array.isArray(answers))throw Error('Expected a complete Typeform response');const field=title=>{const f=fields.filter(x=>x?.title===title);if(f.length!==1||typeof f[0].id!=='string'||!f[0].id)throw Error('Expected one '+title+' question');return f[0].id};const ratingId=field('How useful was the course?'),opinionId=field('Your opinion on the course:'),ratings=answers.filter(x=>x?.field?.id===ratingId),opinions=answers.filter(x=>x?.field?.id===opinionId);if(ratings.length!==1||ratings[0].type!=='number'||typeof ratings[0].number!=='number'||!Number.isFinite(ratings[0].number)||opinions.length>1)throw Error('Invalid course feedback');if(opinions.length&&!(opinions[0].type==='text'&&typeof opinions[0].text==='string'))throw Error('Invalid opinion');const usefulness=ratings[0].number,opinion=opinions[0]?.text??'';return {positive:usefulness>=3,usefulness,opinion,values:[[usefulness,opinion]]}}\n" }, "n_3517d98f39677eae": { "name": "Confirm positive append", "imports": [], "source": "export default function confirm({updatedRows}){if(updatedRows!==1)throw Error('Sheet append not confirmed');return {}}\n" }, "n_61f974738ffdf9da": { "name": "Confirm negative append", "imports": [], "source": "export default function confirm({updatedRows}){if(updatedRows!==1)throw Error('Sheet append not confirmed');return {}}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }