From 747cb3d02e7f7bff141c866e168bbcca0447b762 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 17 Feb 2023 16:54:10 +0800 Subject: [PATCH 1/9] Update edge functions home page --- .../Functions/EdgeFunctionsList.tsx | 123 ++++++++++++++++++ .../Functions/TerminalInstructions.tsx | 15 +-- .../components/interfaces/Functions/index.ts | 3 +- studio/components/layouts/FunctionsLayout.tsx | 72 +++++++--- studio/pages/_app.tsx | 2 + .../pages/project/[ref]/functions/index.tsx | 49 +------ 6 files changed, 193 insertions(+), 71 deletions(-) create mode 100644 studio/components/interfaces/Functions/EdgeFunctionsList.tsx diff --git a/studio/components/interfaces/Functions/EdgeFunctionsList.tsx b/studio/components/interfaces/Functions/EdgeFunctionsList.tsx new file mode 100644 index 00000000000..0ff6d7aa81b --- /dev/null +++ b/studio/components/interfaces/Functions/EdgeFunctionsList.tsx @@ -0,0 +1,123 @@ +import dayjs from 'dayjs' +import { FC, useState } from 'react' +import * as Tooltip from '@radix-ui/react-tooltip' + +import { useParams, useStore } from 'hooks' +import Table from 'components/to-be-cleaned/Table' +import FunctionsListItem from './FunctionsListItem' +import { Function } from 'components/interfaces/Functions/Functions.types' +import { IconUpload, IconCheck, IconClipboard, IconClock } from 'ui' +import { useRouter } from 'next/router' + +interface Props {} + +const EdgeFunctionsList: FC = () => { + const router = useRouter() + const { ref } = useParams() + const { ui, functions: functionsStore } = useStore() + const functions: Function[] = functionsStore.list() ?? [] + + const restUrl = ui.selectedProject?.restUrl + const restUrlTld = new URL(restUrl as string).hostname.split('.').pop() + + return ( +
+
+ {functions.map((fn) => { + const [isCopied, setIsCopied] = useState(false) + const functionUrl = `https://${ref}.functions.supabase.${restUrlTld}/${fn.slug}` + + return ( +
router.push(`/project/${ref}/functions/${fn.id}`)} + > +
+

{fn.name}

+
+

{functionUrl}

+ +
+
+
+ + +

Deployment {fn.version}

+
+ + +
+ + Deployment history coming soon + +
+
+
+ + +
+ +

{dayjs(fn.updated_at).fromNow()}

+
+
+ + +
+ + Last updated on {dayjs(fn.updated_at).format('DD MMM, YYYY HH:mm')} + +
+
+
+
+
+ ) + })} +
+
+ ) +} + +export default EdgeFunctionsList diff --git a/studio/components/interfaces/Functions/TerminalInstructions.tsx b/studio/components/interfaces/Functions/TerminalInstructions.tsx index f8f30b523ab..16a2e2eea21 100644 --- a/studio/components/interfaces/Functions/TerminalInstructions.tsx +++ b/studio/components/interfaces/Functions/TerminalInstructions.tsx @@ -10,19 +10,16 @@ import { Commands } from './Functions.types' interface Props { closable?: boolean + removeBorder?: boolean } -const TerminalInstructions: FC = ({ closable = false }) => { +const TerminalInstructions: FC = ({ closable = false, removeBorder = false }) => { const router = useRouter() const { ref: projectRef } = useParams() - const [showInstructions, setShowInstructions] = useState(!closable) - const { data: tokens } = useAccessTokensQuery() + const { data: settings } = useProjectApiQuery({ projectRef }) - const { data: settings } = useProjectApiQuery({ - projectRef, - }) const apiService = settings?.autoApiService const anonKey = apiService?.service_api_keys.find((x) => x.name === 'anon key') ? apiService.defaultApiKey @@ -86,7 +83,9 @@ const TerminalInstructions: FC = ({ closable = false }) => { return (
@@ -107,7 +106,7 @@ const TerminalInstructions: FC = ({ closable = false }) => {
)}
-
+
diff --git a/studio/components/interfaces/Functions/index.ts b/studio/components/interfaces/Functions/index.ts index b096ff5e604..e2d3d9dda40 100644 --- a/studio/components/interfaces/Functions/index.ts +++ b/studio/components/interfaces/Functions/index.ts @@ -1,5 +1,6 @@ +import EdgeFunctionsList from './EdgeFunctionsList' import FunctionsListItem from './FunctionsListItem' import FunctionsEmptyState from './FunctionsEmptyState' import TerminalInstructions from './TerminalInstructions' -export { FunctionsListItem, FunctionsEmptyState, TerminalInstructions } +export { EdgeFunctionsList, FunctionsListItem, FunctionsEmptyState, TerminalInstructions } diff --git a/studio/components/layouts/FunctionsLayout.tsx b/studio/components/layouts/FunctionsLayout.tsx index 511d401da95..67d65717dea 100644 --- a/studio/components/layouts/FunctionsLayout.tsx +++ b/studio/components/layouts/FunctionsLayout.tsx @@ -1,14 +1,15 @@ import Link from 'next/link' import { useRouter } from 'next/router' -import { FC, useEffect, ReactNode } from 'react' +import { FC, useEffect, ReactNode, useState } from 'react' import { observer } from 'mobx-react-lite' -import { IconCode } from 'ui' +import { Button, IconExternalLink, IconCode, Modal, IconTerminal } from 'ui' -import { checkPermissions, useStore, withAuth } from 'hooks' +import { checkPermissions, useParams, useStore, withAuth } from 'hooks' import FunctionsNav from '../interfaces/Functions/FunctionsNav' import BaseLayout from 'components/layouts' import NoPermission from 'components/ui/NoPermission' import { PermissionAction } from '@supabase/shared-types/out/constants' +import { TerminalInstructions } from 'components/interfaces/Functions' interface Props { title?: string @@ -16,10 +17,10 @@ interface Props { } const FunctionsLayout: FC = ({ title, children }) => { - const { functions, ui } = useStore() const router = useRouter() - - const { id, ref } = router.query + const { id, ref } = useParams() + const { functions, ui } = useStore() + const [showTerminalInstructions, setShowTerminalInstructions] = useState(false) useEffect(() => { if (ui.selectedProjectRef) functions.load() @@ -87,7 +88,7 @@ const FunctionsLayout: FC = ({ title, children }) => { ].join(' ')} >
-
+
= ({ title, children }) => {
-
- -

- Edge Functions -

- -

Beta

+
+
+ +

+ Edge Functions +

+ +

Beta

+
+
+ + + + + + +
- {name && ( + {/* {name && ( <> = ({ title, children }) => {
{name}
- )} + )} */}
{item && } @@ -140,6 +162,24 @@ const FunctionsLayout: FC = ({ title, children }) => {
)} + + setShowTerminalInstructions(false)} + header={

Deploying an edge function to your project

} + customFooter={ +
+ +
+ } + > +
+ +
+
) } diff --git a/studio/pages/_app.tsx b/studio/pages/_app.tsx index d97675d69ef..f542d30ca75 100644 --- a/studio/pages/_app.tsx +++ b/studio/pages/_app.tsx @@ -16,6 +16,7 @@ import dayjs from 'dayjs' import customParseFormat from 'dayjs/plugin/customParseFormat' import timezone from 'dayjs/plugin/timezone' import utc from 'dayjs/plugin/utc' +import relativeTime from 'dayjs/plugin/relativeTime' // @ts-ignore import Prism from 'prism-react-renderer/prism' @@ -44,6 +45,7 @@ import useAutoAuthRedirect from 'hooks/misc/useAutoAuthRedirect' dayjs.extend(customParseFormat) dayjs.extend(utc) dayjs.extend(timezone) +dayjs.extend(relativeTime) dart(Prism) diff --git a/studio/pages/project/[ref]/functions/index.tsx b/studio/pages/project/[ref]/functions/index.tsx index f8778eb09f5..7848cff03fe 100644 --- a/studio/pages/project/[ref]/functions/index.tsx +++ b/studio/pages/project/[ref]/functions/index.tsx @@ -1,50 +1,8 @@ import { observer } from 'mobx-react-lite' - -import { NextPageWithLayout } from 'types' import { useStore } from 'hooks' +import { NextPageWithLayout } from 'types' import FunctionsLayout from 'components/layouts/FunctionsLayout' - -import Table from 'components/to-be-cleaned/Table' -import { Function } from 'components/interfaces/Functions/Functions.types' -import { - FunctionsListItem, - FunctionsEmptyState, - TerminalInstructions, -} from 'components/interfaces/Functions' - -const FunctionsList = ({ functions }: { functions: Function[] }) => { - return ( - <> -
-
- {`${functions.length} function${ - functions.length > 1 ? 's' : '' - } deployed`} -
-
- - Name - URL - Created - Last updated - Version - Status - - } - body={ - <> - {functions.length > 0 && - functions.map((item: any) => )} - - } - /> - - - - ) -} +import { EdgeFunctionsList, FunctionsEmptyState } from 'components/interfaces/Functions' const PageLayout: NextPageWithLayout = () => { const { functions } = useStore() @@ -52,8 +10,7 @@ const PageLayout: NextPageWithLayout = () => { return hasFunctions ? (
- - +
) : ( From c5f162a8e699dd816bda64e5608bb3554919a7a5 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 17 Feb 2023 18:27:06 +0800 Subject: [PATCH 2/9] Shift edge functions to RQ and implement delete function --- .../Functions/EdgeFunctionDetails.tsx | 296 ++++++++++++++++++ .../Functions/EdgeFunctionsList.tsx | 123 -------- .../interfaces/Functions/EdgeFunctionsRow.tsx | 111 +++++++ .../Functions/FunctionsListItem.tsx | 62 ---- .../interfaces/Functions/FunctionsNav.tsx | 4 +- .../components/interfaces/Functions/index.ts | 5 +- studio/components/layouts/FunctionsLayout.tsx | 41 ++- .../edge-functions-delete-mutation.ts | 43 +++ .../edge-functions/edge-functions-query.ts | 57 ++++ studio/data/edge-functions/keys.ts | 3 + .../project/[ref]/functions/[id]/details.tsx | 231 +------------- .../pages/project/[ref]/functions/index.tsx | 27 +- 12 files changed, 559 insertions(+), 444 deletions(-) create mode 100644 studio/components/interfaces/Functions/EdgeFunctionDetails.tsx delete mode 100644 studio/components/interfaces/Functions/EdgeFunctionsList.tsx create mode 100644 studio/components/interfaces/Functions/EdgeFunctionsRow.tsx delete mode 100644 studio/components/interfaces/Functions/FunctionsListItem.tsx create mode 100644 studio/data/edge-functions/edge-functions-delete-mutation.ts create mode 100644 studio/data/edge-functions/edge-functions-query.ts create mode 100644 studio/data/edge-functions/keys.ts diff --git a/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx new file mode 100644 index 00000000000..4f25d522982 --- /dev/null +++ b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx @@ -0,0 +1,296 @@ +import dayjs from 'dayjs' +import { useRouter } from 'next/router' +import { FC, useState, useEffect } from 'react' +import * as Tooltip from '@radix-ui/react-tooltip' +import { PermissionAction } from '@supabase/shared-types/out/constants' +import { Alert, IconGlobe, IconTerminal, IconMinimize2, IconMaximize2, Button, Modal } from 'ui' + +import { useStore, useParams, checkPermissions } from 'hooks' +import Panel from 'components/ui/Panel' +import CommandRender from './CommandRender' +import { useProjectApiQuery } from 'data/config/project-api-query' +import { useEdgeFunctionDeleteMutation } from 'data/edge-functions/edge-functions-delete-mutation' + +interface Props {} + +const EdgeFunctionDetails: FC = () => { + const router = useRouter() + const { functions, ui } = useStore() + const { ref: projectRef, id } = useParams() + const [showDeleteModal, setShowDeleteModal] = useState(false) + const [showInstructions, setShowInstructions] = useState(false) + const [selectedFunction, setSelectedFunction] = useState(null) + + const canDeleteEdgeFunction = checkPermissions(PermissionAction.FUNCTIONS_WRITE, '*') + const { mutateAsync: deleteEdgeFunction, isLoading: isDeleting } = useEdgeFunctionDeleteMutation() + + useEffect(() => { + setSelectedFunction(functions.byId(id)) + }, [functions.isLoaded, ui.selectedProject]) + + const { data: settings } = useProjectApiQuery({ projectRef }) + const functionUrl = `https://${projectRef}.functions.supabase.co/${selectedFunction?.slug}` + + // get the .co or .net TLD from the restUrl + const restUrl = ui.selectedProject?.restUrl + const restUrlTld = new URL(restUrl as string).hostname.split('.').pop() + + // Get the API service + const apiService = settings?.autoApiService + const anonKey = apiService?.service_api_keys.find((x) => x.name === 'anon key') + ? apiService.defaultApiKey + : undefined + + const endpoint = apiService?.app_config.endpoint ?? '' + const endpointSections = endpoint.split('.') + const functionsEndpoint = [ + ...endpointSections.slice(0, 1), + 'functions', + ...endpointSections.slice(1), + ].join('.') + + const onConfirmDelete = async () => { + if (!projectRef) return console.error('Project ref is required') + if (selectedFunction === undefined) return console.error('No edge function selected') + + try { + await deleteEdgeFunction({ projectRef, slug: selectedFunction.slug }) + ui.setNotification({ + category: 'success', + message: `Succesfully deleted "${selectedFunction.name}"`, + }) + router.push(`/project/${projectRef}/functions`) + } catch (error: any) { + ui.setNotification({ + category: 'error', + message: `Failed to delete function: ${error.message}`, + }) + } + } + + const managementCommands: any = [ + { + command: `supabase functions deploy ${selectedFunction?.slug}`, + description: 'This will overwrite the deployed function with your new function', + jsx: () => { + return ( + <> + supabase functions deploy{' '} + {selectedFunction?.slug} + + ) + }, + comment: 'Deploy a new version', + }, + { + command: `supabase functions delete ${selectedFunction?.slug}`, + description: 'This will remove the function and all the logs associated with it', + jsx: () => { + return ( + <> + supabase functions delete{' '} + {selectedFunction?.slug} + + ) + }, + comment: 'Delete the function', + }, + ] + + const secretCommands: any = [ + { + command: `supabase secrets list`, + description: 'This will list all the secrets for your project', + jsx: () => { + return ( + <> + supabase secrets list + + ) + }, + comment: 'View all secrets', + }, + { + command: `supabase secrets set NAME1=VALUE1 NAME2=VALUE2`, + description: 'This will set secrets for your project', + jsx: () => { + return ( + <> + supabase secrets set NAME1=VALUE1 NAME2=VALUE2 + + ) + }, + comment: 'Set secrets for your project', + }, + { + command: `supabase secrets unset NAME1 NAME2 `, + description: 'This will delete secrets for your project', + jsx: () => { + return ( + <> + supabase secrets unset NAME1 NAME2 + + ) + }, + comment: 'Unset secrets for your project', + }, + ] + + const invokeCommands: any = [ + { + command: `curl -L -X POST 'https://${projectRef}.functions.supabase.${restUrlTld}/${ + selectedFunction?.slug + }' -H 'Authorization: Bearer ${anonKey ?? '[YOUR ANON KEY]'}' --data '{"name":"Functions"}'`, + description: 'Invokes the hello function', + jsx: () => { + return ( + <> + curl -L -X POST 'https://{functionsEndpoint}/ + {selectedFunction?.slug}' -H 'Authorization: Bearer [YOUR ANON KEY]'{' '} + {`--data '{"name":"Functions"}'`} + + ) + }, + comment: 'Invoke your function', + }, + ] + + return ( + <> +
+
+
+

Function Name

+

{selectedFunction?.name}

+
+ +
+

Endpoint URL

+

{functionUrl}

+
+ +
+

Created At

+

+ {selectedFunction?.created_at && + dayjs(selectedFunction.created_at).format('dddd, MMMM D, YYYY h:mm A')} +

+
+ +
+

Last Updated At

+

+ {selectedFunction?.updated_at && + dayjs(selectedFunction.updated_at).format('dddd, MMMM D, YYYY h:mm A')} +

+
+ +
+ Deployment +
{selectedFunction?.version}
+
+ +
+ Regions +
+
+ + Earth +
+ All functions are deployed globally +
+
+
+ +
+
+
+
+ +
+

Command line access

+
+
setShowInstructions(!showInstructions)}> + {showInstructions ? ( + + ) : ( + + )} +
+
+ +
Deployment management
+ +
Invoke
+ +
Secrets management
+ +
+ + Delete Edge Function

}> + + +

+ Make sure you have made a backup if you want to restore your edge function +

+ + + + + {!canDeleteEdgeFunction && ( + + +
+ + You need additional permissions to delete an edge function + +
+
+ )} +
+
+
+
+
+ + Confirm to delete {selectedFunction?.name}} + visible={showDeleteModal} + loading={isDeleting} + onCancel={() => setShowDeleteModal(false)} + onConfirm={onConfirmDelete} + > +
+ + + Ensure that you have made a backup if you want to restore your edge function + + +
+
+ + ) +} + +export default EdgeFunctionDetails diff --git a/studio/components/interfaces/Functions/EdgeFunctionsList.tsx b/studio/components/interfaces/Functions/EdgeFunctionsList.tsx deleted file mode 100644 index 0ff6d7aa81b..00000000000 --- a/studio/components/interfaces/Functions/EdgeFunctionsList.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import dayjs from 'dayjs' -import { FC, useState } from 'react' -import * as Tooltip from '@radix-ui/react-tooltip' - -import { useParams, useStore } from 'hooks' -import Table from 'components/to-be-cleaned/Table' -import FunctionsListItem from './FunctionsListItem' -import { Function } from 'components/interfaces/Functions/Functions.types' -import { IconUpload, IconCheck, IconClipboard, IconClock } from 'ui' -import { useRouter } from 'next/router' - -interface Props {} - -const EdgeFunctionsList: FC = () => { - const router = useRouter() - const { ref } = useParams() - const { ui, functions: functionsStore } = useStore() - const functions: Function[] = functionsStore.list() ?? [] - - const restUrl = ui.selectedProject?.restUrl - const restUrlTld = new URL(restUrl as string).hostname.split('.').pop() - - return ( -
-
- {functions.map((fn) => { - const [isCopied, setIsCopied] = useState(false) - const functionUrl = `https://${ref}.functions.supabase.${restUrlTld}/${fn.slug}` - - return ( -
router.push(`/project/${ref}/functions/${fn.id}`)} - > -
-

{fn.name}

-
-

{functionUrl}

- -
-
-
- - -

Deployment {fn.version}

-
- - -
- - Deployment history coming soon - -
-
-
- - -
- -

{dayjs(fn.updated_at).fromNow()}

-
-
- - -
- - Last updated on {dayjs(fn.updated_at).format('DD MMM, YYYY HH:mm')} - -
-
-
-
-
- ) - })} -
-
- ) -} - -export default EdgeFunctionsList diff --git a/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx b/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx new file mode 100644 index 00000000000..185f01348e3 --- /dev/null +++ b/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx @@ -0,0 +1,111 @@ +import dayjs from 'dayjs' +import { useRouter } from 'next/router' +import { FC, useState } from 'react' +import * as Tooltip from '@radix-ui/react-tooltip' +import { IconUpload, IconCheck, IconClipboard } from 'ui' + +import { useParams, useStore } from 'hooks' +import { EdgeFunctionsResponse } from 'data/edge-functions/edge-functions-query' + +interface Props { + fn: EdgeFunctionsResponse +} + +const EdgeFunctionsRow: FC = ({ fn }) => { + const router = useRouter() + const { ref } = useParams() + const { ui } = useStore() + const [isCopied, setIsCopied] = useState(false) + + const restUrl = ui.selectedProject?.restUrl + const restUrlTld = new URL(restUrl as string).hostname.split('.').pop() + const functionUrl = `https://${ref}.functions.supabase.${restUrlTld}/${fn.slug}` + + return ( +
router.push(`/project/${ref}/functions/${fn.id}`)} + > +
+

{fn.name}

+
+

{functionUrl}

+ +
+
+
+ + +

Deployment {fn.version}

+
+ + +
+ Deployment history coming soon +
+
+
+ + +
+ +

{dayjs(fn.updated_at).fromNow()}

+
+
+ + +
+ + Last updated on {dayjs(fn.updated_at).format('DD MMM, YYYY HH:mm')} + +
+
+
+
+
+ ) +} + +export default EdgeFunctionsRow diff --git a/studio/components/interfaces/Functions/FunctionsListItem.tsx b/studio/components/interfaces/Functions/FunctionsListItem.tsx deleted file mode 100644 index f2b9045d417..00000000000 --- a/studio/components/interfaces/Functions/FunctionsListItem.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import dayjs from 'dayjs' -import { FC } from 'react' -import { useRouter } from 'next/router' -import { observer } from 'mobx-react-lite' - -import { useStore } from 'hooks' -import Table from 'components/to-be-cleaned/Table' - -interface Props { - function: any -} - -const FunctionsListItem: FC = ({ function: item }) => { - const { ui } = useStore() - const is_functionConfirmed = item.email_confirmed_at || item.phone_confirmed_at - - const router = useRouter() - const ref = ui?.selectedProject?.ref - - // get the .co or .net TLD from the restUrl - const restUrl = ui.selectedProject?.restUrl - const restUrlTld = new URL(restUrl as string).hostname.split('.').pop() - - return ( - { - router.push(`/project/${ref}/functions/${item.id}`) - }} - > - -
- {item.name} -
-
- -
- {`https://${ref}.functions.supabase.${restUrlTld}/${item.slug}`} - {`/${item.name}`} -
-
- - - {dayjs(item.created_at).format('DD MMM, YYYY HH:mm')} - - - - - {dayjs(item.updated_at).format('DD MMM, YYYY HH:mm')} - - - - v{item.version} - - - {item.status} - -
- ) -} - -export default observer(FunctionsListItem) diff --git a/studio/components/interfaces/Functions/FunctionsNav.tsx b/studio/components/interfaces/Functions/FunctionsNav.tsx index dfe6bd5ab13..992a11474d0 100644 --- a/studio/components/interfaces/Functions/FunctionsNav.tsx +++ b/studio/components/interfaces/Functions/FunctionsNav.tsx @@ -1,8 +1,7 @@ import { Tabs } from 'ui' import { useRouter } from 'next/router' -// @ts-ignore -const FunctionsNav = ({ item }) => { +const FunctionsNav = ({ item }: any) => { const router = useRouter() const activeRoute = router.pathname.split('/')[5] const { ref } = router.query @@ -19,7 +18,6 @@ const FunctionsNav = ({ item }) => { > - {/* */} diff --git a/studio/components/interfaces/Functions/index.ts b/studio/components/interfaces/Functions/index.ts index e2d3d9dda40..af628ba5d81 100644 --- a/studio/components/interfaces/Functions/index.ts +++ b/studio/components/interfaces/Functions/index.ts @@ -1,6 +1,5 @@ -import EdgeFunctionsList from './EdgeFunctionsList' -import FunctionsListItem from './FunctionsListItem' +import EdgeFunctionDetails from './EdgeFunctionDetails' import FunctionsEmptyState from './FunctionsEmptyState' import TerminalInstructions from './TerminalInstructions' -export { EdgeFunctionsList, FunctionsListItem, FunctionsEmptyState, TerminalInstructions } +export { EdgeFunctionDetails, FunctionsEmptyState, TerminalInstructions } diff --git a/studio/components/layouts/FunctionsLayout.tsx b/studio/components/layouts/FunctionsLayout.tsx index 67d65717dea..0dc242cd836 100644 --- a/studio/components/layouts/FunctionsLayout.tsx +++ b/studio/components/layouts/FunctionsLayout.tsx @@ -106,6 +106,26 @@ const FunctionsLayout: FC = ({ title, children }) => {

