From 1fb755bfc715446de9600ce84084f1b3290b2829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Gr=C3=BCneberg?= Date: Mon, 8 Jan 2024 07:39:32 +0100 Subject: [PATCH] chore: use data/fetchers and API spec (#20196) * chore: use data/fetchers and API spec * Remove unused imports --------- Co-authored-by: Thomas Eckl <31189692+ecktoteckto@users.noreply.github.com> Co-authored-by: Joshen Lim --- .../Subscription/Subscription.utils.ts | 3 +- .../Database/Backups/PITR/PITR.utils.ts | 4 +- .../BillingBreakdown/BillingMetric.tsx | 2 +- .../Subscription/DowngradeModal.tsx | 2 +- .../Subscription/ExitSurveyModal.tsx | 2 +- .../Subscription/PlanUpdateSidePanel.tsx | 5 +- .../InvoicesSettings/InvoicesSettings.tsx | 4 +- .../Organization/Usage/Activity.tsx | 2 +- .../Organization/Usage/Bandwidth.tsx | 2 +- .../interfaces/Organization/Usage/Compute.tsx | 2 +- .../Organization/Usage/SizeAndCounts.tsx | 2 +- .../Organization/Usage/TotalUsage.tsx | 2 +- .../Organization/Usage/Usage.constants.tsx | 2 +- .../Organization/Usage/Usage.utils.ts | 2 +- .../Usage/UsageSection/AttributeUsage.tsx | 2 +- .../Usage/UsageSection/UsageSection.tsx | 2 +- .../interfaces/Settings/Addons/Addons.tsx | 35 ++-- .../Addons/ComputeInstanceSidePanel.tsx | 7 +- .../Settings/Addons/CustomDomainSidePanel.tsx | 3 +- .../Settings/Addons/PITRSidePanel.tsx | 3 +- .../interfaces/Settings/Logs/Logs.utils.ts | 2 +- apps/studio/data/api.d.ts | 170 +++++++++++++++--- .../data/invoices/invoices-overdue-query.ts | 17 +- .../data/stripe/setup-intent-mutation.ts | 1 - apps/studio/data/subscriptions/keys.ts | 2 - .../data/subscriptions/org-plans-query.ts | 21 +-- .../subscriptions/org-subscription-query.ts | 73 +------- .../org-subscription-update-mutation.ts | 20 ++- .../project-addon-remove-mutation.ts | 26 +-- .../project-addon-update-mutation.ts | 32 ++-- .../subscriptions/project-addons-query.ts | 51 +----- apps/studio/data/subscriptions/types.ts | 28 +++ 32 files changed, 295 insertions(+), 236 deletions(-) diff --git a/apps/studio/components/interfaces/Billing/Subscription/Subscription.utils.ts b/apps/studio/components/interfaces/Billing/Subscription/Subscription.utils.ts index 0011e6dc0d1..7a71e6e25f3 100644 --- a/apps/studio/components/interfaces/Billing/Subscription/Subscription.utils.ts +++ b/apps/studio/components/interfaces/Billing/Subscription/Subscription.utils.ts @@ -1,6 +1,5 @@ import { IS_PLATFORM } from 'lib/constants' -import { OrgSubscription } from 'data/subscriptions/org-subscription-query' -import { ProjectSelectedAddon } from 'data/subscriptions/project-addons-query' +import { OrgSubscription, ProjectSelectedAddon } from 'data/subscriptions/types' export const getAddons = (selectedAddons: ProjectSelectedAddon[]) => { const computeInstance = selectedAddons.find((addon) => addon.type === 'compute_instance') diff --git a/apps/studio/components/interfaces/Database/Backups/PITR/PITR.utils.ts b/apps/studio/components/interfaces/Database/Backups/PITR/PITR.utils.ts index 878c553fd9e..a380e8c86d4 100644 --- a/apps/studio/components/interfaces/Database/Backups/PITR/PITR.utils.ts +++ b/apps/studio/components/interfaces/Database/Backups/PITR/PITR.utils.ts @@ -1,7 +1,7 @@ import dayjs from 'dayjs' import { ALL_TIMEZONES } from './PITR.constants' -import { Time, Timezone } from './PITR.types' -import { ProjectSelectedAddon } from 'data/subscriptions/project-addons-query' +import { Time } from './PITR.types' +import { ProjectSelectedAddon } from 'data/subscriptions/types' export const getPITRRetentionDuration = (addons: ProjectSelectedAddon[]) => { const pitrAddon = addons.find((addon) => addon.type === 'pitr') diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingMetric.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingMetric.tsx index 591da335219..01706ebedb0 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingMetric.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/BillingMetric.tsx @@ -1,7 +1,7 @@ import * as Tooltip from '@radix-ui/react-tooltip' import Link from 'next/link' -import { OrgSubscription } from 'data/subscriptions/org-subscription-query' +import { OrgSubscription } from 'data/subscriptions/types' import { OrgUsageResponse } from 'data/usage/org-usage-query' import { Button, IconChevronRight, IconPieChart } from 'ui' import { Metric, USAGE_APPROACHING_THRESHOLD } from './BillingBreakdown.constants' diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/DowngradeModal.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/DowngradeModal.tsx index 707ab5c91c7..1b8e7a9f729 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/DowngradeModal.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/DowngradeModal.tsx @@ -1,4 +1,4 @@ -import { OrgSubscription, ProjectAddon } from 'data/subscriptions/org-subscription-query' +import { OrgSubscription, ProjectAddon } from 'data/subscriptions/types' import { PricingInformation } from 'shared-data' import { Alert, IconAlertOctagon, IconMinusCircle, IconPauseCircle, Modal } from 'ui' diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/ExitSurveyModal.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/ExitSurveyModal.tsx index f44ccfc31ac..29019f47d5c 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/ExitSurveyModal.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/ExitSurveyModal.tsx @@ -4,7 +4,7 @@ import { useReducer, useRef, useState } from 'react' import { useParams } from 'common' import { useSendDowngradeFeedbackMutation } from 'data/feedback/exit-survey-send' -import { OrgSubscription } from 'data/subscriptions/org-subscription-query' +import { OrgSubscription } from 'data/subscriptions/types' import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation' import { useFlag, useStore } from 'hooks' import { Alert, Button, Input, Modal } from 'ui' diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx index c3844d47c46..cfc042e5a11 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx @@ -10,7 +10,7 @@ import InformationBox from 'components/ui/InformationBox' import ShimmeringLoader from 'components/ui/ShimmeringLoader' import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-limit-check-query' import { useOrganizationBillingSubscriptionPreview } from 'data/organizations/organization-billing-subscription-preview' -import { useOrgPlansQuery } from 'data/subscriptions/org-plans-query' +import { OrgPlan } from 'data/subscriptions/types' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation' import { SubscriptionTier } from 'data/subscriptions/types' @@ -34,6 +34,7 @@ import EnterpriseCard from './EnterpriseCard' import ExitSurveyModal from './ExitSurveyModal' import MembersExceedLimitModal from './MembersExceedLimitModal' import PaymentMethodSelection from './PaymentMethodSelection' +import { useOrgPlansQuery } from 'data/subscriptions/org-plans-query' const PlanUpdateSidePanel = () => { const { ui } = useStore() @@ -96,7 +97,7 @@ const PlanUpdateSidePanel = () => { isSuccess: subscriptionPreviewInitialized, } = useOrganizationBillingSubscriptionPreview({ tier: selectedTier, organizationSlug: slug }) - const availablePlans = plans ?? [] + const availablePlans: OrgPlan[] = plans?.plans ?? [] const hasMembersExceedingFreeTierLimit = (membersExceededLimit || []).length > 0 const subscriptionPlanMeta = subscriptionsPlans.find((tier) => tier.id === selectedTier) diff --git a/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx b/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx index 5ae834caf3b..bad4868bcd6 100644 --- a/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx +++ b/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx @@ -104,12 +104,12 @@ const InvoicesSettings = () => {

{x.number}

- +
{[InvoiceStatus.UNCOLLECTIBLE, InvoiceStatus.OPEN].includes( - x.status as any + x.status as InvoiceStatus ) && (
-

{computeInstance?.variant?.meta?.memory_gb ?? 1} GB

+

{meta?.memory_gb ?? 1} GB

@@ -276,21 +277,17 @@ const Addons = () => {

- {computeInstance?.variant?.meta?.cpu_cores ?? 2}-core {cpuArchitecture}{' '} - {computeInstance?.variant?.meta?.cpu_dedicated ? '(Dedicated)' : '(Shared)'} + {meta?.cpu_cores ?? 2}-core {cpuArchitecture}{' '} + {meta?.cpu_dedicated ? '(Dedicated)' : '(Shared)'}

No. of direct connections

-

- {computeInstance?.variant?.meta?.connections_direct ?? 60} -

+

{meta?.connections_direct ?? 60}

No. of pooler connections

-

- {computeInstance?.variant?.meta?.connections_pooler ?? 200} -

+

{meta?.connections_pooler ?? 200}

@@ -306,9 +303,7 @@ const Addons = () => {

- {computeInstance?.variant?.meta?.max_disk_io_mbs?.toLocaleString() ?? - '2,085'}{' '} - Mbps + {meta?.max_disk_io_mbs?.toLocaleString() ?? '2,085'} Mbps

@@ -325,9 +320,7 @@ const Addons = () => {

- {computeInstance?.variant?.meta?.baseline_disk_io_mbs?.toLocaleString() ?? - 87}{' '} - Mbps + {meta?.baseline_disk_io_mbs?.toLocaleString() ?? 87} Mbps

diff --git a/apps/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx b/apps/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx index c324fa9a6cf..d23c3aaa614 100644 --- a/apps/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx +++ b/apps/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx @@ -32,6 +32,7 @@ import { import * as Tooltip from '@radix-ui/react-tooltip' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' +import { AddonVariantId } from 'data/subscriptions/types' const COMPUTE_CATEGORY_OPTIONS: { id: 'micro' | 'optimized' @@ -170,7 +171,11 @@ const ComputeInstanceSidePanel = () => { if (selectedOption === 'ci_micro' && subscriptionCompute !== undefined) { removeAddon({ projectRef, variant: subscriptionCompute.variant.identifier }) } else { - updateAddon({ projectRef, type: 'compute_instance', variant: selectedOption }) + updateAddon({ + projectRef, + type: 'compute_instance', + variant: selectedOption as AddonVariantId, + }) } } diff --git a/apps/studio/components/interfaces/Settings/Addons/CustomDomainSidePanel.tsx b/apps/studio/components/interfaces/Settings/Addons/CustomDomainSidePanel.tsx index 06a50441fb9..b1567c8d2e5 100644 --- a/apps/studio/components/interfaces/Settings/Addons/CustomDomainSidePanel.tsx +++ b/apps/studio/components/interfaces/Settings/Addons/CustomDomainSidePanel.tsx @@ -22,6 +22,7 @@ import { SidePanel, } from 'ui' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' +import { AddonVariantId } from 'data/subscriptions/types' const CustomDomainSidePanel = () => { const { ui } = useStore() @@ -121,7 +122,7 @@ const CustomDomainSidePanel = () => { if (selectedOption === 'cd_none' && subscriptionCDOption !== undefined) { removeAddon({ projectRef, variant: subscriptionCDOption.variant.identifier }) } else { - updateAddon({ projectRef, type: 'custom_domain', variant: selectedOption }) + updateAddon({ projectRef, type: 'custom_domain', variant: selectedOption as AddonVariantId }) } } diff --git a/apps/studio/components/interfaces/Settings/Addons/PITRSidePanel.tsx b/apps/studio/components/interfaces/Settings/Addons/PITRSidePanel.tsx index 5101f044af8..75a2c7d898c 100644 --- a/apps/studio/components/interfaces/Settings/Addons/PITRSidePanel.tsx +++ b/apps/studio/components/interfaces/Settings/Addons/PITRSidePanel.tsx @@ -26,6 +26,7 @@ import { } from 'ui' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' import { AlertTriangleIcon } from 'lucide-react' +import { AddonVariantId } from 'data/subscriptions/types' const PITR_CATEGORY_OPTIONS: { id: 'off' | 'on' @@ -149,7 +150,7 @@ const PITRSidePanel = () => { if (selectedOption === 'pitr_0' && subscriptionPitr !== undefined) { removeAddon({ projectRef, variant: subscriptionPitr.variant.identifier }) } else { - updateAddon({ projectRef, type: 'pitr', variant: selectedOption }) + updateAddon({ projectRef, type: 'pitr', variant: selectedOption as AddonVariantId }) } } diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts index bdabec39cc9..d500548f006 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts @@ -6,7 +6,7 @@ import logConstants from 'shared-data/logConstants' import BackwardIterator from 'components/ui/CodeEditor/Providers/BackwardIterator' import uniqBy from 'lodash/uniqBy' import { useEffect } from 'react' -import { PlanId } from 'data/subscriptions/org-subscription-query' +import { PlanId } from 'data/subscriptions/types' /** * Convert a micro timestamp from number/string to iso timestamp diff --git a/apps/studio/data/api.d.ts b/apps/studio/data/api.d.ts index 9b86b55740c..85e1b46997a 100644 --- a/apps/studio/data/api.d.ts +++ b/apps/studio/data/api.d.ts @@ -236,7 +236,7 @@ export interface paths { } '/platform/organizations/{slug}/payments/setup-intent': { /** Sets up a payment method */ - post: operations['SetupIntentController_setUpPaymentMethod'] + post: operations['SetupIntentOrgController_setUpPaymentMethod'] } '/platform/organizations/{slug}/billing/subscription': { /** Gets the current subscription */ @@ -467,6 +467,10 @@ export interface paths { /** Updates project's content */ patch: operations['ContentController_updateContent'] } + '/platform/projects/{ref}/daily-stats': { + /** Gets daily project stats */ + get: operations['DailyStatsController_getDailyStats'] + } '/platform/projects/{ref}/databases': { /** Gets non-removed databases of a specified project */ get: operations['DatabasesController_getDatabases'] @@ -1659,6 +1663,10 @@ export interface paths { /** List members of an organization */ get: operations['V1OrganizationMembersController_v1ListOrganizationMembers'] } + '/v1/organizations/{slug}': { + /** Gets information about the organization */ + get: operations['V1OrganizationSlugController_getOrganization'] + } '/v1/oauth/authorize': { /** Authorize user through oauth */ get: operations['OAuthController_authorize'] @@ -2713,6 +2721,9 @@ export interface components { stripeAccount?: string } } + HCaptchaBody: { + hcaptchaToken: string + } SetupIntentResponse: { id: string object: string @@ -2746,8 +2757,10 @@ export interface components { stripeAccount?: string } } + /** @enum {string} */ + BillingPlanId: 'free' | 'pro' | 'team' | 'enterprise' BillingSubscriptionPlan: { - id: Record + id: components['schemas']['BillingPlanId'] name: string price: number } @@ -2756,18 +2769,38 @@ export interface components { supabase_prod_id: string price: number } + /** @enum {string} */ + ProjectAddonType: 'custom_domain' | 'compute_instance' | 'pitr' + /** @enum {string} */ + AddonVariantId: + | 'ci_small' + | 'ci_medium' + | 'ci_large' + | 'ci_xlarge' + | 'ci_2xlarge' + | 'ci_4xlarge' + | 'ci_8xlarge' + | 'ci_12xlarge' + | 'ci_16xlarge' + | 'cd_default' + | 'pitr_7' + | 'pitr_14' + | 'pitr_28' + /** @enum {string} */ + ProjectAddonVariantPricingType: 'fixed' | 'usage' + /** @enum {string} */ + ProjectAddonPricingInterval: 'monthly' | 'hourly' ProjectAddonVariantResponse: { - identifier: string + identifier: components['schemas']['AddonVariantId'] + price_type: components['schemas']['ProjectAddonVariantPricingType'] + price_interval: components['schemas']['ProjectAddonPricingInterval'] name: string price_description: string - price_type: Record - price_interval: Record price: number meta?: Record } SelectedAddonResponse: { - /** @enum {string} */ - type: 'custom_domain' | 'compute_instance' | 'pitr' + type: components['schemas']['ProjectAddonType'] variant: components['schemas']['ProjectAddonVariantResponse'] } BillingProjectAddonResponse: { @@ -2827,9 +2860,9 @@ export interface components { expiry_year: number } ScheduledPlanChange: { + target_plan: components['schemas']['BillingPlanId'] /** Format: date-time */ at: string - target_plan: Record usage_billing_enabled: boolean } GetSubscriptionResponse: { @@ -2856,6 +2889,21 @@ export interface components { /** @enum {string} */ tier: 'tier_payg' | 'tier_pro' | 'tier_free' | 'tier_team' | 'tier_enterprise' } + /** @enum {string} */ + BillingPlanChangeType: 'upgrade' | 'downgrade' | 'none' + /** @enum {string} */ + BillingPlanEffectiveAt: 'now' | 'end_of_billing_period' | 'none' + PlanResponse: { + id: components['schemas']['BillingPlanId'] + change_type: components['schemas']['BillingPlanChangeType'] + effective_at: components['schemas']['BillingPlanEffectiveAt'] + name: string + price: number + is_current: boolean + } + PlansResponse: { + plans: components['schemas']['PlanResponse'][] + } UpcomingInvoice: Record ColumnPrivilege: { grantor: string @@ -3749,6 +3797,8 @@ export interface components { amount: number } PreviewProjectTransferResponse: { + source_subscription_plan: components['schemas']['BillingPlanId'] + target_subscription_plan: components['schemas']['BillingPlanId'] valid: boolean warnings: components['schemas']['PreviewTransferInfo'][] errors: components['schemas']['PreviewTransferInfo'][] @@ -3761,8 +3811,6 @@ export interface components { credits_on_source_organization: number costs_on_target_organization: number charge_on_target_organization: number - source_subscription_plan: Record - target_subscription_plan: Record | null source_invoice_items: components['schemas']['PreviewTransferInvoiceItem'][] target_invoice_items: components['schemas']['PreviewTransferInvoiceItem'][] } @@ -3881,8 +3929,7 @@ export interface components { fileSizeLimit: number } AvailableAddonResponse: { - /** @enum {string} */ - type: 'custom_domain' | 'compute_instance' | 'pitr' + type: components['schemas']['ProjectAddonType'] name: string variants: components['schemas']['ProjectAddonVariantResponse'][] } @@ -3892,9 +3939,8 @@ export interface components { available_addons: components['schemas']['AvailableAddonResponse'][] } UpdateAddonBody: { - addon_variant: Record - /** @enum {string} */ - addon_type: 'custom_domain' | 'compute_instance' | 'pitr' + addon_variant: components['schemas']['AddonVariantId'] + addon_type: components['schemas']['ProjectAddonType'] } ServiceApiKey: { api_key_encrypted?: string @@ -4384,9 +4430,8 @@ export interface components { expiry_time: string } UpdateAddonAdminBody: { - addon_variant: Record - /** @enum {string} */ - addon_type: 'custom_domain' | 'compute_instance' | 'pitr' + addon_variant: components['schemas']['AddonVariantId'] + addon_type: components['schemas']['ProjectAddonType'] price_id?: string } DatabaseResponse: { @@ -4775,6 +4820,12 @@ export interface components { role_name: string mfa_enabled: boolean } + V1OrganizationSlugResponse: { + plan?: components['schemas']['BillingPlanId'] + opt_in_tags: ('AI_SQL_GENERATOR_OPT_IN' | 'PREVIEW_BRANCHES_OPT_IN')[] + id: string + name: string + } OAuthTokenBody: { /** @enum {string} */ grant_type: 'authorization_code' | 'refresh_token' @@ -4896,8 +4947,7 @@ export interface components { supabase_org_id: string } FlyOrganization: { - /** @enum {string} */ - plan: 'free' | 'pro' | 'team' | 'enterprise' + plan: components['schemas']['BillingPlanId'] id: string supabase_org_id: string name: string @@ -6381,13 +6431,27 @@ export interface operations { } } /** Sets up a payment method */ - SetupIntentController_setUpPaymentMethod: { + SetupIntentOrgController_setUpPaymentMethod: { + parameters: { + path: { + /** @description Organization slug */ + slug: string + } + } + requestBody: { + content: { + 'application/json': components['schemas']['HCaptchaBody'] + } + } responses: { 201: { content: { 'application/json': components['schemas']['SetupIntentResponse'] } } + 403: { + content: never + } /** @description Failed to set up a payment method */ 500: { content: never @@ -6501,7 +6565,7 @@ export interface operations { responses: { 200: { content: { - 'application/json': Record + 'application/json': components['schemas']['PlansResponse'] } } 403: { @@ -8461,6 +8525,32 @@ export interface operations { } } } + /** Gets daily project stats */ + DailyStatsController_getDailyStats: { + parameters: { + query: { + attribute: string + interval: string + endDate: string + startDate: string + } + path: { + /** @description Project ref */ + ref: string + } + } + responses: { + 200: { + content: { + 'application/json': Record + } + } + /** @description Failed to get daily project stats */ + 500: { + content: never + } + } + } /** Gets non-removed databases of a specified project */ DatabasesController_getDatabases: { parameters: { @@ -9328,6 +9418,7 @@ export interface operations { path: { /** @description Project ref */ ref: string + addon_variant: components['schemas']['AddonVariantId'] } } responses: { @@ -9843,6 +9934,25 @@ export interface operations { } } } + /** Sets up a payment method */ + SetupIntentController_setUpPaymentMethod: { + requestBody: { + content: { + 'application/json': components['schemas']['HCaptchaBody'] + } + } + responses: { + 201: { + content: { + 'application/json': components['schemas']['SetupIntentResponse'] + } + } + /** @description Failed to set up a payment method */ + 500: { + content: never + } + } + } /** Sends analytics server event */ TelemetryEventController_sendServerEvent: { requestBody: { @@ -10871,6 +10981,7 @@ export interface operations { path: { /** @description Project ref */ ref: string + addon_variant: components['schemas']['AddonVariantId'] } } responses: { @@ -12398,6 +12509,21 @@ export interface operations { } } } + /** Gets information about the organization */ + V1OrganizationSlugController_getOrganization: { + parameters: { + path: { + slug: string + } + } + responses: { + 200: { + content: { + 'application/json': components['schemas']['V1OrganizationSlugResponse'] + } + } + } + } /** Authorize user through oauth */ OAuthController_authorize: { parameters: { diff --git a/apps/studio/data/invoices/invoices-overdue-query.ts b/apps/studio/data/invoices/invoices-overdue-query.ts index 4cc20b8a736..a34b563e09f 100644 --- a/apps/studio/data/invoices/invoices-overdue-query.ts +++ b/apps/studio/data/invoices/invoices-overdue-query.ts @@ -1,22 +1,19 @@ import { useQuery, UseQueryOptions } from '@tanstack/react-query' -import { get } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' +import { get } from 'data/fetchers' import { invoicesKeys } from './keys' +import { components } from 'data/api' -export type OverdueInvoicesResponse = { - id: string - organization_id: number -} +export type OverdueInvoicesResponse = components['schemas']['OverdueInvoiceCount'] export async function getOverdueInvoices(signal?: AbortSignal) { - const response = await get(`${API_URL}/stripe/invoices/overdue`, { + const { data, error } = await get('/platform/stripe/invoices/overdue', { signal, }) - if (response.error) { - throw response.error + if (error) { + throw error } - return response as OverdueInvoicesResponse[] + return data } export type OverdueInvoicesData = Awaited> diff --git a/apps/studio/data/stripe/setup-intent-mutation.ts b/apps/studio/data/stripe/setup-intent-mutation.ts index e6fab19727f..cad5489d964 100644 --- a/apps/studio/data/stripe/setup-intent-mutation.ts +++ b/apps/studio/data/stripe/setup-intent-mutation.ts @@ -10,7 +10,6 @@ export type SetupIntentVariables = { export async function setupIntent({ hcaptchaToken }: SetupIntentVariables) { const { data, error } = await post('/platform/stripe/setup-intent', { - // @ts-ignore [Joshen] API seems to be having the wrong spec body: { hcaptchaToken }, }) if (error) throw error diff --git a/apps/studio/data/subscriptions/keys.ts b/apps/studio/data/subscriptions/keys.ts index 91986c7dde9..956211a7a85 100644 --- a/apps/studio/data/subscriptions/keys.ts +++ b/apps/studio/data/subscriptions/keys.ts @@ -1,5 +1,3 @@ -// [Joshen TODO] To remove subscription V2 after v2 is launched and stable to deprecate old billing ui + endpoints - export const subscriptionKeys = { orgSubscription: (orgSlug: string | undefined) => ['organizations', orgSlug, 'subscription'] as const, diff --git a/apps/studio/data/subscriptions/org-plans-query.ts b/apps/studio/data/subscriptions/org-plans-query.ts index 1cfd667e528..a3cb6ea3b9e 100644 --- a/apps/studio/data/subscriptions/org-plans-query.ts +++ b/apps/studio/data/subscriptions/org-plans-query.ts @@ -1,28 +1,21 @@ import { useQuery, UseQueryOptions } from '@tanstack/react-query' -import { get } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' +import { get } from 'data/fetchers' import { subscriptionKeys } from './keys' export type OrgPlansVariables = { orgSlug?: string } -export type OrgPlansResponse = { - id: 'free' | 'pro' | 'team' | 'enterprise' - name: string - price: number - is_current: boolean - change_type: 'downgrade' | 'upgrade' | 'none' - effective_at: string -}[] - export async function getOrgPlans({ orgSlug }: OrgPlansVariables, signal?: AbortSignal) { if (!orgSlug) throw new Error('orgSlug is required') - const response = await get(`${API_URL}/organizations/${orgSlug}/billing/plans`, { signal }) - if (response.error) throw response.error + const { error, data } = await get('/platform/organizations/{slug}/billing/plans', { + params: { path: { slug: orgSlug } }, + signal, + }) + if (error) throw error - return response.plans as OrgPlansResponse + return data } export type OrgPlansData = Awaited> diff --git a/apps/studio/data/subscriptions/org-subscription-query.ts b/apps/studio/data/subscriptions/org-subscription-query.ts index 75c5f6526cb..ce4078a0087 100644 --- a/apps/studio/data/subscriptions/org-subscription-query.ts +++ b/apps/studio/data/subscriptions/org-subscription-query.ts @@ -1,6 +1,5 @@ import { useQuery, UseQueryOptions } from '@tanstack/react-query' -import { get } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' +import { get } from 'data/fetchers' import { subscriptionKeys } from './keys' import { ResponseError } from 'types' @@ -8,77 +7,19 @@ export type OrgSubscriptionVariables = { orgSlug?: string } -export type PlanId = 'free' | 'pro' | 'team' | 'enterprise' - -export type ProjectAddon = { - addons: { - type: 'custom_domain' | 'compute_instance' | 'pitr' - variant: { - identifier: string - name: string - price: number - price_description: string - price_interval: string - price_type: string - } - }[] - name: string - ref: string -} - -export type OrgSubscription = { - billing_cycle_anchor: number - current_period_start: number - current_period_end: number - next_invoice_at: number - usage_billing_enabled: boolean - plan: { - id: PlanId - name: string - price: number - } - addons: { - name: string - supabase_prod_id: string - price: number - }[] - usage_fees: { - metric: string - pricingStrategy: 'UNIT' | 'PACKAGE' - pricingOptions: { - perUnitPrice?: number - packagePrice?: number - packageSize?: number - freeUnits: number - } - }[] - payment_method_id?: string - payment_method_type: 'invoice' | 'card' | 'none' - payment_method_card_details?: { - last_4_digits: string - brand: string - expiry_month: number - expiry_year: number - } - project_addons: ProjectAddon[] - billing_via_partner?: boolean - scheduled_plan_change: { - at: string - target_plan: PlanId - usage_billing_enabled: boolean - } | null -} - export async function getOrgSubscription( { orgSlug }: OrgSubscriptionVariables, signal?: AbortSignal ) { if (!orgSlug) throw new Error('orgSlug is required') - const response = await get(`${API_URL}/organizations/${orgSlug}/billing/subscription`, { signal }) - if (response.error) throw response.error + const { error, data } = await get('/platform/organizations/{slug}/billing/subscription', { + params: { path: { slug: orgSlug } }, + signal, + }) + if (error) throw error - return response as OrgSubscription + return data } export type OrgSubscriptionData = Awaited> diff --git a/apps/studio/data/subscriptions/org-subscription-update-mutation.ts b/apps/studio/data/subscriptions/org-subscription-update-mutation.ts index 464dbe8c88d..da5490d4db4 100644 --- a/apps/studio/data/subscriptions/org-subscription-update-mutation.ts +++ b/apps/studio/data/subscriptions/org-subscription-update-mutation.ts @@ -1,6 +1,5 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' -import { isResponseOk, put } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' +import { put } from 'data/fetchers' import { toast } from 'react-hot-toast' import { ResponseError } from 'types/base' import { subscriptionKeys } from './keys' @@ -22,15 +21,22 @@ export async function updateOrgSubscription({ if (!slug) throw new Error('slug is required') if (!tier) throw new Error('tier is required') - const payload: { tier: string; payment_method?: string } = { tier } + const payload: { tier: SubscriptionTier; payment_method?: string } = { tier } if (paymentMethod !== undefined) payload.payment_method = paymentMethod - const response = await put(`${API_URL}/organizations/${slug}/billing/subscription`, payload) - if (!isResponseOk(response)) { - throw response.error + const { error, data } = await put(`/platform/organizations/{slug}/billing/subscription`, { + body: { + payment_method: payload.payment_method, + tier: payload.tier, + }, + params: { path: { slug } }, + }) + + if (error) { + throw error } - return response + return data } type OrgSubscriptionUpdateData = Awaited> diff --git a/apps/studio/data/subscriptions/project-addon-remove-mutation.ts b/apps/studio/data/subscriptions/project-addon-remove-mutation.ts index 75a930727fe..5c0bcf06b87 100644 --- a/apps/studio/data/subscriptions/project-addon-remove-mutation.ts +++ b/apps/studio/data/subscriptions/project-addon-remove-mutation.ts @@ -1,18 +1,14 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' import { toast } from 'react-hot-toast' -import { delete_ } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' +import { del } from 'data/fetchers' import { ResponseError } from 'types' import { subscriptionKeys } from './keys' +import { AddonVariantId } from './types' export type ProjectAddonRemoveVariables = { projectRef: string - variant: string -} - -export type ProjectAddonRemoveResponse = { - error?: any + variant: AddonVariantId } export async function removeSubscriptionAddon({ @@ -22,12 +18,18 @@ export async function removeSubscriptionAddon({ if (!projectRef) throw new Error('projectRef is required') if (!variant) throw new Error('variant is required') - const response = (await delete_( - `${API_URL}/projects/${projectRef}/billing/addons/${variant}` - )) as ProjectAddonRemoveResponse - if (response.error) throw response.error + const { data, error } = await del(`/platform/projects/{ref}/billing/addons/{addon_variant}`, { + params: { + path: { + ref: projectRef, + addon_variant: variant, + }, + }, + }) - return response + if (error) throw error + + return data } type ProjectAddonRemoveData = Awaited> diff --git a/apps/studio/data/subscriptions/project-addon-update-mutation.ts b/apps/studio/data/subscriptions/project-addon-update-mutation.ts index e8ad01d8ebe..d135b153ff1 100644 --- a/apps/studio/data/subscriptions/project-addon-update-mutation.ts +++ b/apps/studio/data/subscriptions/project-addon-update-mutation.ts @@ -1,19 +1,15 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' import { toast } from 'react-hot-toast' -import { post } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' +import { post } from 'data/fetchers' import { ResponseError } from 'types' import { subscriptionKeys } from './keys' +import { AddonVariantId, ProjectAddonType } from './types' export type ProjectAddonUpdateVariables = { projectRef: string - variant: string - type: 'custom_domain' | 'compute_instance' | 'pitr' -} - -export type ProjectAddonUpdateResponse = { - error?: any + variant: AddonVariantId + type: ProjectAddonType } export async function updateSubscriptionAddon({ @@ -25,13 +21,21 @@ export async function updateSubscriptionAddon({ if (!variant) throw new Error('variant is required') if (!type) throw new Error('type is required') - const response = (await post(`${API_URL}/projects/${projectRef}/billing/addons`, { - addon_type: type, - addon_variant: variant, - })) as ProjectAddonUpdateResponse - if (response.error) throw response.error + const { data, error } = await post(`/platform/projects/{ref}/billing/addons`, { + params: { + path: { + ref: projectRef, + }, + }, + body: { + addon_type: type, + addon_variant: variant, + }, + }) - return response + if (error) throw error + + return data } type ProjectAddonUpdateData = Awaited> diff --git a/apps/studio/data/subscriptions/project-addons-query.ts b/apps/studio/data/subscriptions/project-addons-query.ts index f5f38058d40..7d8533562f8 100644 --- a/apps/studio/data/subscriptions/project-addons-query.ts +++ b/apps/studio/data/subscriptions/project-addons-query.ts @@ -1,6 +1,5 @@ import { useQuery, UseQueryOptions } from '@tanstack/react-query' -import { get } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' +import { get } from 'data/fetchers' import { subscriptionKeys } from './keys' import { ResponseError } from 'types' @@ -8,54 +7,20 @@ export type ProjectAddonsVariables = { projectRef?: string } -export type ProjectAddonType = 'compute_instance' | 'pitr' | 'custom_domain' - -export type ProjectAddonVariant = { - identifier: string - name: string - price: number - price_description: string - price_interval: 'monthly' | 'hourly' - price_type: 'fixed' | 'usage' - meta?: ProjectAddonVariantMeta -} - -export interface ProjectAddonVariantMeta { - cpu_cores?: number - cpu_dedicated?: boolean - baseline_disk_io_mbs?: number - max_disk_io_mbs?: number - memory_gb?: number - connections_direct?: number - connections_pooler?: number - backup_duration_days?: number -} - -export type ProjectSelectedAddon = { - type: ProjectAddonType - variant: ProjectAddonVariant -} - -export type ProjectAddonsResponse = { - ref: string - selected_addons: ProjectSelectedAddon[] - available_addons: { - name: string - type: ProjectAddonType - variants: ProjectAddonVariant[] - }[] -} - export async function getProjectAddons( { projectRef }: ProjectAddonsVariables, signal?: AbortSignal ) { if (!projectRef) throw new Error('projectRef is required') - const response = await get(`${API_URL}/projects/${projectRef}/billing/addons`, { signal }) - if (response.error) throw response.error + const { error, data } = await get(`/platform/projects/{ref}/billing/addons`, { + params: { path: { ref: projectRef } }, + signal, + }) - return response as ProjectAddonsResponse + if (error) throw error + + return data } export type ProjectAddonsData = Awaited> diff --git a/apps/studio/data/subscriptions/types.ts b/apps/studio/data/subscriptions/types.ts index d0755cb9406..e4ec4475247 100644 --- a/apps/studio/data/subscriptions/types.ts +++ b/apps/studio/data/subscriptions/types.ts @@ -1,6 +1,34 @@ +import { components } from 'data/api' + export type SubscriptionTier = | 'tier_free' | 'tier_pro' | 'tier_payg' | 'tier_team' | 'tier_enterprise' + +export type AddonVariantId = components['schemas']['AddonVariantId'] + +export type OrgSubscription = components['schemas']['GetSubscriptionResponse'] + +export type ProjectAddon = components['schemas']['BillingProjectAddonResponse'] + +export type PlanId = components['schemas']['BillingPlanId'] + +export type OrgPlan = components['schemas']['PlanResponse'] + +export type ProjectAddonType = components['schemas']['ProjectAddonType'] + +export interface ProjectAddonVariantMeta { + cpu_cores?: number + cpu_dedicated?: boolean + baseline_disk_io_mbs?: number + max_disk_io_mbs?: number + memory_gb?: number + connections_direct?: number + connections_pooler?: number + backup_duration_days?: number +} + +export type ProjectSelectedAddon = components['schemas']['SelectedAddonResponse'] +export type ProjectAvailableAddon = components['schemas']['AvailableAddonResponse']