⚗️ Implement chat API

This commit is contained in:
Baptiste Arnaud
2022-11-29 10:02:40 +01:00
parent 49ba434350
commit bf0d0c2475
122 changed files with 5075 additions and 292 deletions

View File

@@ -0,0 +1 @@
export * from './utils'

View File

@@ -0,0 +1,30 @@
import { ExecuteIntegrationResponse } from '@/features/chat'
import { GoogleSheetsBlock, GoogleSheetsAction, SessionState } from 'models'
import { getRow } from './getRow'
import { insertRow } from './insertRow'
import { updateRow } from './updateRow'
export const executeGoogleSheetBlock = async (
state: SessionState,
block: GoogleSheetsBlock
): Promise<ExecuteIntegrationResponse> => {
if (!('action' in block.options))
return { outgoingEdgeId: block.outgoingEdgeId }
switch (block.options.action) {
case GoogleSheetsAction.INSERT_ROW:
return insertRow(state, {
options: block.options,
outgoingEdgeId: block.outgoingEdgeId,
})
case GoogleSheetsAction.UPDATE_ROW:
return updateRow(state, {
options: block.options,
outgoingEdgeId: block.outgoingEdgeId,
})
case GoogleSheetsAction.GET:
return getRow(state, {
options: block.options,
outgoingEdgeId: block.outgoingEdgeId,
})
}
}

View File

@@ -0,0 +1,89 @@
import { SessionState, GoogleSheetsGetOptions, VariableWithValue } from 'models'
import { saveErrorLog, saveSuccessLog } from '@/features/logs/api'
import { getAuthenticatedGoogleDoc } from './helpers'
import { parseVariables, updateVariables } from '@/features/variables'
import { isNotEmpty, byId } from 'utils'
import { ExecuteIntegrationResponse } from '@/features/chat'
export const getRow = async (
state: SessionState,
{
outgoingEdgeId,
options,
}: { outgoingEdgeId?: string; options: GoogleSheetsGetOptions }
): Promise<ExecuteIntegrationResponse> => {
const { sheetId, cellsToExtract, referenceCell } = options
if (!cellsToExtract || !sheetId || !referenceCell) return { outgoingEdgeId }
const variables = state.typebot.variables
const resultId = state.result.id
const doc = await getAuthenticatedGoogleDoc({
credentialsId: options.credentialsId,
spreadsheetId: options.spreadsheetId,
})
const parsedReferenceCell = {
column: referenceCell.column,
value: parseVariables(variables)(referenceCell.value),
}
const extractingColumns = cellsToExtract
.map((cell) => cell.column)
.filter(isNotEmpty)
try {
await doc.loadInfo()
const sheet = doc.sheetsById[sheetId]
const rows = await sheet.getRows()
const row = rows.find(
(row) =>
row[parsedReferenceCell.column as string] === parsedReferenceCell.value
)
if (!row) {
await saveErrorLog({
resultId,
message: "Couldn't find reference cell",
})
return { outgoingEdgeId }
}
const data: { [key: string]: string } = {
...extractingColumns.reduce(
(obj, column) => ({ ...obj, [column]: row[column] }),
{}
),
}
await saveSuccessLog({
resultId,
message: 'Succesfully fetched spreadsheet data',
})
const newVariables = options.cellsToExtract.reduce<VariableWithValue[]>(
(newVariables, cell) => {
const existingVariable = variables.find(byId(cell.variableId))
const value = data[cell.column ?? ''] ?? null
if (!existingVariable) return newVariables
return [
...newVariables,
{
...existingVariable,
value,
},
]
},
[]
)
const newSessionState = await updateVariables(state)(newVariables)
return {
outgoingEdgeId,
newSessionState,
}
} catch (err) {
await saveErrorLog({
resultId,
message: "Couldn't fetch spreadsheet data",
details: err,
})
}
return { outgoingEdgeId }
}

View File

