Skip to content

Commit 669a96b

Browse files
authored
Chore/deprecate lib common fetch (supabase#36503)
* Deprecate use of getWithTimeout, refactor BuildingState and RestoringState to use RQ * Refactor profile-create-mutation to use data/fetchers, and edge-function-status-query to use fetch * Shift post from lib/common/fetch, refactor bucket-object-download-mutation * Address feedback * Minor fix * Smol fix
1 parent aa1d904 commit 669a96b

File tree

9 files changed

+112
-110
lines changed

9 files changed

+112
-110
lines changed

apps/studio/components/layouts/ProjectLayout/BuildingState.tsx

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,39 @@
11
import { useQueryClient } from '@tanstack/react-query'
2-
import ClientLibrary from 'components/interfaces/Home/ClientLibrary'
3-
import ExampleProject from 'components/interfaces/Home/ExampleProject'
4-
import { CLIENT_LIBRARIES, EXAMPLE_PROJECTS } from 'components/interfaces/Home/Home.constants'
2+
import { ArrowRight, Loader2 } from 'lucide-react'
53
import Link from 'next/link'
6-
import { useEffect, useRef } from 'react'
74

85
import { useParams } from 'common'
6+
import ClientLibrary from 'components/interfaces/Home/ClientLibrary'
7+
import ExampleProject from 'components/interfaces/Home/ExampleProject'
8+
import { CLIENT_LIBRARIES, EXAMPLE_PROJECTS } from 'components/interfaces/Home/Home.constants'
99
import { DisplayApiSettings, DisplayConfigSettings } from 'components/ui/ProjectSettings'
1010
import { invalidateProjectDetailsQuery } from 'data/projects/project-detail-query'
11+
import { useProjectStatusQuery } from 'data/projects/project-status-query'
1112
import { invalidateProjectsQuery } from 'data/projects/projects-query'
1213
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
13-
import { getWithTimeout } from 'lib/common/fetch'
14-
import { API_URL, PROJECT_STATUS } from 'lib/constants'
15-
import { ArrowRight, Loader2 } from 'lucide-react'
14+
import { PROJECT_STATUS } from 'lib/constants'
1615
import { Badge, Button } from 'ui'
1716

1817
const BuildingState = () => {
1918
const { ref } = useParams()
2019
const project = useSelectedProject()
2120
const queryClient = useQueryClient()
22-
const checkServerInterval = useRef<number>()
23-
24-
// TODO: move to react-query
25-
async function checkServer() {
26-
if (!project) return
2721

28-
const projectStatus = await getWithTimeout(`${API_URL}/projects/${project.ref}/status`, {
29-
timeout: 2000,
30-
})
31-
if (projectStatus && !projectStatus.error) {
32-
const { status } = projectStatus
33-
if (status === PROJECT_STATUS.ACTIVE_HEALTHY) {
34-
clearInterval(checkServerInterval.current)
35-
if (ref) await invalidateProjectDetailsQuery(queryClient, ref)
36-
await invalidateProjectsQuery(queryClient)
37-
}
22+
useProjectStatusQuery(
23+
{ projectRef: ref },
24+
{
25+
enabled: project?.status !== PROJECT_STATUS.ACTIVE_HEALTHY,
26+
refetchInterval: (res) => {
27+
return res?.status === PROJECT_STATUS.ACTIVE_HEALTHY ? false : 4000
28+
},
29+
onSuccess: async (res) => {
30+
if (res.status === PROJECT_STATUS.ACTIVE_HEALTHY) {
31+
if (ref) invalidateProjectDetailsQuery(queryClient, ref)
32+
invalidateProjectsQuery(queryClient)
33+
}
34+
},
3835
}
39-
}
40-
41-
useEffect(() => {
42-
// check server status every 4s
43-
checkServerInterval.current = window.setInterval(checkServer, 4000)
44-
return () => {
45-
clearInterval(checkServerInterval.current)
46-
}
47-
}, [])
36+
)
4837

4938
if (project === undefined) return null
5039

apps/studio/components/layouts/ProjectLayout/RestoringState.tsx

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
import { useQueryClient } from '@tanstack/react-query'
22
import { CheckCircle, Download, Loader } from 'lucide-react'
33
import Link from 'next/link'
4-
import { useEffect, useRef, useState } from 'react'
4+
import { useState } from 'react'
55

66
import { useParams } from 'common'
77
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
88
import { useBackupDownloadMutation } from 'data/database/backup-download-mutation'
99
import { useDownloadableBackupQuery } from 'data/database/backup-query'
10-
import { projectKeys } from 'data/projects/keys'
1110
import { invalidateProjectDetailsQuery } from 'data/projects/project-detail-query'
12-
import { getWithTimeout } from 'lib/common/fetch'
13-
import { API_URL, PROJECT_STATUS } from 'lib/constants'
11+
import { useProjectStatusQuery } from 'data/projects/project-status-query'
12+
import { PROJECT_STATUS } from 'lib/constants'
1413
import { Button } from 'ui'
1514
import { useProjectContext } from './ProjectContext'
1615

1716
const RestoringState = () => {
1817
const { ref } = useParams()
1918
const queryClient = useQueryClient()
2019
const { project } = useProjectContext()
21-
const checkServerInterval = useRef<number>()
2220

2321
const [loading, setLoading] = useState(false)
2422
const [isCompleted, setIsCompleted] = useState(false)
@@ -27,6 +25,23 @@ const RestoringState = () => {
2725
const backups = data?.backups ?? []
2826
const logicalBackups = backups.filter((b) => !b.isPhysicalBackup)
2927

28+
useProjectStatusQuery(
29+
{ projectRef: ref },
30+
{
31+
enabled: project?.status !== PROJECT_STATUS.ACTIVE_HEALTHY,
32+
refetchInterval: (res) => {
33+
return res?.status === PROJECT_STATUS.ACTIVE_HEALTHY ? false : 4000
34+
},
35+
onSuccess: async (res) => {
36+
if (res.status === PROJECT_STATUS.ACTIVE_HEALTHY) {
37+
setIsCompleted(true)
38+
} else {
39+
if (ref) invalidateProjectDetailsQuery(queryClient, ref)
40+
}
41+
},
42+
}
43+
)
44+
3045
const { mutate: downloadBackup, isLoading: isDownloading } = useBackupDownloadMutation({
3146
onSuccess: (res) => {
3247
const { fileUrl } = res
@@ -47,35 +62,13 @@ const RestoringState = () => {
4762
downloadBackup({ ref, backup: logicalBackups[0] })
4863
}
4964

50-
async function checkServer() {
51-
if (!project) return
52-
53-
const projectStatus = await getWithTimeout(`${API_URL}/projects/${project.ref}/status`, {
54-
timeout: 2000,
55-
})
56-
if (projectStatus && !projectStatus.error) {
57-
const { status } = projectStatus
58-
if (status === PROJECT_STATUS.ACTIVE_HEALTHY) {
59-
clearInterval(checkServerInterval.current)
60-
setIsCompleted(true)
61-
} else {
62-
queryClient.invalidateQueries(projectKeys.detail(ref))
63-
}
64-
}
65-
}
66-
6765
const onConfirm = async () => {
6866
if (!project) return console.error('Project is required')
6967

7068
setLoading(true)
7169
if (ref) await invalidateProjectDetailsQuery(queryClient, ref)
7270
}
7371

74-
useEffect(() => {
75-
checkServerInterval.current = window.setInterval(checkServer, 4000)
76-
return () => clearInterval(checkServerInterval.current)
77-
}, [])
78-
7972
return (
8073
<div className="flex items-center justify-center h-full">
8174
<div className="bg-surface-100 border border-overlay rounded-md w-3/4 lg:w-1/2">

apps/studio/data/fetchers.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,55 @@ export const handleError = (error: unknown): never => {
160160
// up in the UI.
161161
throw new ResponseError(undefined)
162162
}
163+
164+
// [Joshen] The methods below are brought over from lib/common/fetchers because we still need them
165+
// primarily for our own endpoints in the dashboard repo. So consolidating all the fetch methods into here.
166+
167+
async function handleFetchResponse<T>(response: Response): Promise<T | ResponseError> {
168+
const contentType = response.headers.get('Content-Type')
169+
if (contentType === 'application/octet-stream') return response as any
170+
try {
171+
const resTxt = await response.text()
172+
try {
173+
// try to parse response text as json
174+
return JSON.parse(resTxt)
175+
} catch (err) {
176+
// return as text plain
177+
return resTxt as any
178+
}
179+
} catch (e) {
180+
return handleError(response) as T | ResponseError
181+
}
182+
}
183+
184+
/**
185+
* To be used only for dashboard API endpoints. Use `fetch` directly if calling a non dashboard API endpoint
186+
*
187+
* Exception for `bucket-object-download-mutation` as openapi-fetch doesn't support octet-stream responses
188+
*/
189+
export async function fetchPost<T = any>(
190+
url: string,
191+
data: { [prop: string]: any },
192+
options?: { [prop: string]: any }
193+
): Promise<T | ResponseError> {
194+
try {
195+
const { headers: otherHeaders, abortSignal, ...otherOptions } = options ?? {}
196+
const headers = await constructHeaders({
197+
'Content-Type': 'application/json',
198+
...DEFAULT_HEADERS,
199+
...otherHeaders,
200+
})
201+
const response = await fetch(url, {
202+
method: 'POST',
203+
body: JSON.stringify(data),
204+
referrerPolicy: 'no-referrer-when-downgrade',
205+
headers,
206+
...otherOptions,
207+
signal: abortSignal,
208+
})
209+
if (!response.ok) return handleError(response)
210+
return handleFetchResponse(response)
211+
} catch (error) {
212+
return handleError(error)
213+
}
214+
}

apps/studio/data/profile/profile-create-mutation.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
11
import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query'
22
import { toast } from 'sonner'
33

4+
import { components } from 'api-types'
5+
import { handleError, post } from 'data/fetchers'
46
import { organizationKeys } from 'data/organizations/keys'
57
import { permissionKeys } from 'data/permissions/keys'
6-
import { post } from 'lib/common/fetch'
7-
import { API_URL } from 'lib/constants'
88
import type { ResponseError } from 'types'
99
import { profileKeys } from './keys'
10-
import type { Profile } from './types'
1110

12-
export type ProfileResponse = Profile
11+
export type ProfileResponse = components['schemas']['ProfileResponse']
1312

1413
export async function createProfile() {
15-
const response = await post(`${API_URL}/profile`, {})
16-
if (response.error) {
17-
throw response.error
18-
}
14+
const { data, error } = await post('/platform/profile')
1915

20-
return response as ProfileResponse
16+
if (error) handleError(error)
17+
return data
2118
}
2219

2320
type ProfileCreateData = Awaited<ReturnType<typeof createProfile>>

apps/studio/data/service-status/edge-functions-status-query.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
22

3-
import { get } from 'lib/common/fetch'
43
import type { ResponseError } from 'types'
54
import { serviceStatusKeys } from './keys'
65

@@ -9,10 +8,16 @@ export type EdgeFunctionServiceStatusVariables = {
98
}
109

1110
export async function getEdgeFunctionServiceStatus(signal?: AbortSignal) {
12-
const res = await get(`https://obuldanrptloktxcffvn.supabase.co/functions/v1/health-check`, {
13-
signal,
14-
})
15-
return res as { healthy: boolean }
11+
try {
12+
const res = await fetch('https://obuldanrptloktxcffvn.supabase.co/functions/v1/health-check', {
13+
method: 'GET',
14+
signal,
15+
})
16+
const response = await res.json()
17+
return response as { healthy: boolean }
18+
} catch (err) {
19+
return { healthy: false }
20+
}
1621
}
1722

1823
export type EdgeFunctionServiceStatusData = Awaited<ReturnType<typeof getEdgeFunctionServiceStatus>>

apps/studio/data/storage/bucket-object-download-mutation.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { UseMutationOptions, useMutation } from '@tanstack/react-query'
22
import { toast } from 'sonner'
33

44
import { components } from 'data/api'
5-
import { post } from 'lib/common/fetch'
5+
import { fetchPost } from 'data/fetchers'
66
import { API_URL, IS_PLATFORM } from 'lib/constants'
77
import { ResponseError } from 'types'
88

@@ -18,15 +18,9 @@ export const downloadBucketObject = async (
1818
) => {
1919
if (!bucketId) throw new Error('bucketId is required')
2020

21-
// [Joshen] JFYI we have to use lib/common/fetch post as post from openapi-fetch doesn't support receiving octet-streams
22-
// Opting to hard code /platform for non platform just for this particular mutation, so that it's clear what's happening
23-
const response = await post(
21+
const response = await fetchPost(
2422
`${API_URL}${IS_PLATFORM ? '' : '/platform'}/storage/${projectRef}/buckets/${bucketId}/objects/download`,
25-
{
26-
path,
27-
options,
28-
abortSignal: signal,
29-
}
23+
{ path, options, abortSignal: signal }
3024
)
3125

3226
if (response.error) throw response.error

apps/studio/lib/common/fetch/base.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export async function handleResponse<T>(
1515
): Promise<SupaResponse<T>> {
1616
const contentType = response.headers.get('Content-Type')
1717
if (contentType === 'application/octet-stream') return response as any
18-
1918
try {
2019
const resTxt = await response.text()
2120
try {

apps/studio/lib/common/fetch/get.ts

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,3 @@ export async function get<T = any>(
2525
return handleError(error, requestId)
2626
}
2727
}
28-
29-
export async function getWithTimeout<T = any>(
30-
url: string,
31-
options?: { [prop: string]: any }
32-
): Promise<SupaResponse<T>> {
33-
const requestId = uuidv4()
34-
try {
35-
const timeout = options?.timeout ?? 60000
36-
const controller = new AbortController()
37-
const id = setTimeout(() => controller.abort(), timeout)
38-
const { headers: optionHeaders, ...otherOptions } = options ?? {}
39-
const headers = await constructHeaders(requestId, optionHeaders)
40-
const response = await fetch(url, {
41-
method: 'GET',
42-
referrerPolicy: 'no-referrer-when-downgrade',
43-
headers,
44-
...otherOptions,
45-
signal: controller.signal,
46-
})
47-
clearTimeout(id)
48-
49-
if (!response.ok) return handleResponseError(response, requestId)
50-
return handleResponse(response, requestId)
51-
} catch (error) {
52-
return handleError(error, requestId)
53-
}
54-
}

apps/studio/lib/common/fetch/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export {
99
isResponseOk,
1010
} from './base'
1111
export { delete_ } from './delete'
12-
export { get, getWithTimeout } from './get'
12+
export { get } from './get'
1313
export { head, headWithTimeout } from './head'
1414
export { patch } from './patch'
1515
export { post } from './post'

0 commit comments

Comments
 (0)