mirror of
https://github.com/supabase/supabase.git
synced 2026-10-16 14:49:10 +03:00
add edge function block with deploy
This commit is contained in:
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>
|
||||
)
|
||||
}
|
||||
@@ -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: '👋'})"
|
||||
|
||||
Reference in new issue
Block a user