mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
improvement(supabase): add supabase upsert tool, insert/replace on PK conflict (#1038)
This commit is contained in:
committed by
Siddharth Ganesan
parent
79e932fed9
commit
bbb5e53e43
@@ -185,6 +185,26 @@ Delete rows from a Supabase table based on filter criteria
|
||||
| `message` | string | Operation status message |
|
||||
| `results` | array | Array of deleted records |
|
||||
|
||||
### `supabase_upsert`
|
||||
|
||||
Insert or update data in a Supabase table (upsert operation)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Your Supabase project ID \(e.g., jdrkgepadsdopsntdlom\) |
|
||||
| `table` | string | Yes | The name of the Supabase table to upsert data into |
|
||||
| `data` | any | Yes | The data to upsert \(insert or update\) |
|
||||
| `apiKey` | string | Yes | Your Supabase service role secret key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Operation status message |
|
||||
| `results` | array | Array of upserted records |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -27,6 +27,7 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
{ label: 'Create a Row', id: 'insert' },
|
||||
{ label: 'Update a Row', id: 'update' },
|
||||
{ label: 'Delete a Row', id: 'delete' },
|
||||
{ label: 'Upsert a Row', id: 'upsert' },
|
||||
],
|
||||
value: () => 'query',
|
||||
},
|
||||
@@ -75,6 +76,15 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'data',
|
||||
title: 'Data',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
placeholder: '{\n "column1": "value1",\n "column2": "value2"\n}',
|
||||
condition: { field: 'operation', value: 'upsert' },
|
||||
required: true,
|
||||
},
|
||||
// Filter for get_row, update, delete operations (required)
|
||||
{
|
||||
id: 'filter',
|
||||
@@ -138,6 +148,7 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
'supabase_get_row',
|
||||
'supabase_update',
|
||||
'supabase_delete',
|
||||
'supabase_upsert',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
@@ -152,6 +163,8 @@ export const SupabaseBlock: BlockConfig<SupabaseResponse> = {
|
||||
return 'supabase_update'
|
||||
case 'delete':
|
||||
return 'supabase_delete'
|
||||
case 'upsert':
|
||||
return 'supabase_upsert'
|
||||
default:
|
||||
throw new Error(`Invalid Supabase operation: ${params.operation}`)
|
||||
}
|
||||
|
||||
@@ -128,6 +128,7 @@ import {
|
||||
supabaseInsertTool,
|
||||
supabaseQueryTool,
|
||||
supabaseUpdateTool,
|
||||
supabaseUpsertTool,
|
||||
} from '@/tools/supabase'
|
||||
import { tavilyExtractTool, tavilySearchTool } from '@/tools/tavily'
|
||||
import { telegramMessageTool } from '@/tools/telegram'
|
||||
@@ -190,6 +191,7 @@ export const tools: Record<string, ToolConfig> = {
|
||||
supabase_get_row: supabaseGetRowTool,
|
||||
supabase_update: supabaseUpdateTool,
|
||||
supabase_delete: supabaseDeleteTool,
|
||||
supabase_upsert: supabaseUpsertTool,
|
||||
typeform_responses: typeformResponsesTool,
|
||||
typeform_files: typeformFilesTool,
|
||||
typeform_insights: typeformInsightsTool,
|
||||
|
||||
@@ -3,9 +3,11 @@ import { getRowTool } from '@/tools/supabase/get_row'
|
||||
import { insertTool } from '@/tools/supabase/insert'
|
||||
import { queryTool } from '@/tools/supabase/query'
|
||||
import { updateTool } from '@/tools/supabase/update'
|
||||
import { upsertTool } from '@/tools/supabase/upsert'
|
||||
|
||||
export const supabaseQueryTool = queryTool
|
||||
export const supabaseInsertTool = insertTool
|
||||
export const supabaseGetRowTool = getRowTool
|
||||
export const supabaseUpdateTool = updateTool
|
||||
export const supabaseDeleteTool = deleteTool
|
||||
export const supabaseUpsertTool = upsertTool
|
||||
|
||||
@@ -38,6 +38,13 @@ export interface SupabaseDeleteParams {
|
||||
filter: string
|
||||
}
|
||||
|
||||
export interface SupabaseUpsertParams {
|
||||
apiKey: string
|
||||
projectId: string
|
||||
table: string
|
||||
data: any
|
||||
}
|
||||
|
||||
export interface SupabaseBaseResponse extends ToolResponse {
|
||||
output: {
|
||||
message: string
|
||||
@@ -56,4 +63,6 @@ export interface SupabaseUpdateResponse extends SupabaseBaseResponse {}
|
||||
|
||||
export interface SupabaseDeleteResponse extends SupabaseBaseResponse {}
|
||||
|
||||
export interface SupabaseUpsertResponse extends SupabaseBaseResponse {}
|
||||
|
||||
export interface SupabaseResponse extends SupabaseBaseResponse {}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import type { SupabaseUpsertParams, SupabaseUpsertResponse } from '@/tools/supabase/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const upsertTool: ToolConfig<SupabaseUpsertParams, SupabaseUpsertResponse> = {
|
||||
id: 'supabase_upsert',
|
||||
name: 'Supabase Upsert',
|
||||
description: 'Insert or update data in a Supabase table (upsert operation)',
|
||||
version: '1.0',
|
||||
|
||||
params: {
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Your Supabase project ID (e.g., jdrkgepadsdopsntdlom)',
|
||||
},
|
||||
table: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'The name of the Supabase table to upsert data into',
|
||||
},
|
||||
data: {
|
||||
type: 'any',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The data to upsert (insert or update)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Your Supabase service role secret key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://${params.projectId}.supabase.co/rest/v1/${params.table}?select=*`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
apikey: params.apiKey,
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
Prefer: 'return=representation,resolution=merge-duplicates',
|
||||
}),
|
||||
body: (params) => {
|
||||
// Prepare the data - if it's an object but not an array, wrap it in an array
|
||||
const dataToSend =
|
||||
typeof params.data === 'object' && !Array.isArray(params.data) ? [params.data] : params.data
|
||||
|
||||
return dataToSend
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const text = await response.text()
|
||||
|
||||
if (!text || text.trim() === '') {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
message: 'Successfully upserted data into Supabase (no data returned)',
|
||||
results: [],
|
||||
},
|
||||
error: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
let data
|
||||
try {
|
||||
data = JSON.parse(text)
|
||||
} catch (parseError) {
|
||||
throw new Error(`Failed to parse Supabase response: ${parseError}`)
|
||||
}
|
||||
|
||||
// Check if results array is empty and provide better feedback
|
||||
const resultsArray = Array.isArray(data) ? data : [data]
|
||||
const isEmpty = resultsArray.length === 0 || (resultsArray.length === 1 && !resultsArray[0])
|
||||
|
||||
if (isEmpty) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
message: 'No data was upserted into Supabase',
|
||||
results: data,
|
||||
},
|
||||
error:
|
||||
'No data was upserted into Supabase. This usually indicates invalid data format or schema mismatch. Please check that your JSON is valid and matches your table schema.',
|
||||
}
|
||||
}
|
||||
|
||||
const upsertedCount = resultsArray.length
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
message: `Successfully upserted ${upsertedCount} row${upsertedCount === 1 ? '' : 's'} into Supabase`,
|
||||
results: data,
|
||||
},
|
||||
error: undefined,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
message: { type: 'string', description: 'Operation status message' },
|
||||
results: { type: 'array', description: 'Array of upserted records' },
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user