feat(integration): ✨ Add Google Sheets integration
This commit is contained in:
13
packages/bot-engine/src/services/inputs.ts
Normal file
13
packages/bot-engine/src/services/inputs.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { ChoiceInputStep, ChoiceItem, Table, Target } from 'models'
|
||||
|
||||
export const getSingleChoiceTargetId = (
|
||||
currentStep: ChoiceInputStep,
|
||||
choiceItems: Table<ChoiceItem>,
|
||||
answerContent?: string
|
||||
): Target | undefined => {
|
||||
const itemId = currentStep.options.itemIds.find(
|
||||
(itemId) => choiceItems.byId[itemId].content === answerContent
|
||||
)
|
||||
if (!itemId) throw new Error('itemId should exist')
|
||||
return choiceItems.byId[itemId].target ?? currentStep.target
|
||||
}
|
121
packages/bot-engine/src/services/integration.ts
Normal file
121
packages/bot-engine/src/services/integration.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import {
|
||||
IntegrationStep,
|
||||
IntegrationStepType,
|
||||
GoogleSheetsStep,
|
||||
GoogleSheetsAction,
|
||||
GoogleSheetsInsertRowOptions,
|
||||
Variable,
|
||||
Table,
|
||||
GoogleSheetsUpdateRowOptions,
|
||||
Cell,
|
||||
GoogleSheetsGetOptions,
|
||||
} from 'models'
|
||||
import { stringify } from 'qs'
|
||||
import { sendRequest } from 'utils'
|
||||
import { parseVariables } from './variable'
|
||||
|
||||
export const executeIntegration = (
|
||||
step: IntegrationStep,
|
||||
variables: Table<Variable>,
|
||||
updateVariableValue: (variableId: string, value: string) => void
|
||||
) => {
|
||||
switch (step.type) {
|
||||
case IntegrationStepType.GOOGLE_SHEETS:
|
||||
return executeGoogleSheetIntegration(step, variables, updateVariableValue)
|
||||
}
|
||||
}
|
||||
|
||||
const executeGoogleSheetIntegration = async (
|
||||
step: GoogleSheetsStep,
|
||||
variables: Table<Variable>,
|
||||
updateVariableValue: (variableId: string, value: string) => void
|
||||
) => {
|
||||
if (!step.options) return step.target
|
||||
switch (step.options?.action) {
|
||||
case GoogleSheetsAction.INSERT_ROW:
|
||||
await insertRowInGoogleSheets(step.options, variables)
|
||||
break
|
||||
case GoogleSheetsAction.UPDATE_ROW:
|
||||
await updateRowInGoogleSheets(step.options, variables)
|
||||
break
|
||||
case GoogleSheetsAction.GET:
|
||||
await getRowFromGoogleSheets(step.options, variables, updateVariableValue)
|
||||
break
|
||||
}
|
||||
return step.target
|
||||
}
|
||||
|
||||
const insertRowInGoogleSheets = async (
|
||||
options: GoogleSheetsInsertRowOptions,
|
||||
variables: Table<Variable>
|
||||
) => {
|
||||
if (!options.cellsToInsert) return
|
||||
return sendRequest({
|
||||
url: `http://localhost:3001/api/integrations/google-sheets/spreadsheets/${options.spreadsheetId}/sheets/${options.sheetId}`,
|
||||
method: 'POST',
|
||||
body: {
|
||||
credentialsId: options.credentialsId,
|
||||
values: parseCellValues(options.cellsToInsert, variables),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const updateRowInGoogleSheets = async (
|
||||
options: GoogleSheetsUpdateRowOptions,
|
||||
variables: Table<Variable>
|
||||
) => {
|
||||
if (!options.cellsToUpsert || !options.referenceCell) return
|
||||
return sendRequest({
|
||||
url: `http://localhost:3001/api/integrations/google-sheets/spreadsheets/${options.spreadsheetId}/sheets/${options.sheetId}`,
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
credentialsId: options.credentialsId,
|
||||
values: parseCellValues(options.cellsToUpsert, variables),
|
||||
referenceCell: {
|
||||
column: options.referenceCell.column,
|
||||
value: parseVariables(options.referenceCell.value ?? '', variables),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const getRowFromGoogleSheets = async (
|
||||
options: GoogleSheetsGetOptions,
|
||||
variables: Table<Variable>,
|
||||
updateVariableValue: (variableId: string, value: string) => void
|
||||
) => {
|
||||
if (!options.referenceCell || !options.cellsToExtract) return
|
||||
const queryParams = stringify(
|
||||
{
|
||||
credentialsId: options.credentialsId,
|
||||
referenceCell: {
|
||||
column: options.referenceCell.column,
|
||||
value: parseVariables(options.referenceCell.value ?? '', variables),
|
||||
},
|
||||
columns: options.cellsToExtract.allIds.map(
|
||||
(id) => options.cellsToExtract?.byId[id].column
|
||||
),
|
||||
},
|
||||
{ indices: false }
|
||||
)
|
||||
const { data } = await sendRequest<{ [key: string]: string }>({
|
||||
url: `http://localhost:3001/api/integrations/google-sheets/spreadsheets/${options.spreadsheetId}/sheets/${options.sheetId}?${queryParams}`,
|
||||
method: 'GET',
|
||||
})
|
||||
if (!data) return
|
||||
options.cellsToExtract.allIds.forEach((cellId) => {
|
||||
const cell = options.cellsToExtract?.byId[cellId]
|
||||
if (!cell) return
|
||||
updateVariableValue(cell.variableId ?? '', data[cell.column ?? ''])
|
||||
})
|
||||
}
|
||||
const parseCellValues = (
|
||||
cells: Table<Cell>,
|
||||
variables: Table<Variable>
|
||||
): { [key: string]: string } =>
|
||||
cells.allIds.reduce((row, id) => {
|
||||
const cell = cells.byId[id]
|
||||
return !cell.column || !cell.value
|
||||
? row
|
||||
: { ...row, [cell.column]: parseVariables(cell.value, variables) }
|
||||
}, {})
|
72
packages/bot-engine/src/services/logic.ts
Normal file
72
packages/bot-engine/src/services/logic.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import {
|
||||
LogicStep,
|
||||
Target,
|
||||
LogicStepType,
|
||||
LogicalOperator,
|
||||
ConditionStep,
|
||||
Table,
|
||||
Variable,
|
||||
ComparisonOperators,
|
||||
} from 'models'
|
||||
import { isDefined } from 'utils'
|
||||
import { isMathFormula, evaluateExpression, parseVariables } from './variable'
|
||||
|
||||
export const executeLogic = (
|
||||
step: LogicStep,
|
||||
variables: Table<Variable>,
|
||||
updateVariableValue: (variableId: string, expression: string) => void
|
||||
): Target | undefined => {
|
||||
switch (step.type) {
|
||||
case LogicStepType.SET_VARIABLE: {
|
||||
if (!step.options?.variableId || !step.options.expressionToEvaluate)
|
||||
return
|
||||
const expression = step.options.expressionToEvaluate
|
||||
const evaluatedExpression = isMathFormula(expression)
|
||||
? evaluateExpression(parseVariables(expression, variables))
|
||||
: expression
|
||||
updateVariableValue(step.options.variableId, evaluatedExpression)
|
||||
return
|
||||
}
|
||||
case LogicStepType.CONDITION: {
|
||||
const isConditionPassed =
|
||||
step.options?.logicalOperator === LogicalOperator.AND
|
||||
? step.options?.comparisons.allIds.every(
|
||||
executeComparison(step, variables)
|
||||
)
|
||||
: step.options?.comparisons.allIds.some(
|
||||
executeComparison(step, variables)
|
||||
)
|
||||
return isConditionPassed ? step.trueTarget : step.falseTarget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const executeComparison =
|
||||
(step: ConditionStep, variables: Table<Variable>) =>
|
||||
(comparisonId: string) => {
|
||||
const comparison = step.options?.comparisons.byId[comparisonId]
|
||||
if (!comparison?.variableId) return false
|
||||
const inputValue = variables.byId[comparison.variableId].value ?? ''
|
||||
const { value } = comparison
|
||||
if (!isDefined(value)) return false
|
||||
switch (comparison.comparisonOperator) {
|
||||
case ComparisonOperators.CONTAINS: {
|
||||
return inputValue.includes(value)
|
||||
}
|
||||
case ComparisonOperators.EQUAL: {
|
||||
return inputValue === value
|
||||
}
|
||||
case ComparisonOperators.NOT_EQUAL: {
|
||||
return inputValue !== value
|
||||
}
|
||||
case ComparisonOperators.GREATER: {
|
||||
return parseFloat(inputValue) >= parseFloat(value)
|
||||
}
|
||||
case ComparisonOperators.LESS: {
|
||||
return parseFloat(inputValue) <= parseFloat(value)
|
||||
}
|
||||
case ComparisonOperators.IS_SET: {
|
||||
return isDefined(inputValue) && inputValue.length > 0
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user