Beta

+ {name && ( +
+ + + + + +
{name}
+
+ )}
- - {/* {name && ( - <> - - - - - -
{name}
- - )} */} {item && } diff --git a/studio/data/edge-functions/edge-functions-delete-mutation.ts b/studio/data/edge-functions/edge-functions-delete-mutation.ts new file mode 100644 index 00000000000..23552bb4e48 --- /dev/null +++ b/studio/data/edge-functions/edge-functions-delete-mutation.ts @@ -0,0 +1,43 @@ +import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' +import { delete_ } from 'lib/common/fetch' +import { API_ADMIN_URL } from 'lib/constants' +import { edgeFunctionsKeys } from './keys' + +export type EdgeFunctionsDeleteVariables = { + projectRef: string + slug: string +} + +export async function deleteEdgeFunction({ projectRef, slug }: EdgeFunctionsDeleteVariables) { + if (!projectRef) throw new Error('projectRef is required') + + const response = await delete_(`${API_ADMIN_URL}/projects/${projectRef}/functions/${slug}`, {}) + if (response.error) { + throw response.error + } + + return response +} + +type EdgeFunctionsDeleteData = Awaited> + +export const useEdgeFunctionDeleteMutation = ({ + onSuccess, + ...options +}: Omit< + UseMutationOptions, + 'mutationFn' +> = {}) => { + const queryClient = useQueryClient() + + return useMutation( + (vars) => deleteEdgeFunction(vars), + { + async onSuccess(data, variables, context) { + await queryClient.invalidateQueries(edgeFunctionsKeys.list('edge-functions')) + await onSuccess?.(data, variables, context) + }, + ...options, + } + ) +} diff --git a/studio/data/edge-functions/edge-functions-query.ts b/studio/data/edge-functions/edge-functions-query.ts new file mode 100644 index 00000000000..28bc3f8c649 --- /dev/null +++ b/studio/data/edge-functions/edge-functions-query.ts @@ -0,0 +1,57 @@ +import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' +import { get } from 'lib/common/fetch' +import { API_ADMIN_URL } from 'lib/constants' +import { useCallback } from 'react' +import { edgeFunctionsKeys } from './keys' + +export type EdgeFunctionsVariables = { projectRef?: string } + +export type EdgeFunctionsResponse = { + id: string + name: string + slug: string + status: string + version: number + created_at: number + updated_at: number + verify_jwt: boolean + import_map: boolean +} + +export async function getEdgeFunctions( + { projectRef }: EdgeFunctionsVariables, + signal?: AbortSignal +) { + if (!projectRef) throw new Error('projectRef is required') + + const response = await get(`${API_ADMIN_URL}/projects/${projectRef}/functions`, { + signal, + }) + if (response.error) throw response.error + return response as EdgeFunctionsResponse[] +} + +export type EdgeFunctionsData = Awaited> +export type EdgeFunctionsError = unknown + +export const useEdgeFunctionsQuery = ( + { projectRef }: EdgeFunctionsVariables, + { enabled = true, ...options }: UseQueryOptions = {} +) => + useQuery( + edgeFunctionsKeys.list(projectRef), + ({ signal }) => getEdgeFunctions({ projectRef }, signal), + { enabled: enabled && typeof projectRef !== 'undefined', ...options } + ) + +export const useEdgeFunctionsPrefetch = ({ projectRef }: EdgeFunctionsVariables) => { + const client = useQueryClient() + + return useCallback(() => { + if (projectRef) { + client.prefetchQuery(edgeFunctionsKeys.list(projectRef), ({ signal }) => + getEdgeFunctions({ projectRef }, signal) + ) + } + }, [projectRef]) +} diff --git a/studio/data/edge-functions/keys.ts b/studio/data/edge-functions/keys.ts new file mode 100644 index 00000000000..dacd8d4a8dd --- /dev/null +++ b/studio/data/edge-functions/keys.ts @@ -0,0 +1,3 @@ +export const edgeFunctionsKeys = { + list: (projectRef: string | undefined) => ['projects', projectRef, 'edge-functions'] as const, +} diff --git a/studio/pages/project/[ref]/functions/[id]/details.tsx b/studio/pages/project/[ref]/functions/[id]/details.tsx index 613833e4f31..debcd5ebe99 100644 --- a/studio/pages/project/[ref]/functions/[id]/details.tsx +++ b/studio/pages/project/[ref]/functions/[id]/details.tsx @@ -1,244 +1,21 @@ -import dayjs from 'dayjs' -import { useEffect, useState } from 'react' import { observer } from 'mobx-react-lite' -import { IconGlobe, IconTerminal } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' import { NextPageWithLayout } from 'types' -import { checkPermissions, useParams, useStore } from 'hooks' -import { useProjectApiQuery } from 'data/config/project-api-query' +import { checkPermissions, useParams } from 'hooks' import FunctionsLayout from 'components/layouts/FunctionsLayout' -import CommandRender from 'components/interfaces/Functions/CommandRender' import NoPermission from 'components/ui/NoPermission' +import { EdgeFunctionDetails } from 'components/interfaces/Functions' const PageLayout: NextPageWithLayout = () => { - const { ref: projectRef, id } = useParams() - - const { functions, ui } = useStore() - - const [selectedFunction, setSelectedFunction] = useState(null) - - useEffect(() => { - setSelectedFunction(functions.byId(id)) - }, [functions.isLoaded, ui.selectedProject]) - - const { data: settings } = useProjectApiQuery({ - projectRef, - }) - - // get the .co or .net TLD from the restUrl - const restUrl = ui.selectedProject?.restUrl - const restUrlTld = new URL(restUrl as string).hostname.split('.').pop() - - const managementCommands: any = [ - { - command: `supabase functions deploy ${selectedFunction?.slug}`, - description: 'This will overwrite the deployed function with your new function', - jsx: () => { - return ( - <> - supabase functions deploy{' '} - {selectedFunction?.slug} - - ) - }, - comment: 'Deploy a new version', - }, - { - command: `supabase functions delete ${selectedFunction?.slug}`, - description: 'This will remove the function and all the logs associated with it', - jsx: () => { - return ( - <> - supabase functions delete{' '} - {selectedFunction?.slug} - - ) - }, - comment: 'Delete the function', - }, - ] - - const secretCommands: any = [ - { - command: `supabase secrets list`, - description: 'This will list all the secrets for your project', - jsx: () => { - return ( - <> - supabase secrets list - - ) - }, - comment: 'View all secrets', - }, - { - command: `supabase secrets set NAME1=VALUE1 NAME2=VALUE2`, - description: 'This will set secrets for your project', - jsx: () => { - return ( - <> - supabase secrets set NAME1=VALUE1 NAME2=VALUE2 - - ) - }, - comment: 'Set secrets for your project', - }, - { - command: `supabase secrets unset NAME1 NAME2 `, - description: 'This will delete secrets for your project', - jsx: () => { - return ( - <> - supabase secrets unset NAME1 NAME2 - - ) - }, - comment: 'Unset secrets for your project', - }, - ] - - // Get the API service - const apiService = settings?.autoApiService - const anonKey = apiService?.service_api_keys.find((x) => x.name === 'anon key') - ? apiService.defaultApiKey - : undefined - - const endpoint = apiService?.app_config.endpoint ?? '' - const endpointSections = endpoint.split('.') - const functionsEndpoint = [ - ...endpointSections.slice(0, 1), - 'functions', - ...endpointSections.slice(1), - ].join('.') - - const invokeCommands: any = [ - { - command: `curl -L -X POST 'https://${projectRef}.functions.supabase.${restUrlTld}/${ - selectedFunction?.slug - }' -H 'Authorization: Bearer ${anonKey ?? '[YOUR ANON KEY]'}' --data '{"name":"Functions"}'`, - description: 'Invokes the hello function', - jsx: () => { - return ( - <> - curl -L -X POST 'https://{functionsEndpoint}/ - {selectedFunction?.slug}' -H 'Authorization: Bearer [YOUR ANON KEY]'{' '} - {`--data '{"name":"Functions"}'`} - - ) - }, - comment: 'Invoke your function', - }, - ] + const { id } = useParams() const canReadFunction = checkPermissions(PermissionAction.FUNCTIONS_READ, id as string) if (!canReadFunction) { return } - return ( -
-
-
-
-
- Function Name -
{selectedFunction?.name}
-
- -
- Status -
-
-
- {selectedFunction?.status} -
- - - - -
-
-
-
-
-
- -
- Endpoint URL -
- {`https://${projectRef}.functions.supabase.co/${selectedFunction?.slug}`} -
-
- -
- Created At -
- {selectedFunction?.created_at && - dayjs(selectedFunction.created_at).format('dddd, MMMM D, YYYY h:mm A')} -
-
- -
- Updated At -
- {selectedFunction?.updated_at && - dayjs(selectedFunction.updated_at).format('dddd, MMMM D, YYYY h:mm A')} -
-
- -
- Version -
v{selectedFunction?.version}
-
- -
- Regions -
-
- - Earth -
- All functions are deployed globally -
-
-
-
- -
-
-
-
- -
-

