2022-12-22 17:02:34 +01:00
|
|
|
import {
|
|
|
|
SessionState,
|
|
|
|
GoogleSheetsGetOptions,
|
|
|
|
VariableWithValue,
|
|
|
|
ComparisonOperators,
|
|
|
|
LogicalOperator,
|
2023-01-25 11:27:47 +01:00
|
|
|
ReplyLog,
|
2023-03-15 08:35:16 +01:00
|
|
|
} from '@typebot.io/schemas'
|
|
|
|
import { isNotEmpty, byId, isDefined } from '@typebot.io/lib'
|
2022-12-22 17:02:34 +01:00
|
|
|
import type { GoogleSpreadsheetRow } from 'google-spreadsheet'
|
2023-03-15 12:21:52 +01:00
|
|
|
import { getAuthenticatedGoogleDoc } from './helpers/getAuthenticatedGoogleDoc'
|
|
|
|
import { ExecuteIntegrationResponse } from '@/features/chat/types'
|
|
|
|
import { saveErrorLog } from '@/features/logs/saveErrorLog'
|
|
|
|
import { updateVariables } from '@/features/variables/updateVariables'
|
|
|
|
import { deepParseVariables } from '@/features/variables/deepParseVariable'
|
2022-11-29 10:02:40 +01:00
|
|
|
|
|
|
|
export const getRow = async (
|
|
|
|
state: SessionState,
|
|
|
|
{
|
|
|
|
outgoingEdgeId,
|
|
|
|
options,
|
|
|
|
}: { outgoingEdgeId?: string; options: GoogleSheetsGetOptions }
|
|
|
|
): Promise<ExecuteIntegrationResponse> => {
|
2023-03-15 12:21:52 +01:00
|
|
|
const { sheetId, cellsToExtract, referenceCell, filter } = deepParseVariables(
|
2023-03-03 15:33:44 +01:00
|
|
|
state.typebot.variables
|
|
|
|
)(options)
|
2023-01-25 11:27:47 +01:00
|
|
|
if (!sheetId) return { outgoingEdgeId }
|
|
|
|
|
|
|
|
let log: ReplyLog | undefined
|
2022-11-29 10:02:40 +01:00
|
|
|
|
|
|
|
const variables = state.typebot.variables
|
2022-12-22 17:02:34 +01:00
|
|
|
const resultId = state.result?.id
|
2022-11-29 10:02:40 +01:00
|
|
|
|
|
|
|
const doc = await getAuthenticatedGoogleDoc({
|
|
|
|
credentialsId: options.credentialsId,
|
|
|
|
spreadsheetId: options.spreadsheetId,
|
|
|
|
})
|
|
|
|
|
|
|
|
try {
|
|
|
|
await doc.loadInfo()
|
|
|
|
const sheet = doc.sheetsById[sheetId]
|
|
|
|
const rows = await sheet.getRows()
|
2022-12-22 17:02:34 +01:00
|
|
|
const filteredRows = rows.filter((row) =>
|
|
|
|
referenceCell
|
|
|
|
? row[referenceCell.column as string] === referenceCell.value
|
2023-03-17 07:42:42 +01:00
|
|
|
: matchFilter(row, filter as NonNullable<typeof filter>)
|
2022-11-29 10:02:40 +01:00
|
|
|
)
|
2022-12-22 17:02:34 +01:00
|
|
|
if (filteredRows.length === 0) {
|
2023-01-25 11:27:47 +01:00
|
|
|
log = {
|
|
|
|
status: 'error',
|
|
|
|
description: `Couldn't find any rows matching the filter`,
|
|
|
|
}
|
2022-11-29 10:02:40 +01:00
|
|
|
await saveErrorLog({
|
|
|
|
resultId,
|
2023-01-25 11:27:47 +01:00
|
|
|
message: log.description,
|
2022-11-29 10:02:40 +01:00
|
|
|
})
|
2023-01-25 11:27:47 +01:00
|
|
|
return { outgoingEdgeId, logs: log ? [log] : undefined }
|
2022-11-29 10:02:40 +01:00
|
|
|
}
|
2022-12-22 17:02:34 +01:00
|
|
|
const extractingColumns = cellsToExtract
|
|
|
|
.map((cell) => cell.column)
|
|
|
|
.filter(isNotEmpty)
|
2023-02-23 14:44:37 +01:00
|
|
|
const selectedRows = filteredRows.map((row) =>
|
|
|
|
extractingColumns.reduce<{ [key: string]: string }>(
|
|
|
|
(obj, column) => ({ ...obj, [column]: row[column] }),
|
|
|
|
{}
|
2022-12-22 17:02:34 +01:00
|
|
|
)
|
2023-02-23 14:44:37 +01:00
|
|
|
)
|
|
|
|
if (!selectedRows) return { outgoingEdgeId }
|
2022-11-29 10:02:40 +01:00
|
|
|
|
|
|
|
const newVariables = options.cellsToExtract.reduce<VariableWithValue[]>(
|
|
|
|
(newVariables, cell) => {
|
|
|
|
const existingVariable = variables.find(byId(cell.variableId))
|
2023-02-23 14:44:37 +01:00
|
|
|
const value = selectedRows.map((row) => row[cell.column ?? ''])
|
2022-11-29 10:02:40 +01:00
|
|
|
if (!existingVariable) return newVariables
|
|
|
|
return [
|
|
|
|
...newVariables,
|
|
|
|
{
|
|
|
|
...existingVariable,
|
2023-03-03 09:39:42 +01:00
|
|
|
value: value.length === 1 ? value[0] : value,
|
2022-11-29 10:02:40 +01:00
|
|
|
},
|
|
|
|
]
|
|
|
|
},
|
|
|
|
[]
|
|
|
|
)
|
|
|
|
const newSessionState = await updateVariables(state)(newVariables)
|
|
|
|
return {
|
|
|
|
outgoingEdgeId,
|
|
|
|
newSessionState,
|
|
|
|
}
|
|
|
|
} catch (err) {
|
2023-01-25 11:27:47 +01:00
|
|
|
log = {
|
|
|
|
status: 'error',
|
|
|
|
description: `An error occurred while fetching the spreadsheet data`,
|
|
|
|
details: err,
|
|
|
|
}
|
2022-11-29 10:02:40 +01:00
|
|
|
await saveErrorLog({
|
|
|
|
resultId,
|
2023-01-25 11:27:47 +01:00
|
|
|
message: log.description,
|
2022-11-29 10:02:40 +01:00
|
|
|
details: err,
|
|
|
|
})
|
|
|
|
}
|
2023-01-25 11:27:47 +01:00
|
|
|
return { outgoingEdgeId, logs: log ? [log] : undefined }
|
2022-11-29 10:02:40 +01:00
|
|
|
}
|
2022-12-22 17:02:34 +01:00
|
|
|
|
|
|
|
const matchFilter = (
|
|
|
|
row: GoogleSpreadsheetRow,
|
2023-03-17 07:42:42 +01:00
|
|
|
filter: NonNullable<GoogleSheetsGetOptions['filter']>
|
2022-12-22 17:02:34 +01:00
|
|
|
) => {
|
|
|
|
return filter.logicalOperator === LogicalOperator.AND
|
|
|
|
? filter.comparisons.every(
|
|
|
|
(comparison) =>
|
|
|
|
comparison.column &&
|
|
|
|
matchComparison(
|
|
|
|
row[comparison.column],
|
|
|
|
comparison.comparisonOperator,
|
|
|
|
comparison.value
|
|
|
|
)
|
|
|
|
)
|
|
|
|
: filter.comparisons.some(
|
|
|
|
(comparison) =>
|
|
|
|
comparison.column &&
|
|
|
|
matchComparison(
|
|
|
|
row[comparison.column],
|
|
|
|
comparison.comparisonOperator,
|
|
|
|
comparison.value
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const matchComparison = (
|
|
|
|
inputValue?: string,
|
|
|
|
comparisonOperator?: ComparisonOperators,
|
|
|
|
value?: string
|
|
|
|
) => {
|
|
|
|
if (!inputValue || !comparisonOperator || !value) return false
|
|
|
|
switch (comparisonOperator) {
|
|
|
|
case ComparisonOperators.CONTAINS: {
|
|
|
|
return inputValue.toLowerCase().includes(value.toLowerCase())
|
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|