@@ -0,0 +1,40 @@
import { parseVariables } from '@/features/variables'
import { getAuthenticatedGoogleClient } from '@/lib/google-sheets'
import { TRPCError } from '@trpc/server'
import { GoogleSpreadsheet } from 'google-spreadsheet'
import { Variable, Cell } from 'models'
export const parseCellValues =
(variables: Variable[]) =>
(cells: Cell[]): { [key: string]: string } =>
cells.reduce((row, cell) => {
return !cell.column || !cell.value
? row
: {
...row,
[cell.column]: parseVariables(variables)(cell.value),
}
}, {})
export const getAuthenticatedGoogleDoc = async ({
credentialsId,
spreadsheetId,
}: {
credentialsId?: string
spreadsheetId?: string
}) => {
if (!credentialsId)
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'Missing credentialsId or sheetId',
})
const doc = new GoogleSpreadsheet(spreadsheetId)
const auth = await getAuthenticatedGoogleClient(credentialsId)
if (!auth)
throw new TRPCError({
code: 'NOT_FOUND',
message: "Couldn't find credentials in database",
})
doc.useOAuth2Client(auth)
return doc
}

View File

@@ -0,0 +1 @@
export * from './executeGoogleSheetBlock'

View File

@@ -0,0 +1,38 @@
import { SessionState, GoogleSheetsInsertRowOptions } from 'models'
import { saveErrorLog, saveSuccessLog } from '@/features/logs/api'
import { getAuthenticatedGoogleDoc, parseCellValues } from './helpers'
import { ExecuteIntegrationResponse } from '@/features/chat'
export const insertRow = async (
{ result, typebot: { variables } }: SessionState,
{
outgoingEdgeId,
options,
}: { outgoingEdgeId?: string; options: GoogleSheetsInsertRowOptions }
): Promise<ExecuteIntegrationResponse> => {
if (!options.cellsToInsert || !options.sheetId) return { outgoingEdgeId }
const doc = await getAuthenticatedGoogleDoc({
credentialsId: options.credentialsId,
spreadsheetId: options.spreadsheetId,
})
const parsedValues = parseCellValues(variables)(options.cellsToInsert)
try {
await doc.loadInfo()
const sheet = doc.sheetsById[options.sheetId]
await sheet.addRow(parsedValues)
await saveSuccessLog({
resultId: result.id,
message: 'Succesfully inserted row',
})
} catch (err) {
await saveErrorLog({
resultId: result.id,
message: "Couldn't fetch spreadsheet data",
details: err,
})
}
return { outgoingEdgeId }
}

View File

@@ -0,0 +1,60 @@
import { SessionState, GoogleSheetsUpdateRowOptions } from 'models'
import { saveErrorLog, saveSuccessLog } from '@/features/logs/api'
import { getAuthenticatedGoogleDoc, parseCellValues } from './helpers'
import { TRPCError } from '@trpc/server'
import { parseVariables } from '@/features/variables'
import { ExecuteIntegrationResponse } from '@/features/chat'
export const updateRow = async (
{ result, typebot: { variables } }: SessionState,
{
outgoingEdgeId,
options,
}: { outgoingEdgeId?: string; options: GoogleSheetsUpdateRowOptions }
): Promise<ExecuteIntegrationResponse> => {
const { sheetId, referenceCell } = options
if (!options.cellsToUpsert || !sheetId || !referenceCell)
return { outgoingEdgeId }
const doc = await getAuthenticatedGoogleDoc({
credentialsId: options.credentialsId,
spreadsheetId: options.spreadsheetId,
})
const parsedReferenceCell = {
column: referenceCell.column,
value: parseVariables(variables)(referenceCell.value),
}
const parsedValues = parseCellValues(variables)(options.cellsToUpsert)
try {
await doc.loadInfo()
const sheet = doc.sheetsById[sheetId]
const rows = await sheet.getRows()
const updatingRowIndex = rows.findIndex(
(row) =>
row[parsedReferenceCell.column as string] === parsedReferenceCell.value
)
if (updatingRowIndex === -1) {
new TRPCError({
code: 'NOT_FOUND',
message: "Couldn't find row to update",
})
}
for (const key in parsedValues) {
rows[updatingRowIndex][key] = parsedValues[key]
}
await rows[updatingRowIndex].save()
await saveSuccessLog({
resultId: result.id,
message: 'Succesfully updated row',
})
} catch (err) {
await saveErrorLog({
resultId: result.id,
message: "Couldn't fetch spreadsheet data",
details: err,
})
}
return { outgoingEdgeId }
}