Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import { EMPTY_ARR } from 'lib/void'
import { basename } from 'path'
import { Card, CardContent, CardHeader, CardTitle, cn, Skeleton } from 'ui'

const EMPTY_FUNCTION_BODY: EdgeFunctionBodyData = {
version: 0,
files: EMPTY_ARR,
}

interface EdgeFunctionsDiffPanelProps {
diffResults: EdgeFunctionsDiffResult
currentBranchRef?: string
Expand Down Expand Up @@ -81,11 +86,11 @@ const FunctionDiff = ({
}
}, [allFileKeys, activeFileKey])

const currentFile = currentBody.find(
(f: EdgeFunctionBodyData[number]) => fileKey(f.name) === activeFileKey
const currentFile = currentBody.files.find(
(f: EdgeFunctionBodyData['files'][number]) => fileKey(f.name) === activeFileKey
)
const mainFile = mainBody.find(
(f: EdgeFunctionBodyData[number]) => fileKey(f.name) === activeFileKey
const mainFile = mainBody.files.find(
(f: EdgeFunctionBodyData['files'][number]) => fileKey(f.name) === activeFileKey
)

const language = useMemo(() => {
Expand Down Expand Up @@ -189,7 +194,7 @@ const EdgeFunctionsDiffPanel = ({
key={slug}
functionSlug={slug}
currentBody={diffResults.addedBodiesMap[slug]!}
mainBody={EMPTY_ARR}
mainBody={EMPTY_FUNCTION_BODY}
currentBranchRef={currentBranchRef}
fileInfos={diffResults.functionFileInfo[slug] || EMPTY_ARR}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,23 @@ const EdgeFunctionDetailsLayout = ({
isError,
} = useEdgeFunctionQuery({ projectRef: ref, slug: functionSlug })

const { data: functionFiles = [], error: filesError } = useEdgeFunctionBodyQuery(
{
projectRef: ref,
slug: functionSlug,
},
{
retry: false,
retryOnMount: true,
refetchOnWindowFocus: false,
staleTime: Infinity,
refetchOnMount: false,
refetchOnReconnect: false,
refetchInterval: false,
refetchIntervalInBackground: false,
}
)
const { data: functionBody = { version: 0, files: [] }, error: filesError } =
useEdgeFunctionBodyQuery(
{
projectRef: ref,
slug: functionSlug,
},
{
retry: false,
retryOnMount: true,
refetchOnWindowFocus: false,
staleTime: Infinity,
refetchOnMount: false,
refetchOnReconnect: false,
refetchInterval: false,
refetchIntervalInBackground: false,
}
)

const name = selectedFunction?.name || ''

Expand Down Expand Up @@ -112,7 +113,7 @@ const EdgeFunctionDetailsLayout = ({

const zipFileWriter = new BlobWriter('application/zip')
const zipWriter = new ZipWriter(zipFileWriter, { bufferedWrite: true })
functionFiles.forEach((file) => {
functionBody.files.forEach((file) => {
const nameSections = file.name.split('/')
const slugIndex = nameSections.indexOf(functionSlug ?? '')
const fileName = nameSections.slice(slugIndex + 1).join('/')
Expand Down
4 changes: 2 additions & 2 deletions apps/studio/components/layouts/Tabs/Tabs.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function useTableEditorTabsCleanUp() {
// e.g Using the SQL editor to rename the entity
const openTabs = openTabsRef.current
.map((id) => tabMapRef.current[id])
.filter((tab) => editorEntityTypes['table']?.includes(tab.type))
.filter((tab) => !!tab && editorEntityTypes['table']?.includes(tab.type))

openTabs.forEach((tab) => {
const entity = entities?.find((x) => tab.metadata?.tableId === x.id)
Expand Down Expand Up @@ -97,7 +97,7 @@ export function useSqlEditorTabsCleanup() {
// e.g for a shared snippet, the owner could've updated the name of the snippet
const openSqlTabs = openTabsRef.current
.map((id) => tabMapRef.current[id])
.filter((tab) => editorEntityTypes['sql']?.includes(tab.type))
.filter((tab) => !!tab && editorEntityTypes['sql']?.includes(tab.type))

openSqlTabs.forEach((tab) => {
const snippet = snippets?.find((x) => tab.metadata?.sqlId === x.id)
Expand Down
10 changes: 7 additions & 3 deletions apps/studio/data/edge-functions/edge-function-body-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type EdgeFunctionFile = {
}

export type EdgeFunctionBodyResponse = {
version: number
files: EdgeFunctionFile[]
}

Expand Down Expand Up @@ -51,11 +52,14 @@ export async function getEdgeFunctionBody(
)
}

const { files } = await parseResponse.json()
return files as EdgeFunctionFile[]
const response = (await parseResponse.json()) as EdgeFunctionBodyResponse
return response
} catch (error) {
handleError(error)
return []
return {
version: 0,
files: [],
} as EdgeFunctionBodyResponse
}
}

Expand Down
12 changes: 7 additions & 5 deletions apps/studio/hooks/branches/useEdgeFunctionsDiff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,15 @@ export const useEdgeFunctionsDiff = ({
const mainBody = mainBodiesMap[slug]
if (!currentBody || !mainBody) return

const allFileKeys = new Set([...currentBody, ...mainBody].map((f) => fileKey(f.name)))
const allFileKeys = new Set(
[...currentBody.files, ...mainBody.files].map((f) => fileKey(f.name))
)
const fileInfos: FileInfo[] = []
let hasModifications = false

for (const key of allFileKeys) {
const currentFile = currentBody.find((f) => fileKey(f.name) === key)
const mainFile = mainBody.find((f) => fileKey(f.name) === key)
const currentFile = currentBody.files.find((f) => fileKey(f.name) === key)
const mainFile = mainBody.files.find((f) => fileKey(f.name) === key)

let status: FileStatus = 'unchanged'

Expand Down Expand Up @@ -236,7 +238,7 @@ export const useEdgeFunctionsDiff = ({
addedSlugs.forEach((slug) => {
const body = addedBodiesMap[slug]
if (body) {
functionFileInfo[slug] = body.map((file) => ({
functionFileInfo[slug] = body.files.map((file) => ({
key: fileKey(file.name),
status: 'added' as FileStatus,
}))
Expand All @@ -247,7 +249,7 @@ export const useEdgeFunctionsDiff = ({
removedSlugs.forEach((slug) => {
const body = removedBodiesMap[slug]
if (body) {
functionFileInfo[slug] = body.map((file) => ({
functionFileInfo[slug] = body.files.map((file) => ({
key: fileKey(file.name),
status: 'removed' as FileStatus,
}))
Expand Down
2 changes: 1 addition & 1 deletion apps/studio/instrumentation-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Sentry.init({
`Failed to construct 'URL': Invalid URL`
)
// [Joshen] Similar behaviour for this error from SessionTimeoutModal to control the quota usage
const isSessionTimeoutEvent = (hint.originalException as any)?.message.includes(
const isSessionTimeoutEvent = (hint.originalException as any)?.message?.includes(
'Session error detected'
)

Expand Down
15 changes: 9 additions & 6 deletions apps/studio/lib/eszip-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ describe('eszip-parser', () => {
mockParser.parseBytes.mockResolvedValue(mockSpecifiers)
mockParser.load.mockResolvedValue(undefined)
mockParser.getModuleSource
.mockResolvedValueOnce(null)
.mockResolvedValueOnce(mockModuleSource1)
.mockResolvedValueOnce(mockModuleSource2)

Expand All @@ -70,12 +71,13 @@ describe('eszip-parser', () => {
expect(Parser.createInstance).toHaveBeenCalledTimes(1)
expect(mockParser.parseBytes).toHaveBeenCalledWith(mockBytes)
expect(mockParser.load).toHaveBeenCalled()
expect(result).toHaveLength(2)
expect(result[0]).toEqual({
expect(result.version).toEqual(0)
expect(result.files).toHaveLength(2)
expect(result.files[0]).toEqual({
name: 'file1.ts',
content: mockModuleSource1,
})
expect(result[1]).toEqual({
expect(result.files[1]).toEqual({
name: 'file2.ts',
content: mockModuleSource2,
})
Expand Down Expand Up @@ -111,9 +113,10 @@ describe('eszip-parser', () => {

const result = await parseEszip(mockBytes)
// Only file1.ts and file2.ts should be included
expect(result).toHaveLength(2)
expect(result[0].name).toBe('file1.ts')
expect(result[1].name).toBe('file2.ts')
expect(result.version).toEqual(0)
expect(result.files).toHaveLength(2)
expect(result.files[0].name).toBe('file1.ts')
expect(result.files[1].name).toBe('file2.ts')
})
})
})
21 changes: 17 additions & 4 deletions apps/studio/lib/eszip-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,14 @@ export async function parseEszip(bytes: Uint8Array) {
throw loadError
}

// Extract version
let version = parseInt(await parser.getModuleSource('---SUPABASE-ESZIP-VERSION-ESZIP---'))
if (isNaN(version)) {
version = 0
}

// Extract files from the eszip
const files = await extractEszip(parser, specifiers)
const files = await extractEszip(parser, specifiers, version >= 2)

// Convert files to the expected format
const responseFiles = await Promise.all(
Expand All @@ -77,14 +83,17 @@ export async function parseEszip(bytes: Uint8Array) {
})
)

return responseFiles
return {
version,
files: responseFiles,
}
} catch (error) {
console.error('Error in parseEszip:', error)
throw error
}
}

async function extractEszip(parser: any, specifiers: string[]) {
async function extractEszip(parser: any, specifiers: string[], isDeno2: boolean) {
const files = []

// First, filter out the specifiers we want to keep
Expand Down Expand Up @@ -114,9 +123,13 @@ async function extractEszip(parser: any, specifiers: string[]) {
try {
// Try to get the module source
const moduleSource = await parser.getModuleSource(specifier)
let qualifiedSpecifier = specifier

// Get the file path
const filePath = url2path(specifier)
if (isDeno2 && !specifier.startsWith('file://')) {
qualifiedSpecifier = `file://${specifier}`
}
const filePath = url2path(qualifiedSpecifier)

// Create a file object
const file = new File([moduleSource], filePath)
Expand Down
6 changes: 2 additions & 4 deletions apps/studio/pages/api/edge-functions/body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,9 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
const uint8Array = new Uint8Array(arrayBuffer)

// Parse the eszip file using our utility
const files = await parseEszip(uint8Array)
const parsed = await parseEszip(uint8Array)

return res.status(200).json({
files,
})
return res.status(200).json(parsed)
} catch (error) {
console.error('Error processing edge function body:', error)
return res.status(500).json({ error: 'Internal server error' })
Expand Down
34 changes: 26 additions & 8 deletions apps/studio/pages/project/[ref]/functions/[functionSlug]/code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const CodePage = () => {

const { data: selectedFunction } = useEdgeFunctionQuery({ projectRef: ref, slug: functionSlug })
const {
data: functionFiles,
data: functionBody,
isLoading: isLoadingFiles,
isError: isErrorLoadingFiles,
isSuccess: isSuccessLoadingFiles,
Expand Down Expand Up @@ -123,15 +123,29 @@ const CodePage = () => {
}
}

function getBasePath(entrypoint: string | undefined): string {
function getBasePath(
entrypoint: string | undefined,
fileNames: string[],
version: number
): string {
if (!entrypoint) {
return '/'
}

let qualifiedEntrypoint = entrypoint

if (version >= 2) {
const candidate = fileNames.find((name) => entrypoint.endsWith(name))
if (candidate) {
qualifiedEntrypoint = `file://${candidate}`
} else {
qualifiedEntrypoint = entrypoint
}
}
try {
return dirname(new URL(entrypoint).pathname)
return dirname(new URL(qualifiedEntrypoint).pathname)
} catch (e) {
console.error('Failed to parse entrypoint', entrypoint)
console.error('Failed to parse entrypoint', qualifiedEntrypoint)
return '/'
}
}
Expand All @@ -155,9 +169,13 @@ const CodePage = () => {

useEffect(() => {
// Set files from API response when available
if (selectedFunction?.entrypoint_path && functionFiles) {
const base_path = getBasePath(selectedFunction?.entrypoint_path)
const filesWithRelPath = functionFiles
if (selectedFunction?.entrypoint_path && functionBody) {
const base_path = getBasePath(
selectedFunction?.entrypoint_path,
functionBody.files.map((file) => file.name),
functionBody.version
)
const filesWithRelPath = functionBody.files
// ignore empty files
.filter((file: { name: string; content: string }) => !!file.content.length)
// set file paths relative to entrypoint
Expand Down Expand Up @@ -192,7 +210,7 @@ const CodePage = () => {
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [functionFiles])
}, [functionBody])

return (
<div className="flex flex-col h-full">
Expand Down
Loading