Command line access

-
- -
Deployment management
- -
Invoke
- -
Secrets management
- -
-
-
- ) + return } PageLayout.getLayout = (page) => {page} diff --git a/studio/pages/project/[ref]/functions/index.tsx b/studio/pages/project/[ref]/functions/index.tsx index 7848cff03fe..480e1700d87 100644 --- a/studio/pages/project/[ref]/functions/index.tsx +++ b/studio/pages/project/[ref]/functions/index.tsx @@ -1,16 +1,33 @@ import { observer } from 'mobx-react-lite' -import { useStore } from 'hooks' +import { useParams } from 'hooks' import { NextPageWithLayout } from 'types' import FunctionsLayout from 'components/layouts/FunctionsLayout' -import { EdgeFunctionsList, FunctionsEmptyState } from 'components/interfaces/Functions' +import { FunctionsEmptyState } from 'components/interfaces/Functions' +import { useEdgeFunctionsQuery } from 'data/edge-functions/edge-functions-query' +import ShimmeringLoader from 'components/ui/ShimmeringLoader' +import EdgeFunctionsRow from 'components/interfaces/Functions/EdgeFunctionsRow' const PageLayout: NextPageWithLayout = () => { - const { functions } = useStore() - const hasFunctions = functions.list().length > 0 + const { ref } = useParams() + const { isLoading, data: functions } = useEdgeFunctionsQuery({ projectRef: ref }) + + const hasFunctions = (functions ?? []).length > 0 + + if (isLoading || functions === undefined) { + return ( +
+ + + +
+ ) + } return hasFunctions ? (
- + {(functions ?? []).map((fn) => ( + + ))}
) : ( From 3873db7da4e1c7ac8711b8ff694768c01e9fea65 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 20 Feb 2023 12:02:43 +0800 Subject: [PATCH 3/9] Add comment --- studio/components/interfaces/Functions/EdgeFunctionDetails.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx index 4f25d522982..80b929e7324 100644 --- a/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx +++ b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx @@ -13,6 +13,8 @@ import { useEdgeFunctionDeleteMutation } from 'data/edge-functions/edge-function interface Props {} +// [Joshen] Next - additional configs: Verify jwt + import maps + const EdgeFunctionDetails: FC = () => { const router = useRouter() const { functions, ui } = useStore() From 0247e1b8c32cd272696e57ba2e1fce25fdaa594b Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 20 Feb 2023 12:03:29 +0800 Subject: [PATCH 4/9] Fix typo --- studio/components/interfaces/Functions/EdgeFunctionDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx index 80b929e7324..72b73e3e117 100644 --- a/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx +++ b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx @@ -59,7 +59,7 @@ const EdgeFunctionDetails: FC = () => { await deleteEdgeFunction({ projectRef, slug: selectedFunction.slug }) ui.setNotification({ category: 'success', - message: `Succesfully deleted "${selectedFunction.name}"`, + message: `Successfully deleted "${selectedFunction.name}"`, }) router.push(`/project/${projectRef}/functions`) } catch (error: any) { From dbebd83937c8623df0bda4c50dd6c5a1cdd5f394 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 20 Feb 2023 14:21:51 +0800 Subject: [PATCH 5/9] Small style fix --- studio/components/interfaces/Functions/EdgeFunctionsRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx b/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx index 185f01348e3..e9df8387450 100644 --- a/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx +++ b/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx @@ -26,7 +26,7 @@ const EdgeFunctionsRow: FC = ({ fn }) => { key={fn.id} className={[ 'border border-scale-500 border-t-0 first:rounded-t first:border-t last:rounded-b', - 'bg-scale-100 dark:bg-scale-300 py-6 px-6 flex items-center justify-between', + 'bg-scale-100 dark:bg-scale-300 py-4 px-6 flex items-center justify-between', 'hover:border-t hover:-mt-[1px] first:!mt-0 cursor-pointer', 'hover:bg-scale-200 dark:hover:bg-scale-500 hover:border-scale-500 dark:hover:border-scale-700', ].join(' ')} From 7d4721f3e297e09fcd772099cffdd75e340012f4 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 20 Feb 2023 14:48:27 +0800 Subject: [PATCH 6/9] Fixes based on feedback --- .../Functions/EdgeFunctionDetails.tsx | 52 +++++++++++++++---- .../interfaces/Functions/EdgeFunctionsRow.tsx | 2 +- .../edge-functions-delete-mutation.ts | 2 +- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx index 72b73e3e117..f2f21e65c04 100644 --- a/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx +++ b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx @@ -3,7 +3,17 @@ import { useRouter } from 'next/router' import { FC, useState, useEffect } from 'react' import * as Tooltip from '@radix-ui/react-tooltip' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { Alert, IconGlobe, IconTerminal, IconMinimize2, IconMaximize2, Button, Modal } from 'ui' +import { + Alert, + IconGlobe, + IconTerminal, + IconMinimize2, + IconMaximize2, + IconCheck, + IconClipboard, + Button, + Modal, +} from 'ui' import { useStore, useParams, checkPermissions } from 'hooks' import Panel from 'components/ui/Panel' @@ -19,6 +29,7 @@ const EdgeFunctionDetails: FC = () => { const router = useRouter() const { functions, ui } = useStore() const { ref: projectRef, id } = useParams() + const [isCopied, setIsCopied] = useState(false) const [showDeleteModal, setShowDeleteModal] = useState(false) const [showInstructions, setShowInstructions] = useState(false) const [selectedFunction, setSelectedFunction] = useState(null) @@ -31,11 +42,6 @@ const EdgeFunctionDetails: FC = () => { }, [functions.isLoaded, ui.selectedProject]) const { data: settings } = useProjectApiQuery({ projectRef }) - const functionUrl = `https://${projectRef}.functions.supabase.co/${selectedFunction?.slug}` - - // get the .co or .net TLD from the restUrl - const restUrl = ui.selectedProject?.restUrl - const restUrlTld = new URL(restUrl as string).hostname.split('.').pop() // Get the API service const apiService = settings?.autoApiService @@ -50,6 +56,7 @@ const EdgeFunctionDetails: FC = () => { 'functions', ...endpointSections.slice(1), ].join('.') + const functionUrl = `https://${functionsEndpoint}/${selectedFunction?.slug}` const onConfirmDelete = async () => { if (!projectRef) return console.error('Project ref is required') @@ -140,9 +147,9 @@ const EdgeFunctionDetails: FC = () => { const invokeCommands: any = [ { - command: `curl -L -X POST 'https://${projectRef}.functions.supabase.${restUrlTld}/${ - selectedFunction?.slug - }' -H 'Authorization: Bearer ${anonKey ?? '[YOUR ANON KEY]'}' --data '{"name":"Functions"}'`, + command: `curl -L -X POST '${functionUrl}' -H 'Authorization: Bearer ${ + anonKey ?? '[YOUR ANON KEY]' + }' --data '{"name":"Functions"}'`, description: 'Invokes the hello function', jsx: () => { return ( @@ -169,6 +176,33 @@ const EdgeFunctionDetails: FC = () => {

Endpoint URL

{functionUrl}

+
diff --git a/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx b/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx index e9df8387450..a263b6aff26 100644 --- a/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx +++ b/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx @@ -58,7 +58,7 @@ const EdgeFunctionsRow: FC = ({ fn }) => { ) : (
- +
)} diff --git a/studio/data/edge-functions/edge-functions-delete-mutation.ts b/studio/data/edge-functions/edge-functions-delete-mutation.ts index 23552bb4e48..f37faa08a37 100644 --- a/studio/data/edge-functions/edge-functions-delete-mutation.ts +++ b/studio/data/edge-functions/edge-functions-delete-mutation.ts @@ -34,7 +34,7 @@ export const useEdgeFunctionDeleteMutation = ({ (vars) => deleteEdgeFunction(vars), { async onSuccess(data, variables, context) { - await queryClient.invalidateQueries(edgeFunctionsKeys.list('edge-functions')) + await queryClient.invalidateQueries(edgeFunctionsKeys.list(variables.projectRef)) await onSuccess?.(data, variables, context) }, ...options, From cb49607cd89efc11454edd2f3753ff75619c330c Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 20 Feb 2023 21:12:51 +0800 Subject: [PATCH 7/9] Reinstate table component for functions list --- ...tionsRow.tsx => EdgeFunctionsListItem.tsx} | 78 +++++++++---------- .../components/interfaces/Functions/index.ts | 3 +- .../pages/project/[ref]/functions/index.tsx | 34 ++++++-- 3 files changed, 64 insertions(+), 51 deletions(-) rename studio/components/interfaces/Functions/{EdgeFunctionsRow.tsx => EdgeFunctionsListItem.tsx} (54%) diff --git a/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx b/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx similarity index 54% rename from studio/components/interfaces/Functions/EdgeFunctionsRow.tsx rename to studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx index a263b6aff26..0f8a00ecb8b 100644 --- a/studio/components/interfaces/Functions/EdgeFunctionsRow.tsx +++ b/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx @@ -1,41 +1,44 @@ import dayjs from 'dayjs' -import { useRouter } from 'next/router' import { FC, useState } from 'react' +import { useRouter } from 'next/router' +import { observer } from 'mobx-react-lite' +import { IconCheck, IconClipboard } from 'ui' import * as Tooltip from '@radix-ui/react-tooltip' -import { IconUpload, IconCheck, IconClipboard } from 'ui' import { useParams, useStore } from 'hooks' +import Table from 'components/to-be-cleaned/Table' import { EdgeFunctionsResponse } from 'data/edge-functions/edge-functions-query' interface Props { - fn: EdgeFunctionsResponse + function: EdgeFunctionsResponse } -const EdgeFunctionsRow: FC = ({ fn }) => { +const EdgeFunctionsListItem: FC = ({ function: item }) => { const router = useRouter() - const { ref } = useParams() const { ui } = useStore() + const { ref } = useParams() const [isCopied, setIsCopied] = useState(false) + // get the .co or .net TLD from the restUrl const restUrl = ui.selectedProject?.restUrl const restUrlTld = new URL(restUrl as string).hostname.split('.').pop() - const functionUrl = `https://${ref}.functions.supabase.${restUrlTld}/${fn.slug}` + const functionUrl = `https://${ref}.functions.supabase.${restUrlTld}/${item.slug}` return ( -
router.push(`/project/${ref}/functions/${fn.id}`)} + { + router.push(`/project/${ref}/functions/${item.id}`) + }} > -
-

{fn.name}

-
-

{functionUrl}

+ +
+

{item.name}

+
+
+ +
+

{functionUrl}

-
-
- - -

Deployment {fn.version}

-
- - -
- Deployment history coming soon -
-
-
+ + +

{dayjs(item.created_at).format('DD MMM, YYYY HH:mm')}

+
+
- -

{dayjs(fn.updated_at).fromNow()}

+

{dayjs(item.updated_at).fromNow()}

@@ -98,14 +87,17 @@ const EdgeFunctionsRow: FC = ({ fn }) => { ].join(' ')} > - Last updated on {dayjs(fn.updated_at).format('DD MMM, YYYY HH:mm')} + Last updated on {dayjs(item.updated_at).format('DD MMM, YYYY HH:mm')}
-
-
+ + +

{item.version}

+
+ ) } -export default EdgeFunctionsRow +export default observer(EdgeFunctionsListItem) diff --git a/studio/components/interfaces/Functions/index.ts b/studio/components/interfaces/Functions/index.ts index af628ba5d81..003d5feaca6 100644 --- a/studio/components/interfaces/Functions/index.ts +++ b/studio/components/interfaces/Functions/index.ts @@ -1,5 +1,6 @@ import EdgeFunctionDetails from './EdgeFunctionDetails' +import EdgeFunctionsListItem from './EdgeFunctionsListItem' import FunctionsEmptyState from './FunctionsEmptyState' import TerminalInstructions from './TerminalInstructions' -export { EdgeFunctionDetails, FunctionsEmptyState, TerminalInstructions } +export { EdgeFunctionDetails, EdgeFunctionsListItem, FunctionsEmptyState, TerminalInstructions } diff --git a/studio/pages/project/[ref]/functions/index.tsx b/studio/pages/project/[ref]/functions/index.tsx index 480e1700d87..ae427eed957 100644 --- a/studio/pages/project/[ref]/functions/index.tsx +++ b/studio/pages/project/[ref]/functions/index.tsx @@ -1,11 +1,12 @@ import { observer } from 'mobx-react-lite' + import { useParams } from 'hooks' import { NextPageWithLayout } from 'types' +import Table from 'components/to-be-cleaned/Table' import FunctionsLayout from 'components/layouts/FunctionsLayout' -import { FunctionsEmptyState } from 'components/interfaces/Functions' -import { useEdgeFunctionsQuery } from 'data/edge-functions/edge-functions-query' +import { EdgeFunctionsListItem, FunctionsEmptyState } from 'components/interfaces/Functions' import ShimmeringLoader from 'components/ui/ShimmeringLoader' -import EdgeFunctionsRow from 'components/interfaces/Functions/EdgeFunctionsRow' +import { useEdgeFunctionsQuery } from 'data/edge-functions/edge-functions-query' const PageLayout: NextPageWithLayout = () => { const { ref } = useParams() @@ -24,10 +25,29 @@ const PageLayout: NextPageWithLayout = () => { } return hasFunctions ? ( -
- {(functions ?? []).map((fn) => ( - - ))} +
+
+ {`${functions.length} function${ + functions.length > 1 ? 's' : '' + } deployed`} +
+
+ Name + URL + Created + Last updated + Deployment + + } + body={ + <> + {functions.length > 0 && + functions.map((item: any) => )} + + } + /> ) : ( From 4918207adf74be1a46c8fb6eaaab772e2c1f7c34 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 21 Feb 2023 12:03:20 +0800 Subject: [PATCH 8/9] Clicking into edge functions will lead to details page --- studio/components/interfaces/Functions/EdgeFunctionDetails.tsx | 2 +- .../components/interfaces/Functions/EdgeFunctionsListItem.tsx | 2 +- studio/components/interfaces/Functions/FunctionsNav.tsx | 2 +- studio/pages/project/[ref]/functions/index.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx index f2f21e65c04..42b4986fc64 100644 --- a/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx +++ b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx @@ -222,7 +222,7 @@ const EdgeFunctionDetails: FC = () => {
- Deployment + Deployments
{selectedFunction?.version}
diff --git a/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx b/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx index 0f8a00ecb8b..0d9ce1f727b 100644 --- a/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx +++ b/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx @@ -28,7 +28,7 @@ const EdgeFunctionsListItem: FC = ({ function: item }) => { { - router.push(`/project/${ref}/functions/${item.id}`) + router.push(`/project/${ref}/functions/${item.id}/details`) }} > diff --git a/studio/components/interfaces/Functions/FunctionsNav.tsx b/studio/components/interfaces/Functions/FunctionsNav.tsx index 992a11474d0..af24e28d29f 100644 --- a/studio/components/interfaces/Functions/FunctionsNav.tsx +++ b/studio/components/interfaces/Functions/FunctionsNav.tsx @@ -16,8 +16,8 @@ const FunctionsNav = ({ item }: any) => { router.push(`/project/${ref}/functions/${item.id}/${e === 'metrics' ? '' : e}`) } > - + diff --git a/studio/pages/project/[ref]/functions/index.tsx b/studio/pages/project/[ref]/functions/index.tsx index ae427eed957..e78b41e0996 100644 --- a/studio/pages/project/[ref]/functions/index.tsx +++ b/studio/pages/project/[ref]/functions/index.tsx @@ -38,7 +38,7 @@ const PageLayout: NextPageWithLayout = () => { URL Created Last updated - Deployment + Deployments } body={ From dda1206cea566fcde8108813fe85e8688b6afb94 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 22 Feb 2023 13:27:54 +0800 Subject: [PATCH 9/9] Updates based on feedback --- .../components/interfaces/Functions/EdgeFunctionDetails.tsx | 2 +- studio/data/edge-functions/edge-functions-delete-mutation.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx index 42b4986fc64..c93c1fefb66 100644 --- a/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx +++ b/studio/components/interfaces/Functions/EdgeFunctionDetails.tsx @@ -56,7 +56,7 @@ const EdgeFunctionDetails: FC = () => { 'functions', ...endpointSections.slice(1), ].join('.') - const functionUrl = `https://${functionsEndpoint}/${selectedFunction?.slug}` + const functionUrl = `${apiService?.protocol}://${functionsEndpoint}/${selectedFunction?.slug}` const onConfirmDelete = async () => { if (!projectRef) return console.error('Project ref is required') diff --git a/studio/data/edge-functions/edge-functions-delete-mutation.ts b/studio/data/edge-functions/edge-functions-delete-mutation.ts index f37faa08a37..d642758cd9c 100644 --- a/studio/data/edge-functions/edge-functions-delete-mutation.ts +++ b/studio/data/edge-functions/edge-functions-delete-mutation.ts @@ -34,7 +34,10 @@ export const useEdgeFunctionDeleteMutation = ({ (vars) => deleteEdgeFunction(vars), { async onSuccess(data, variables, context) { - await queryClient.invalidateQueries(edgeFunctionsKeys.list(variables.projectRef)) + const { projectRef } = variables + await queryClient.invalidateQueries(edgeFunctionsKeys.list(projectRef), { + refetchType: 'all', + }) await onSuccess?.(data, variables, context) }, ...options,