From 6d93727f27597844f42e13bf623be2e769955d8a Mon Sep 17 00:00:00 2001 From: Greg Richardson Date: Mon, 17 Jul 2023 12:25:02 -0600 Subject: [PATCH] refactor: more isResponseOk updates --- .../interfaces/Auth/Users/InviteUserModal.tsx | 6 ++--- .../interfaces/Auth/Users/UserDropdown.tsx | 22 +++++++++---------- .../Database/Extensions/ExtensionCard.tsx | 5 +++-- .../Database/Functions/CreateFunction.tsx | 8 ++++--- .../Database/Functions/DeleteFunction.tsx | 5 +++-- .../Database/Triggers/DeleteTrigger.tsx | 5 +++-- .../BillingSettings/BillingEmail.tsx | 7 +++--- .../BillingSettingsV2/BillingEmail.tsx | 10 ++++++--- .../DeleteOrganizationButton.tsx | 6 ++--- .../GeneralSettings/GeneralSettings.tsx | 7 +++--- .../Organization/NewOrg/NewOrgForm.tsx | 7 +++--- .../TeamSettings/TeamSettings.tsx | 6 ++--- .../DeleteProjectButton.tsx | 8 ++++--- .../interfaces/Settings/General/General.tsx | 7 +++--- .../interfaces/SignIn/ForgotPasswordForm.tsx | 9 ++++---- .../interfaces/SignIn/SignUpForm.tsx | 9 ++++---- .../interfaces/Support/SupportForm.tsx | 6 ++--- studio/data/__templates/resource-query.ts | 15 ++++++++----- studio/data/__templates/resources-query.ts | 9 ++++---- .../access-tokens-create-mutation.ts | 9 ++++---- .../access-tokens-delete-mutation.ts | 7 +++--- .../data/access-tokens/access-tokens-query.ts | 9 ++++---- .../analytics/functions-inv-stats-query.ts | 4 ++-- .../data/analytics/project-log-stats-query.ts | 8 +++---- studio/data/projects/project-detail-query.ts | 17 +++++++++----- studio/stores/common/PostgresMetaInterface.ts | 10 ++++----- 26 files changed, 124 insertions(+), 97 deletions(-) diff --git a/studio/components/interfaces/Auth/Users/InviteUserModal.tsx b/studio/components/interfaces/Auth/Users/InviteUserModal.tsx index f64c1723ccb..87ede80c3f9 100644 --- a/studio/components/interfaces/Auth/Users/InviteUserModal.tsx +++ b/studio/components/interfaces/Auth/Users/InviteUserModal.tsx @@ -4,7 +4,7 @@ import { useContext } from 'react' import { Button, Form, IconMail, Input, Modal } from 'ui' import { useCheckPermissions, useStore } from 'hooks' -import { post } from 'lib/common/fetch' +import { isResponseOk, post } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { PageContext } from 'pages/project/[ref]/auth/users' @@ -37,10 +37,10 @@ const InviteUserModal = ({ visible, setVisible }: InviteUserModalProps) => { const onInviteUser = async (values: any, { setSubmitting }: any) => { setSubmitting(true) - const response = await post(`${API_URL}/auth/${PageState.projectRef}/invite`, { + const response = await post(`${API_URL}/auth/${PageState.projectRef}/invite`, { email: values.email, }) - if (response.error) { + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to invite user: ${response.error.message}`, diff --git a/studio/components/interfaces/Auth/Users/UserDropdown.tsx b/studio/components/interfaces/Auth/Users/UserDropdown.tsx index 67c06b4da33..57fc321ed95 100644 --- a/studio/components/interfaces/Auth/Users/UserDropdown.tsx +++ b/studio/components/interfaces/Auth/Users/UserDropdown.tsx @@ -5,7 +5,7 @@ import { Button, Dropdown, IconTrash, IconMail, IconMoreHorizontal, IconShieldOf import { useStore } from 'hooks' import { timeout } from 'lib/helpers' -import { post, delete_ } from 'lib/common/fetch' +import { post, delete_, isResponseOk } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { PageContext } from 'pages/project/[ref]/auth/users' import { confirmAlert } from 'components/to-be-cleaned/ModalsDeprecated/ConfirmModal' @@ -25,8 +25,8 @@ const UserDropdown: FC = ({ user, canRemoveUser, canRemoveMFAFactors }) = async function handleResetPassword() { try { setLoading(true) - const response = await post(`${API_URL}/auth/${PageState.projectRef}/recover`, user) - if (response.error) { + const response = await post(`${API_URL}/auth/${PageState.projectRef}/recover`, user) + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to send password recovery: ${response.error.message}`, @@ -50,8 +50,8 @@ const UserDropdown: FC = ({ user, canRemoveUser, canRemoveMFAFactors }) = async function handleSendMagicLink() { try { setLoading(true) - const response = await post(`${API_URL}/auth/${PageState.projectRef}/magiclink`, user) - if (response.error) { + const response = await post(`${API_URL}/auth/${PageState.projectRef}/magiclink`, user) + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to send magic link: ${response.error.message}`, @@ -75,8 +75,8 @@ const UserDropdown: FC = ({ user, canRemoveUser, canRemoveMFAFactors }) = async function handleSendOtp() { try { setLoading(true) - const response = await post(`${API_URL}/auth/${PageState.projectRef}/otp`, user) - if (response.error) { + const response = await post(`${API_URL}/auth/${PageState.projectRef}/otp`, user) + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to OTP: ${response.error.message}`, @@ -105,8 +105,8 @@ const UserDropdown: FC = ({ user, canRemoveUser, canRemoveMFAFactors }) = message: `This is permanent! Are you sure you want to delete user ${user.email} ?`, onAsyncConfirm: async () => { setLoading(true) - const response = await delete_(`${API_URL}/auth/${PageState.projectRef}/users`, user) - if (response.error) { + const response = await delete_(`${API_URL}/auth/${PageState.projectRef}/users`, user) + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to delete user: ${response.error.message}`, @@ -129,10 +129,10 @@ const UserDropdown: FC = ({ user, canRemoveUser, canRemoveMFAFactors }) = message: `This is permanent! Are you sure you want to delete the user's MFA factors?`, onAsyncConfirm: async () => { setLoading(true) - const response = await delete_( + const response = await delete_( `${API_URL}/auth/${PageState.projectRef}/users/${user.id}/factors` ) - if (response.error) { + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to delete factors: ${response.error.message}`, diff --git a/studio/components/interfaces/Database/Extensions/ExtensionCard.tsx b/studio/components/interfaces/Database/Extensions/ExtensionCard.tsx index 4115bd71ec5..0dacb0b2358 100644 --- a/studio/components/interfaces/Database/Extensions/ExtensionCard.tsx +++ b/studio/components/interfaces/Database/Extensions/ExtensionCard.tsx @@ -6,6 +6,7 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { useCheckPermissions, useStore } from 'hooks' import { confirmAlert } from 'components/to-be-cleaned/ModalsDeprecated/ConfirmModal' import EnableExtensionModal from './EnableExtensionModal' +import { isResponseOk } from 'lib/common/fetch' interface Props { extension: any @@ -34,8 +35,8 @@ const ExtensionCard: FC = ({ extension }) => { onAsyncConfirm: async () => { try { setLoading(true) - const response: any = await meta.extensions.del(extension.name) - if (response.error) { + const response = await meta.extensions.del(extension.name) + if (!isResponseOk(response)) { throw response.error } else { ui.setNotification({ diff --git a/studio/components/interfaces/Database/Functions/CreateFunction.tsx b/studio/components/interfaces/Database/Functions/CreateFunction.tsx index e10be245b08..d2d77ae52e1 100644 --- a/studio/components/interfaces/Database/Functions/CreateFunction.tsx +++ b/studio/components/interfaces/Database/Functions/CreateFunction.tsx @@ -10,6 +10,8 @@ import Panel from 'components/ui/Panel' import SqlEditor from 'components/ui/SqlEditor' import { POSTGRES_DATA_TYPES } from 'components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.constants' import ConfirmationModal from 'components/ui/ConfirmationModal' +import { isResponseOk } from 'lib/common/fetch' +import { SupaResponse } from 'types' // [Refactor] Remove local state, just use the Form component @@ -318,15 +320,15 @@ const CreateFunction: FC = ({ func, visible, setVisible }) _localState.setLoading(true) const body = _localState.formState.requestBody - const response: any = body.id + const response: SupaResponse = body.id ? await (_localState!.meta as any).functions.update(body.id, body) : await (_localState!.meta as any).functions.create(body) - if (response.error) { + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to create function: ${ - response.error?.message ?? 'Submit request failed' + response.error.message ?? 'Submit request failed' }`, }) _localState.setLoading(false) diff --git a/studio/components/interfaces/Database/Functions/DeleteFunction.tsx b/studio/components/interfaces/Database/Functions/DeleteFunction.tsx index 93f0bda6709..e24850821c2 100644 --- a/studio/components/interfaces/Database/Functions/DeleteFunction.tsx +++ b/studio/components/interfaces/Database/Functions/DeleteFunction.tsx @@ -2,6 +2,7 @@ import { FC, useState } from 'react' import { observer } from 'mobx-react-lite' import { useStore } from 'hooks' import TextConfirmModal from 'components/ui/Modals/TextConfirmModal' +import { isResponseOk } from 'lib/common/fetch' type DeleteFunctionProps = { func?: any @@ -20,8 +21,8 @@ const DeleteFunction: FC = ({ func, visible, setVisible }) if (!id) { throw Error('Invalid function info') } - const response: any = await meta.functions.del(id) - if (response.error) { + const response = await meta.functions.del(id) + if (!isResponseOk(response)) { throw response.error } else { ui.setNotification({ diff --git a/studio/components/interfaces/Database/Triggers/DeleteTrigger.tsx b/studio/components/interfaces/Database/Triggers/DeleteTrigger.tsx index 62ebf02da44..b58b6ce089b 100644 --- a/studio/components/interfaces/Database/Triggers/DeleteTrigger.tsx +++ b/studio/components/interfaces/Database/Triggers/DeleteTrigger.tsx @@ -2,6 +2,7 @@ import { FC, useState } from 'react' import { observer } from 'mobx-react-lite' import { useStore } from 'hooks' import TextConfirmModal from 'components/ui/Modals/TextConfirmModal' +import { isResponseOk } from 'lib/common/fetch' type DeleteTriggerProps = { trigger?: any @@ -20,8 +21,8 @@ const DeleteTrigger: FC = ({ store, trigger, visible, setVis if (!id) { throw Error('Invalid trigger info') } - const response: any = await meta.triggers.del(id) - if (response.error) { + const response = await meta.triggers.del(id) + if (!isResponseOk(response)) { throw response.error } else { ui.setNotification({ category: 'success', message: `Successfully removed ${name}` }) diff --git a/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx b/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx index 98438a7f4df..7e1af686b6a 100644 --- a/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx +++ b/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx @@ -6,9 +6,10 @@ import { useParams } from 'common/hooks' import { FormActions, FormPanel, FormSection, FormSectionContent } from 'components/ui/Forms' import { invalidateOrganizationsQuery } from 'data/organizations/organizations-query' import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks' -import { patch } from 'lib/common/fetch' +import { isResponseOk, patch } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { Form, Input } from 'ui' +import { Organization } from 'types' const BillingEmail = () => { const queryClient = useQueryClient() @@ -34,11 +35,11 @@ const BillingEmail = () => { } setSubmitting(true) - const response = await patch(`${API_URL}/organizations/${slug}`, { + const response = await patch(`${API_URL}/organizations/${slug}`, { ...values, name, }) - if (response.error) { + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to update organization: ${response.error.message}`, diff --git a/studio/components/interfaces/Organization/BillingSettingsV2/BillingEmail.tsx b/studio/components/interfaces/Organization/BillingSettingsV2/BillingEmail.tsx index dbac307c3af..af15fded846 100644 --- a/studio/components/interfaces/Organization/BillingSettingsV2/BillingEmail.tsx +++ b/studio/components/interfaces/Organization/BillingSettingsV2/BillingEmail.tsx @@ -11,9 +11,10 @@ import { import { FormActions, FormPanel, FormSection, FormSectionContent } from 'components/ui/Forms' import { invalidateOrganizationsQuery } from 'data/organizations/organizations-query' import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks' -import { patch } from 'lib/common/fetch' +import { isResponseOk, patch } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { Form, Input } from 'ui' +import { Organization } from 'types' const BillingEmail = () => { const queryClient = useQueryClient() @@ -37,8 +38,11 @@ const BillingEmail = () => { } setSubmitting(true) - const response = await patch(`${API_URL}/organizations/${slug}`, { ...values, name }) - if (response.error) { + const response = await patch(`${API_URL}/organizations/${slug}`, { + ...values, + name, + }) + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to update organization: ${response.error.message}`, diff --git a/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx b/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx index 55f3c6ed20f..cc5c66fec0b 100644 --- a/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx +++ b/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx @@ -6,7 +6,7 @@ import { Button, Form, Input, Modal } from 'ui' import { invalidateOrganizationsQuery } from 'data/organizations/organizations-query' import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks' -import { delete_ } from 'lib/common/fetch' +import { delete_, isResponseOk } from 'lib/common/fetch' import { API_URL } from 'lib/constants' const DeleteOrganizationButton = () => { @@ -42,8 +42,8 @@ const DeleteOrganizationButton = () => { } setSubmitting(true) - const response = await delete_(`${API_URL}/organizations/${orgSlug}`) - if (response.error) { + const response = await delete_(`${API_URL}/organizations/${orgSlug}`) + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to delete organization: ${response.error.message}`, diff --git a/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx b/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx index 435adc38493..73b69322234 100644 --- a/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx +++ b/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx @@ -16,10 +16,11 @@ import { } from 'components/ui/Forms' import { invalidateOrganizationsQuery } from 'data/organizations/organizations-query' import { useCheckPermissions, useFlag, useSelectedOrganization, useStore } from 'hooks' -import { patch } from 'lib/common/fetch' +import { isResponseOk, patch } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import OrganizationDeletePanel from './OrganizationDeletePanel' import { ScaffoldContainerLegacy } from 'components/layouts/Scaffold' +import { Organization } from 'types' const GeneralSettings = () => { const queryClient = useQueryClient() @@ -50,13 +51,13 @@ const GeneralSettings = () => { // [Joshen] Need to update this logic once we support multiple opt in tags const optInTags = values.isOptedIntoAi ? ['AI_SQL_GENERATOR_OPT_IN'] : [] - const response = await patch(`${API_URL}/organizations/${slug}`, { + const response = await patch(`${API_URL}/organizations/${slug}`, { name: values.name, billing_email: selectedOrganization?.billing_email ?? '', ...(allowCMDKDataOptIn && { opt_in_tags: optInTags }), }) - if (response.error) { + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to update organization: ${response.error.message}`, diff --git a/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx b/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx index 1593289f144..494f9f37e54 100644 --- a/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx +++ b/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx @@ -9,10 +9,11 @@ import InformationBox from 'components/ui/InformationBox' import Panel from 'components/ui/Panel' import { invalidateOrganizationsQuery } from 'data/organizations/organizations-query' import { useStore } from 'hooks' -import { post } from 'lib/common/fetch' +import { isResponseOk, post } from 'lib/common/fetch' import { API_URL, BASE_PATH, PRICING_TIER_LABELS_ORG } from 'lib/constants' import { getURL } from 'lib/helpers' import Link from 'next/link' +import { Organization } from 'types' const ORG_KIND_TYPES = { PERSONAL: 'Personal', @@ -77,7 +78,7 @@ const NewOrgForm = ({ onPaymentMethodReset }: NewOrgFormProps) => { } async function createOrg(paymentMethodId?: string) { - const response = await post( + const response = await post( `${API_URL}/organizations`, { name: orgName, @@ -94,7 +95,7 @@ const NewOrgForm = ({ onPaymentMethodReset }: NewOrgFormProps) => { } ) - if (response.error) { + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to create organization: ${response.error?.message ?? response.error}`, diff --git a/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx b/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx index d704a29dc24..7948d6e0e6e 100644 --- a/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx +++ b/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx @@ -8,7 +8,7 @@ import { useOrganizationDetailQuery } from 'data/organizations/organization-deta import { useOrganizationRolesQuery } from 'data/organizations/organization-roles-query' import { usePermissionsQuery } from 'data/permissions/permissions-query' import { useSelectedOrganization, useStore } from 'hooks' -import { delete_ } from 'lib/common/fetch' +import { delete_, isResponseOk } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { useProfile } from 'lib/profile' import InviteMemberButton from './InviteMemberButton' @@ -55,10 +55,10 @@ const TeamSettings = () => { title: 'Are you sure?', message: 'Are you sure you want to leave this team? This is permanent.', onAsyncConfirm: async () => { - const response = await delete_( + const response = await delete_( `${API_URL}/organizations/${slug}/members/${profile!.gotrue_id}` ) - if (response.error) { + if (!isResponseOk(response)) { throw response.error } else { window?.location.replace('/') // Force reload to clear Store diff --git a/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectButton.tsx b/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectButton.tsx index 915c12b0dc5..c74e4566dda 100644 --- a/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectButton.tsx +++ b/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectButton.tsx @@ -10,7 +10,7 @@ import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectConte import TextConfirmModal from 'components/ui/Modals/TextConfirmModal' import { invalidateProjectsQuery } from 'data/projects/projects-query' import { useCheckPermissions, useStore } from 'hooks' -import { delete_, post } from 'lib/common/fetch' +import { delete_, isResponseOk, post } from 'lib/common/fetch' import { API_URL, PRICING_TIER_PRODUCT_IDS } from 'lib/constants' export interface DeleteProjectButtonProps { @@ -68,8 +68,10 @@ const DeleteProjectButton = ({ type = 'danger' }: DeleteProjectButtonProps) => { setLoading(true) try { - const response = await delete_(`${API_URL}/projects/${projectRef}`) - if (response.error) throw response.error + const response = await delete_(`${API_URL}/projects/${projectRef}`) + if (!isResponseOk(response)) { + throw response.error + } await invalidateProjectsQuery(queryClient) ui.setNotification({ category: 'success', message: `Successfully deleted ${project.name}` }) router.push(`/projects`) diff --git a/studio/components/interfaces/Settings/General/General.tsx b/studio/components/interfaces/Settings/General/General.tsx index caf145a67fd..df12d34b855 100644 --- a/studio/components/interfaces/Settings/General/General.tsx +++ b/studio/components/interfaces/Settings/General/General.tsx @@ -16,10 +16,11 @@ import Panel from 'components/ui/Panel' import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { invalidateProjectsQuery } from 'data/projects/projects-query' import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks' -import { patch } from 'lib/common/fetch' +import { isResponseOk, patch } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import PauseProjectButton from './Infrastructure/PauseProjectButton' import RestartServerButton from './Infrastructure/RestartServerButton' +import { Project } from 'types' const General = () => { const { ui } = useStore() @@ -33,10 +34,10 @@ const General = () => { const canUpdateProject = useCheckPermissions(PermissionAction.UPDATE, 'projects') const onSubmit = async (values: any, { resetForm }: any) => { - const response = await patch(`${API_URL}/projects/${project?.ref}`, { + const response = await patch(`${API_URL}/projects/${project?.ref}`, { name: values.name.trim(), }) - if (response.error) { + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Update project failed: ${response.error.message}`, diff --git a/studio/components/interfaces/SignIn/ForgotPasswordForm.tsx b/studio/components/interfaces/SignIn/ForgotPasswordForm.tsx index 309b739a03e..de6fdad0f0e 100644 --- a/studio/components/interfaces/SignIn/ForgotPasswordForm.tsx +++ b/studio/components/interfaces/SignIn/ForgotPasswordForm.tsx @@ -1,6 +1,6 @@ import HCaptcha from '@hcaptcha/react-hcaptcha' import { useStore } from 'hooks' -import { post } from 'lib/common/fetch' +import { isResponseOk, post } from 'lib/common/fetch' import { API_URL, BASE_PATH } from 'lib/constants' import { useRouter } from 'next/router' import { useRef, useState } from 'react' @@ -30,7 +30,7 @@ const ForgotPasswordForm = () => { token = captchaResponse?.response ?? null } - const response = await post(`${API_URL}/reset-password`, { + const response = await post(`${API_URL}/reset-password`, { email, hcaptchaToken: token ?? undefined, redirectTo: `${ @@ -39,9 +39,8 @@ const ForgotPasswordForm = () => { : process.env.NEXT_PUBLIC_SITE_URL }${BASE_PATH}/reset-password`, }) - const error = response.error - if (!error) { + if (isResponseOk(response)) { ui.setNotification({ id: toastId, category: 'success', @@ -56,7 +55,7 @@ const ForgotPasswordForm = () => { ui.setNotification({ id: toastId, category: 'error', - message: error.message, + message: response.error.message, }) } } diff --git a/studio/components/interfaces/SignIn/SignUpForm.tsx b/studio/components/interfaces/SignIn/SignUpForm.tsx index b206e80e8a6..cb4ca363034 100644 --- a/studio/components/interfaces/SignIn/SignUpForm.tsx +++ b/studio/components/interfaces/SignIn/SignUpForm.tsx @@ -4,7 +4,7 @@ import HCaptcha from '@hcaptcha/react-hcaptcha' import { Alert, Button, Form, IconEye, IconEyeOff, Input } from 'ui' import { useStore } from 'hooks' -import { post } from 'lib/common/fetch' +import { isResponseOk, post } from 'lib/common/fetch' import { API_URL, BASE_PATH } from 'lib/constants' import { passwordSchema } from 'lib/schemas' import { resetSignInClicks } from 'lib/local-storage' @@ -36,7 +36,7 @@ const SignUpForm = () => { resetSignInClicks() - const response = await post(`${API_URL}/signup`, { + const response = await post(`${API_URL}/signup`, { email, password, hcaptchaToken: token ?? null, @@ -46,9 +46,8 @@ const SignUpForm = () => { : process.env.NEXT_PUBLIC_SITE_URL }${BASE_PATH}/sign-in`, }) - const error = response.error - if (!error) { + if (isResponseOk(response)) { ui.setNotification({ id: toastId, category: 'success', @@ -63,7 +62,7 @@ const SignUpForm = () => { ui.setNotification({ id: toastId, category: 'error', - message: error.message, + message: response.error.message, }) } } diff --git a/studio/components/interfaces/Support/SupportForm.tsx b/studio/components/interfaces/Support/SupportForm.tsx index 3bec0ff1722..1864c3bd1eb 100644 --- a/studio/components/interfaces/Support/SupportForm.tsx +++ b/studio/components/interfaces/Support/SupportForm.tsx @@ -26,7 +26,7 @@ import { useProjectsQuery } from 'data/projects/projects-query' import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query' import { useFlag, useStore } from 'hooks' import useLatest from 'hooks/misc/useLatest' -import { get, post } from 'lib/common/fetch' +import { get, isResponseOk, post } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { detectBrowser } from 'lib/helpers' import { useProfile } from 'lib/profile' @@ -181,8 +181,8 @@ const SupportForm = ({ setSentCategory }: SupportFormProps) => { } } - const response = await post(`${API_URL}/feedback/send`, payload) - if (response.error) { + const response = await post(`${API_URL}/feedback/send`, payload) + if (!isResponseOk(response)) { ui.setNotification({ category: 'error', message: `Failed to submit support ticket: ${response.error.message}`, diff --git a/studio/data/__templates/resource-query.ts b/studio/data/__templates/resource-query.ts index 6d278c48ceb..022d625a744 100644 --- a/studio/data/__templates/resource-query.ts +++ b/studio/data/__templates/resource-query.ts @@ -1,5 +1,5 @@ import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' -import { get } from 'lib/common/fetch' +import { get, isResponseOk } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { useCallback } from 'react' import { resourceKeys } from './keys' @@ -21,14 +21,17 @@ export async function getResource({ projectRef, id }: ResourceVariables, signal? throw new Error('id is required') } - const response = await get(`${API_URL}/projects/${projectRef}/resources/${id}`, { - signal, - }) - if (response.error) { + const response = await get( + `${API_URL}/projects/${projectRef}/resources/${id}`, + { + signal, + } + ) + if (!isResponseOk(response)) { throw response.error } - return response as ResourceResponse + return response } export type ResourceData = Awaited> diff --git a/studio/data/__templates/resources-query.ts b/studio/data/__templates/resources-query.ts index b9b1ae01a68..251110db806 100644 --- a/studio/data/__templates/resources-query.ts +++ b/studio/data/__templates/resources-query.ts @@ -1,5 +1,5 @@ import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' -import { get } from 'lib/common/fetch' +import { get, isResponseOk } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { useCallback } from 'react' import { resourceKeys } from './keys' @@ -17,14 +17,15 @@ export async function getResources({ projectRef }: ResourcesVariables, signal?: throw new Error('projectRef is required') } - const response = await get(`${API_URL}/projects/${projectRef}/resources`, { + const response = await get(`${API_URL}/projects/${projectRef}/resources`, { signal, }) - if (response.error) { + + if (!isResponseOk(response)) { throw response.error } - return response as ResourcesResponse[] + return response } export type ResourcesData = Awaited> diff --git a/studio/data/access-tokens/access-tokens-create-mutation.ts b/studio/data/access-tokens/access-tokens-create-mutation.ts index 725ee55a36f..309dc2c43d6 100644 --- a/studio/data/access-tokens/access-tokens-create-mutation.ts +++ b/studio/data/access-tokens/access-tokens-create-mutation.ts @@ -1,5 +1,5 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' -import { post } from 'lib/common/fetch' +import { isResponseOk, post } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { AccessToken } from './access-tokens-query' import { accessTokenKeys } from './keys' @@ -12,12 +12,13 @@ export type AccessTokenCreateVariables = { export type NewAccessToken = AccessToken & { token: string } export async function createAccessToken({ name, scope }: AccessTokenCreateVariables) { - const response = await post(`${API_URL}/profile/access-tokens`, { name, scope }) - if (response.error) { + const response = await post(`${API_URL}/profile/access-tokens`, { name, scope }) + + if (!isResponseOk(response)) { throw response.error } - return response as NewAccessToken + return response } type AccessTokenCreateData = Awaited> diff --git a/studio/data/access-tokens/access-tokens-delete-mutation.ts b/studio/data/access-tokens/access-tokens-delete-mutation.ts index 0f9bc82f575..a1c014c49f9 100644 --- a/studio/data/access-tokens/access-tokens-delete-mutation.ts +++ b/studio/data/access-tokens/access-tokens-delete-mutation.ts @@ -1,5 +1,5 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' -import { delete_ } from 'lib/common/fetch' +import { delete_, isResponseOk } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { accessTokenKeys } from './keys' @@ -8,8 +8,9 @@ export type AccessTokenDeleteVariables = { } export async function deleteAccessToken({ id }: AccessTokenDeleteVariables) { - const response = await delete_(`${API_URL}/profile/access-tokens/${id}`) - if (response.error) { + const response = await delete_(`${API_URL}/profile/access-tokens/${id}`) + + if (!isResponseOk(response)) { throw response.error } diff --git a/studio/data/access-tokens/access-tokens-query.ts b/studio/data/access-tokens/access-tokens-query.ts index 6cfb187510e..858ba912795 100644 --- a/studio/data/access-tokens/access-tokens-query.ts +++ b/studio/data/access-tokens/access-tokens-query.ts @@ -1,5 +1,5 @@ import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' -import { get } from 'lib/common/fetch' +import { get, isResponseOk } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { useCallback } from 'react' import { accessTokenKeys } from './keys' @@ -14,14 +14,15 @@ export type AccessToken = { export type AccessTokensResponse = AccessToken[] export async function getAccessTokens(signal?: AbortSignal) { - const response = await get(`${API_URL}/profile/access-tokens`, { + const response = await get(`${API_URL}/profile/access-tokens`, { signal, }) - if (response.error) { + + if (!isResponseOk(response)) { throw response.error } - return response as AccessTokensResponse + return response } export type AccessTokensData = Awaited> diff --git a/studio/data/analytics/functions-inv-stats-query.ts b/studio/data/analytics/functions-inv-stats-query.ts index 8bf0b5ad76e..5433a29ea1e 100644 --- a/studio/data/analytics/functions-inv-stats-query.ts +++ b/studio/data/analytics/functions-inv-stats-query.ts @@ -26,7 +26,7 @@ export async function getFunctionsInvStats( throw new Error('interval is required') } - const response = await get( + const response = await get( `${API_URL}/projects/${projectRef}/analytics/endpoints/functions.inv-stats?interval=${interval}&function_id=${functionId}`, { signal, @@ -36,7 +36,7 @@ export async function getFunctionsInvStats( throw response.error } - return response as FunctionsInvStatsResponse + return response } export type FunctionsInvStatsData = Awaited> diff --git a/studio/data/analytics/project-log-stats-query.ts b/studio/data/analytics/project-log-stats-query.ts index 023b1c0cfdb..015ff60cb86 100644 --- a/studio/data/analytics/project-log-stats-query.ts +++ b/studio/data/analytics/project-log-stats-query.ts @@ -1,5 +1,5 @@ import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' -import { get } from 'lib/common/fetch' +import { get, isResponseOk } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { useCallback } from 'react' import { analyticsKeys } from './keys' @@ -31,17 +31,17 @@ export async function getProjectLogStats( throw new Error('interval is required') } - const response = await get( + const response = await get( `${API_URL}/projects/${projectRef}/analytics/endpoints/usage.api-counts?interval=${interval}`, { signal, } ) - if (response.error) { + if (!isResponseOk(response)) { throw response.error } - return response as ProjectLogStatsResponse + return response } export type ProjectLogStatsData = Awaited> diff --git a/studio/data/projects/project-detail-query.ts b/studio/data/projects/project-detail-query.ts index 4e7acae201d..79dd3d9ffba 100644 --- a/studio/data/projects/project-detail-query.ts +++ b/studio/data/projects/project-detail-query.ts @@ -1,5 +1,5 @@ import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' -import { get } from 'lib/common/fetch' +import { get, isResponseOk } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { useCallback } from 'react' import { Project } from 'types' @@ -8,10 +8,17 @@ import { projectKeys } from './keys' export type ProjectDetailVariables = { ref?: string } export async function getProjectDetail({ ref }: ProjectDetailVariables, signal?: AbortSignal) { - if (!ref) throw new Error('Project ref is required') - const data = await get(`${API_URL}/projects/${ref}`, { signal }) - if (data.error) throw data.error - return data as Project + if (!ref) { + throw new Error('Project ref is required') + } + + const data = await get(`${API_URL}/projects/${ref}`, { signal }) + + if (!isResponseOk(data)) { + throw data.error + } + + return data } export type ProjectDetailData = Awaited> diff --git a/studio/stores/common/PostgresMetaInterface.ts b/studio/stores/common/PostgresMetaInterface.ts index 903d840821f..c932165fc2f 100644 --- a/studio/stores/common/PostgresMetaInterface.ts +++ b/studio/stores/common/PostgresMetaInterface.ts @@ -2,7 +2,7 @@ import { action, computed, makeObservable, observable } from 'mobx' import { get, patch, post, delete_, isResponseOk } from 'lib/common/fetch' import { keyBy } from 'lodash' import { IRootStore } from '../RootStore' -import { ResponseError } from 'types' +import { ResponseError, SupaResponse } from 'types' type DataKeys = number | string @@ -15,10 +15,10 @@ export interface IPostgresMetaInterface { isInitialized: boolean load: () => void - loadBySchema: (schema: string) => Promise - create: (payload: any) => Promise - update: (id: number | string, updates: any) => Promise - del: (id: number | string, cascade?: boolean) => Promise + loadBySchema: (schema: string) => Promise> + create: (payload: any) => Promise> + update: (id: number | string, updates: any) => Promise> + del: (id: number | string, cascade?: boolean) => Promise> list: (filter?: any) => T[] find: (filter?: any) => T | undefined byId: (id: number | string) => T | undefined