add edge function block with deploy

This commit is contained in:
Saxon Fletcher committed 2025-02-06 11:15:33 +10:00
1 parent 31181ac997
commit fa5a6c2b54
4 files changed
+170 -20

No files matched your search

@@ -1,10 +1,11 @@
export type SupportedAssistantEntities = 'rls-policies' | 'functions'
export type SupportedAssistantQuickPromptTypes = 'suggest' | 'examples' | 'ask'
export type AssistantSnippetProps = {
title: string
runQuery: 'true' | 'false'
isChart?: 'true' | 'false'
export interface AssistantSnippetProps {
title?: string
isChart?: string
runQuery?: string
xAxis?: string
yAxis?: string
name?: string
}
@@ -16,6 +16,7 @@ import { AssistantSnippetProps } from './AIAssistant.types'
import { identifyQueryType } from './AIAssistant.utils'
import CollapsibleCodeBlock from './CollapsibleCodeBlock'
import { MessageContext } from './Message'
import { EdgeFunctionBlock } from '../EdgeFunctionBlock/EdgeFunctionBlock'
export const OrderedList = memo(({ children }: { children: ReactNode }) => (
<ol className="flex flex-col gap-y-4">{children}</ol>
@@ -149,19 +150,22 @@ export const MarkdownPre = ({ children }: { children: any }) => {
})
const language = children[0].props.className?.replace('language-', '') || 'sql'
const rawSql = language === 'sql' ? children[0].props.children : undefined
const formatted = (rawSql || [''])[0]
const propsMatch = formatted.match(/--\s*props:\s*(\{[^}]+\})/)
const rawContent = children[0].props.children[0]
const propsMatch = rawContent.match(/--\s*props:\s*(\{[^}]+\})/)
const snippetProps: AssistantSnippetProps = useMemo(
() => (propsMatch ? JSON.parse(propsMatch[1]) : {}),
[propsMatch]
)
const { xAxis, yAxis } = snippetProps
const title = snippetProps.title || 'SQL Query'
const title = snippetProps.title || (language === 'edge' ? 'Edge Function' : 'SQL Query')
const isChart = snippetProps.isChart === 'true'
const runQuery = snippetProps.runQuery === 'true'
const sql = formatted?.replace(/--\s*props:\s*\{[^}]+\}/, '').trim()
// Strip props from the content for both SQL and edge functions
const cleanContent = rawContent.replace(/--\s*props:\s*\{[^}]+\}/, '').trim()
const isDraggableToReports =
supportSQLBlocks && canCreateSQLSnippet && router.pathname.endsWith('/reports/[id]')
@@ -180,23 +184,28 @@ export const MarkdownPre = ({ children }: { children: any }) => {
action: TelemetryActions.ASSISTANT_SUGGESTION_RUN_QUERY_CLICKED,
properties: {
queryType,
...(queryType === 'mutation' ? { category: identifyQueryType(sql) ?? 'unknown' } : {}),
...(queryType === 'mutation'
? { category: identifyQueryType(cleanContent) ?? 'unknown' }
: {}),
},
})
}
return (
<div className="w-auto -ml-[36px] overflow-x-hidden">
{language === 'sql' ? (
{language === 'edge' ? (
<EdgeFunctionBlock
label={title}
code={cleanContent}
functionName={snippetProps.name || 'my-function'}
showCode={!readOnly}
/>
) : language === 'sql' ? (
readOnly ? (
<CollapsibleCodeBlock
value={children[0].props.children[0]}
language="sql"
hideLineNumbers
/>
<CollapsibleCodeBlock value={cleanContent} language="sql" hideLineNumbers />
) : (
<MemoizedQueryBlock
sql={sql}
sql={cleanContent}
title={title}
xAxis={xAxis}
yAxis={yAxis}
@@ -211,7 +220,7 @@ export const MarkdownPre = ({ children }: { children: any }) => {
onDragStart={(e: DragEvent<Element>) => {
e.dataTransfer.setData(
'application/json',
JSON.stringify({ label: title, sql, config: chartConfig.current })
JSON.stringify({ label: title, sql: cleanContent, config: chartConfig.current })
)
}}
/>
@@ -219,7 +228,7 @@ export const MarkdownPre = ({ children }: { children: any }) => {
) : (
<CodeBlock
hideLineNumbers
value={children[0].props.children[0]}
value={cleanContent}
language={language as CodeBlockLang}
className={cn(
'max-h-96 max-w-none block border rounded !bg-transparent !py-3 !px-3.5 prose dark:prose-dark text-foreground',
@@ -0,0 +1,135 @@
import { DragEvent, ReactNode, useState } from 'react'
import { toast } from 'sonner'
import { useParams } from 'common'
import { useEdgeFunctionDeployMutation } from 'data/edge-functions/edge-functions-deploy-mutation'
import { ReportBlockContainer } from 'components/interfaces/Reports/ReportBlock/ReportBlockContainer'
import { Button, CodeBlock, cn, CodeBlockLang } from 'ui'
import { Code } from 'lucide-react'
interface EdgeFunctionBlockProps {
/** Title of the EdgeFunctionBlock */
label: string
/** Function code to display */
code: string
/** Function name/slug */
functionName: string
/** Any other actions specific to the parent to be rendered in the header */
actions?: ReactNode
/** Toggle visiblity of code on render */
showCode?: boolean
/** Whether function block is draggable */
draggable?: boolean
/** Tooltip when hovering over the header of the block */
tooltip?: ReactNode
/** Optional callback on drag start */
onDragStart?: (e: DragEvent<Element>) => void
}
export const EdgeFunctionBlock = ({
label,
code,
functionName,
actions,
showCode: _showCode = false,
tooltip,
}: EdgeFunctionBlockProps) => {
const { ref } = useParams()
const [isDeployed, setIsDeployed] = useState(false)
const { mutateAsync: deployFunction, isLoading: isDeploying } = useEdgeFunctionDeployMutation({
onSuccess: () => {
setIsDeployed(true)
toast.success('Successfully deployed edge function')
},
})
const handleDeploy = async () => {
if (!code || isDeploying || !ref) return
try {
const deployResult = await deployFunction({
projectRef: ref,
metadata: {
entrypoint_path: 'index.ts',
name: functionName,
verify_jwt: false,
},
files: [{ name: 'index.ts', content: code }],
})
} catch (error) {
toast.error(
`Failed to deploy function: ${error instanceof Error ? error.message : 'Unknown error'}`
)
}
}
const functionUrl = ref ? `https://${ref}.supabase.co/functions/v1/${functionName}` : ''
return (
<ReportBlockContainer
tooltip={tooltip}
icon={
<Code
size={18}
strokeWidth={1.5}
className={cn(
'transition-colors fill-foreground-muted group-aria-selected:fill-foreground',
'w-5 h-5 shrink-0 grow-0 -ml-0.5'
)}
/>
}
label={label}
actions={
<>
<Button
type="outline"
size="tiny"
loading={isDeploying}
disabled={!ref}
onClick={handleDeploy}
>
{isDeploying ? 'Deploying...' : 'Deploy'}
</Button>
{actions}
</>
}
>
<div className="shrink-0 w-full max-h-96 overflow-y-auto">
<CodeBlock
hideLineNumbers
wrapLines={false}
value={code}
language={'typescript' as CodeBlockLang}
className={cn(
'max-w-none block !bg-transparent !py-3 !px-3.5 prose dark:prose-dark border-0 text-foreground !rounded-none w-full',
'[&>code]:m-0 [&>code>span]:text-foreground'
)}
/>
</div>
{(isDeploying || isDeployed) && (
<div className="p-4 w-full border-t bg-surface-75 text-xs">
{isDeploying ? (
<p className="text-foreground-light">Deploying function...</p>
) : (
<>
<p className="text-foreground-light mb-2">
Function deployed successfully! You can now invoke it at:
</p>
<CodeBlock hideLineNumbers value={functionUrl} className="text-xs p-2" />
<p className="text-foreground-light mt-4 mb-2">
To download and work on this function locally, use the CLI command:
</p>
<CodeBlock
hideLineNumbers
value={`supabase functions download ${functionName}`}
className="text-xs p-2"
/>
</>
)}
</div>
)}
</ReportBlockContainer>
)
}
+6 -1
View File
@@ -121,7 +121,12 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
- First, always use the getEdgeFunctionKnowledge tool to get knowledge about how to write edge functions for Supabase
- When writing edge functions, always ensure that they are written in TypeScript and Deno JavaScript runtime.
- When writing edge functions, always ensure that they are written in a way that is compatible with the database schema.
- When suggesting edge functions, dont suggest generic edge functions as shown in the getEdgeFunctionKnowledge tool, create personalised edge functions based on the database schema.
- When suggesting edge functions, follow the guidelines in getEdgeFunctionKnowledge tool. Always create personalised edge functions based on the database schema
- When outputting edge functions, always include a props comment in the first line of the code block:
-- props: {"name": "function-name", "title": "Human readable title"}
- The function name in the props must be URL-friendly (use hyphens instead of spaces or underscores)
- Always wrap the edge function code in a markdown code block with the language set to 'edge'
- The props comment must be the first line inside the code block, followed by the actual function code
# You convert sql to supabase-js client code
Use the convertSqlToSupabaseJs tool to convert select sql to supabase-js client code. Only provide js code snippets if explicitly asked. If conversion isn't supported, build a postgres function instead and suggest using supabase-js to call it via "const { data, error } = await supabase.rpc('echo', { say: '👋'})"