From 758d270f7c6ad87a00e06a3c1744e2df93162164 Mon Sep 17 00:00:00 2001 From: simeng-li Date: Mon, 12 Aug 2024 09:57:45 +0800 Subject: [PATCH] refactor(core): merge subscription usage API request (#6427) * refactor(core): merge subscription usage API request merge subscription usage API request * fix(core): fix type issue fix type issue --- .../connector-logto-email/package.json | 2 +- packages/console/package.json | 2 +- packages/console/src/cloud/types/router.ts | 18 ++- .../SubscriptionDataProvider/index.tsx | 4 +- .../SubscriptionDataProvider/types.ts | 7 +- .../use-new-subscription-data.ts | 109 ++++++++---------- .../src/hooks/use-new-subscription-quota.ts | 19 --- .../use-new-subscription-scopes-usage.ts | 39 ------- .../src/hooks/use-new-subscription-usage.ts | 19 --- .../CreatePermissionModal/index.tsx | 4 +- .../AssignPermissionsModal/index.tsx | 4 +- packages/core/package.json | 2 +- packages/core/src/utils/subscription/types.ts | 1 + pnpm-lock.yaml | 40 ++----- 14 files changed, 77 insertions(+), 193 deletions(-) delete mode 100644 packages/console/src/hooks/use-new-subscription-quota.ts delete mode 100644 packages/console/src/hooks/use-new-subscription-scopes-usage.ts delete mode 100644 packages/console/src/hooks/use-new-subscription-usage.ts diff --git a/packages/connectors/connector-logto-email/package.json b/packages/connectors/connector-logto-email/package.json index ff9df9cd5..4a71efdd8 100644 --- a/packages/connectors/connector-logto-email/package.json +++ b/packages/connectors/connector-logto-email/package.json @@ -52,7 +52,7 @@ "access": "public" }, "devDependencies": { - "@logto/cloud": "0.2.5-3b703da", + "@logto/cloud": "0.2.5-3452c56", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/node": "^20.11.20", diff --git a/packages/console/package.json b/packages/console/package.json index b93ee45ba..19fa4be98 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -27,7 +27,7 @@ "devDependencies": { "@fontsource/roboto-mono": "^5.0.0", "@jest/types": "^29.5.0", - "@logto/cloud": "0.2.5-923c26f", + "@logto/cloud": "0.2.5-3452c56", "@logto/connector-kit": "workspace:^4.0.0", "@logto/core-kit": "workspace:^2.5.0", "@logto/elements": "workspace:^0.0.0", diff --git a/packages/console/src/cloud/types/router.ts b/packages/console/src/cloud/types/router.ts index 7d6cf2f10..afdce6f43 100644 --- a/packages/console/src/cloud/types/router.ts +++ b/packages/console/src/cloud/types/router.ts @@ -19,20 +19,16 @@ export type Subscription = GuardedResponse; /* ===== Use `New` in the naming to avoid confusion with legacy types ===== */ +export type NewSubscriptionUsageResponse = GuardedResponse< + GetRoutes['/api/tenants/:tenantId/subscription-usage'] +>; /** The response of `GET /api/tenants/my/subscription/quota` has the same response type. */ -export type NewSubscriptionQuota = GuardedResponse< - GetRoutes['/api/tenants/:tenantId/subscription/quota'] ->; - +export type NewSubscriptionQuota = NewSubscriptionUsageResponse['quota']; /** The response of `GET /api/tenants/my/subscription/usage` has the same response type. */ -export type NewSubscriptionUsage = GuardedResponse< - GetRoutes['/api/tenants/:tenantId/subscription/usage'] ->; +export type NewSubscriptionUsage = NewSubscriptionUsageResponse['usage']; +export type NewSubscriptionResourceScopeUsage = NewSubscriptionUsageResponse['resources']; +export type NewSubscriptionRoleScopeUsage = NewSubscriptionUsageResponse['roles']; -/** The response of `GET /api/tenants/my/subscription/usage/:entityName/scopes` has the same response type. */ -export type NewSubscriptionScopeUsage = GuardedResponse< - GetRoutes['/api/tenants/:tenantId/subscription/usage/:entityName/scopes'] ->; /* ===== Use `New` in the naming to avoid confusion with legacy types ===== */ export type InvoicesResponse = GuardedResponse; diff --git a/packages/console/src/contexts/SubscriptionDataProvider/index.tsx b/packages/console/src/contexts/SubscriptionDataProvider/index.tsx index f0c83c637..708b9981a 100644 --- a/packages/console/src/contexts/SubscriptionDataProvider/index.tsx +++ b/packages/console/src/contexts/SubscriptionDataProvider/index.tsx @@ -30,8 +30,8 @@ export const SubscriptionDataContext = createContext({ currentSku: defaultLogtoSku, currentSubscriptionQuota: defaultSubscriptionQuota, currentSubscriptionUsage: defaultSubscriptionUsage, - currentSubscriptionScopeResourceUsage: {}, - currentSubscriptionScopeRoleUsage: {}, + currentSubscriptionResourceScopeUsage: {}, + currentSubscriptionRoleScopeUsage: {}, mutateSubscriptionQuotaAndUsages: noop, /* ==== For new pricing model ==== */ }); diff --git a/packages/console/src/contexts/SubscriptionDataProvider/types.ts b/packages/console/src/contexts/SubscriptionDataProvider/types.ts index 6cb185880..cc8c44954 100644 --- a/packages/console/src/contexts/SubscriptionDataProvider/types.ts +++ b/packages/console/src/contexts/SubscriptionDataProvider/types.ts @@ -3,7 +3,8 @@ import { type Subscription, type NewSubscriptionQuota, type NewSubscriptionUsage, - type NewSubscriptionScopeUsage, + type NewSubscriptionResourceScopeUsage, + type NewSubscriptionRoleScopeUsage, } from '@/cloud/types/router'; import { type SubscriptionPlan } from '@/types/subscriptions'; @@ -21,8 +22,8 @@ type NewSubscriptionSupplementContext = { currentSku: LogtoSkuResponse; currentSubscriptionQuota: NewSubscriptionQuota; currentSubscriptionUsage: NewSubscriptionUsage; - currentSubscriptionScopeResourceUsage: NewSubscriptionScopeUsage; - currentSubscriptionScopeRoleUsage: NewSubscriptionScopeUsage; + currentSubscriptionResourceScopeUsage: NewSubscriptionResourceScopeUsage; + currentSubscriptionRoleScopeUsage: NewSubscriptionRoleScopeUsage; mutateSubscriptionQuotaAndUsages: () => void; }; diff --git a/packages/console/src/contexts/SubscriptionDataProvider/use-new-subscription-data.ts b/packages/console/src/contexts/SubscriptionDataProvider/use-new-subscription-data.ts index 3c5a023f3..bee650642 100644 --- a/packages/console/src/contexts/SubscriptionDataProvider/use-new-subscription-data.ts +++ b/packages/console/src/contexts/SubscriptionDataProvider/use-new-subscription-data.ts @@ -1,6 +1,9 @@ import { cond, condString } from '@silverhand/essentials'; -import { useCallback, useContext, useMemo } from 'react'; +import { useContext, useMemo } from 'react'; +import useSWR from 'swr'; +import { useCloudApi } from '@/cloud/hooks/use-cloud-api'; +import { type NewSubscriptionUsageResponse } from '@/cloud/types/router'; import { defaultLogtoSku, defaultTenantResponse, @@ -10,63 +13,35 @@ import { import { isCloud, isDevFeaturesEnabled } from '@/consts/env'; import { TenantsContext } from '@/contexts/TenantsProvider'; import useLogtoSkus from '@/hooks/use-logto-skus'; -import useNewSubscriptionQuota from '@/hooks/use-new-subscription-quota'; -import useNewSubscriptionScopeUsage from '@/hooks/use-new-subscription-scopes-usage'; -import useNewSubscriptionUsage from '@/hooks/use-new-subscription-usage'; import useSubscription from '../../hooks/use-subscription'; import { type NewSubscriptionContext } from './types'; const useNewSubscriptionData: () => NewSubscriptionContext & { isLoading: boolean } = () => { + const cloudApi = useCloudApi(); + const { currentTenant } = useContext(TenantsContext); const { isLoading: isLogtoSkusLoading, data: fetchedLogtoSkus } = useLogtoSkus(); + const tenantId = condString(currentTenant?.id); + const { data: currentSubscription, isLoading: isSubscriptionLoading, mutate: mutateSubscription, - } = useSubscription(condString(currentTenant?.id)); + } = useSubscription(tenantId); const { - data: currentSubscriptionQuota, - isLoading: isSubscriptionQuotaLoading, - mutate: mutateSubscriptionQuota, - } = useNewSubscriptionQuota(condString(currentTenant?.id)); - - const { - data: currentSubscriptionUsage, - isLoading: isSubscriptionUsageLoading, - mutate: mutateSubscriptionUsage, - } = useNewSubscriptionUsage(condString(currentTenant?.id)); - - const { - scopeResourceUsage: { - data: scopeResourceUsage, - isLoading: isScopePerResourceUsageLoading, - mutate: mutateScopeResourceUsage, - }, - scopeRoleUsage: { - data: scopeRoleUsage, - isLoading: isScopePerRoleUsageLoading, - mutate: mutateScopeRoleUsage, - }, - } = useNewSubscriptionScopeUsage(condString(currentTenant?.id)); - - const mutateSubscriptionQuotaAndUsages = useCallback(() => { - if (!isDevFeaturesEnabled) { - return; - } - - void mutateSubscriptionQuota(); - void mutateSubscriptionUsage(); - void mutateScopeResourceUsage(); - void mutateScopeRoleUsage(); - }, [ - mutateScopeResourceUsage, - mutateScopeRoleUsage, - mutateSubscriptionQuota, - mutateSubscriptionUsage, - ]); + data: subscriptionUsageData, + isLoading: isSubscriptionUsageDataLoading, + mutate: mutateSubscriptionQuotaAndUsages, + } = useSWR( + isCloud && isDevFeaturesEnabled && tenantId && `/api/tenants/${tenantId}/subscription-usage`, + async () => + cloudApi.get('/api/tenants/:tenantId/subscription-usage', { + params: { tenantId }, + }) + ); const logtoSkus = useMemo(() => cond(isCloud && fetchedLogtoSkus) ?? [], [fetchedLogtoSkus]); @@ -75,24 +50,34 @@ const useNewSubscriptionData: () => NewSubscriptionContext & { isLoading: boolea [currentTenant?.planId, logtoSkus] ); - return { - isLoading: - isSubscriptionLoading || - isLogtoSkusLoading || - isSubscriptionQuotaLoading || - isSubscriptionUsageLoading || - isScopePerResourceUsageLoading || - isScopePerRoleUsageLoading, - logtoSkus, - currentSku, - currentSubscription: currentSubscription ?? defaultTenantResponse.subscription, - onCurrentSubscriptionUpdated: mutateSubscription, - mutateSubscriptionQuotaAndUsages, - currentSubscriptionQuota: currentSubscriptionQuota ?? defaultSubscriptionQuota, - currentSubscriptionUsage: currentSubscriptionUsage ?? defaultSubscriptionUsage, - currentSubscriptionScopeResourceUsage: scopeResourceUsage ?? {}, - currentSubscriptionScopeRoleUsage: scopeRoleUsage ?? {}, - }; + return useMemo( + () => ({ + isLoading: isSubscriptionLoading || isLogtoSkusLoading || isSubscriptionUsageDataLoading, + logtoSkus, + currentSku, + currentSubscription: currentSubscription ?? defaultTenantResponse.subscription, + onCurrentSubscriptionUpdated: mutateSubscription, + mutateSubscriptionQuotaAndUsages, + currentSubscriptionQuota: subscriptionUsageData?.quota ?? defaultSubscriptionQuota, + currentSubscriptionUsage: subscriptionUsageData?.usage ?? defaultSubscriptionUsage, + currentSubscriptionResourceScopeUsage: subscriptionUsageData?.resources ?? {}, + currentSubscriptionRoleScopeUsage: subscriptionUsageData?.roles ?? {}, + }), + [ + currentSku, + currentSubscription, + isLogtoSkusLoading, + isSubscriptionLoading, + isSubscriptionUsageDataLoading, + logtoSkus, + mutateSubscription, + mutateSubscriptionQuotaAndUsages, + subscriptionUsageData?.quota, + subscriptionUsageData?.resources, + subscriptionUsageData?.roles, + subscriptionUsageData?.usage, + ] + ); }; export default useNewSubscriptionData; diff --git a/packages/console/src/hooks/use-new-subscription-quota.ts b/packages/console/src/hooks/use-new-subscription-quota.ts deleted file mode 100644 index 495905a1e..000000000 --- a/packages/console/src/hooks/use-new-subscription-quota.ts +++ /dev/null @@ -1,19 +0,0 @@ -import useSWR from 'swr'; - -import { useCloudApi } from '@/cloud/hooks/use-cloud-api'; -import { type NewSubscriptionQuota } from '@/cloud/types/router'; -import { isCloud, isDevFeaturesEnabled } from '@/consts/env'; - -const useNewSubscriptionQuota = (tenantId: string) => { - const cloudApi = useCloudApi(); - - return useSWR( - isCloud && isDevFeaturesEnabled && tenantId && `/api/tenants/${tenantId}/subscription/quota`, - async () => - cloudApi.get('/api/tenants/:tenantId/subscription/quota', { - params: { tenantId }, - }) - ); -}; - -export default useNewSubscriptionQuota; diff --git a/packages/console/src/hooks/use-new-subscription-scopes-usage.ts b/packages/console/src/hooks/use-new-subscription-scopes-usage.ts deleted file mode 100644 index 5e39c2005..000000000 --- a/packages/console/src/hooks/use-new-subscription-scopes-usage.ts +++ /dev/null @@ -1,39 +0,0 @@ -import useSWR from 'swr'; - -import { useCloudApi } from '@/cloud/hooks/use-cloud-api'; -import { type NewSubscriptionScopeUsage } from '@/cloud/types/router'; -import { isCloud, isDevFeaturesEnabled } from '@/consts/env'; - -const useNewSubscriptionScopeUsage = (tenantId: string) => { - const cloudApi = useCloudApi(); - - const resourceEntityName = 'resources'; - const roleEntityName = 'roles'; - - return { - scopeResourceUsage: useSWR( - isCloud && - isDevFeaturesEnabled && - tenantId && - `/api/tenants/${tenantId}/subscription/usage/${resourceEntityName}/scopes`, - async () => - cloudApi.get('/api/tenants/:tenantId/subscription/usage/:entityName/scopes', { - params: { tenantId, entityName: resourceEntityName }, - search: {}, - }) - ), - scopeRoleUsage: useSWR( - isCloud && - isDevFeaturesEnabled && - tenantId && - `/api/tenants/${tenantId}/subscription/usage/${roleEntityName}/scopes`, - async () => - cloudApi.get('/api/tenants/:tenantId/subscription/usage/:entityName/scopes', { - params: { tenantId, entityName: roleEntityName }, - search: {}, - }) - ), - }; -}; - -export default useNewSubscriptionScopeUsage; diff --git a/packages/console/src/hooks/use-new-subscription-usage.ts b/packages/console/src/hooks/use-new-subscription-usage.ts deleted file mode 100644 index fe25b5cc7..000000000 --- a/packages/console/src/hooks/use-new-subscription-usage.ts +++ /dev/null @@ -1,19 +0,0 @@ -import useSWR from 'swr'; - -import { useCloudApi } from '@/cloud/hooks/use-cloud-api'; -import { type NewSubscriptionUsage } from '@/cloud/types/router'; -import { isCloud, isDevFeaturesEnabled } from '@/consts/env'; - -const useNewSubscriptionUsage = (tenantId: string) => { - const cloudApi = useCloudApi(); - - return useSWR( - isCloud && isDevFeaturesEnabled && tenantId && `/api/tenants/${tenantId}/subscription/usage`, - async () => - cloudApi.get('/api/tenants/:tenantId/subscription/usage', { - params: { tenantId }, - }) - ); -}; - -export default useNewSubscriptionUsage; diff --git a/packages/console/src/pages/ApiResourceDetails/ApiResourcePermissions/components/CreatePermissionModal/index.tsx b/packages/console/src/pages/ApiResourceDetails/ApiResourcePermissions/components/CreatePermissionModal/index.tsx index ee90978e2..398de8ce8 100644 --- a/packages/console/src/pages/ApiResourceDetails/ApiResourcePermissions/components/CreatePermissionModal/index.tsx +++ b/packages/console/src/pages/ApiResourceDetails/ApiResourcePermissions/components/CreatePermissionModal/index.tsx @@ -33,7 +33,7 @@ function CreatePermissionModal({ resourceId, totalResourceCount, onClose }: Prop currentPlan, currentSku, currentSubscriptionQuota, - currentSubscriptionScopeResourceUsage, + currentSubscriptionResourceScopeUsage, } = useContext(SubscriptionDataContext); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); @@ -62,7 +62,7 @@ function CreatePermissionModal({ resourceId, totalResourceCount, onClose }: Prop const isScopesPerResourceReachLimit = isDevFeaturesEnabled ? hasReachedSubscriptionQuotaLimit({ quotaKey: 'scopesPerResourceLimit', - usage: currentSubscriptionScopeResourceUsage[resourceId] ?? 0, + usage: currentSubscriptionResourceScopeUsage[resourceId] ?? 0, quota: currentSubscriptionQuota, }) : hasReachedQuotaLimit({ diff --git a/packages/console/src/pages/RoleDetails/RolePermissions/components/AssignPermissionsModal/index.tsx b/packages/console/src/pages/RoleDetails/RolePermissions/components/AssignPermissionsModal/index.tsx index 82d3be01c..69ce77ffd 100644 --- a/packages/console/src/pages/RoleDetails/RolePermissions/components/AssignPermissionsModal/index.tsx +++ b/packages/console/src/pages/RoleDetails/RolePermissions/components/AssignPermissionsModal/index.tsx @@ -27,7 +27,7 @@ type Props = { function AssignPermissionsModal({ roleId, roleType, totalRoleScopeCount, onClose }: Props) { const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const { currentPlan, currentSku, currentSubscriptionScopeRoleUsage, currentSubscriptionQuota } = + const { currentPlan, currentSku, currentSubscriptionRoleScopeUsage, currentSubscriptionQuota } = useContext(SubscriptionDataContext); const [isSubmitting, setIsSubmitting] = useState(false); const [scopes, setScopes] = useState([]); @@ -55,7 +55,7 @@ function AssignPermissionsModal({ roleId, roleType, totalRoleScopeCount, onClose const shouldBlockScopeAssignment = isDevFeaturesEnabled ? hasSurpassedSubscriptionQuotaLimit({ quotaKey: 'scopesPerRoleLimit', - usage: (currentSubscriptionScopeRoleUsage[roleId] ?? 0) + scopes.length, + usage: (currentSubscriptionRoleScopeUsage[roleId] ?? 0) + scopes.length, quota: currentSubscriptionQuota, }) : hasSurpassedQuotaLimit({ diff --git a/packages/core/package.json b/packages/core/package.json index 87d1f9473..404e5cac2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -95,7 +95,7 @@ "zod": "^3.23.8" }, "devDependencies": { - "@logto/cloud": "0.2.5-50ff8fe", + "@logto/cloud": "0.2.5-3452c56", "@silverhand/eslint-config": "6.0.1", "@silverhand/ts-config": "6.0.0", "@types/adm-zip": "^0.5.5", diff --git a/packages/core/src/utils/subscription/types.ts b/packages/core/src/utils/subscription/types.ts index 74f9f138b..f50fcb6d2 100644 --- a/packages/core/src/utils/subscription/types.ts +++ b/packages/core/src/utils/subscription/types.ts @@ -54,4 +54,5 @@ export const allReportSubscriptionUpdatesUsageKeys = Object.freeze([ 'organizationsEnabled', 'tenantMembersLimit', 'enterpriseSsoLimit', + 'hooksLimit', ]) satisfies readonly ReportSubscriptionUpdatesUsageKey[]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c43e01b75..f895e49cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1217,8 +1217,8 @@ importers: version: 3.23.8 devDependencies: '@logto/cloud': - specifier: 0.2.5-3b703da - version: 0.2.5-3b703da(zod@3.23.8) + specifier: 0.2.5-3452c56 + version: 0.2.5-3452c56(zod@3.23.8) '@silverhand/eslint-config': specifier: 6.0.1 version: 6.0.1(eslint@8.57.0)(prettier@3.0.0)(typescript@5.5.3) @@ -2458,8 +2458,8 @@ importers: specifier: ^29.5.0 version: 29.5.0 '@logto/cloud': - specifier: 0.2.5-923c26f - version: 0.2.5-923c26f(zod@3.23.8) + specifier: 0.2.5-3452c56 + version: 0.2.5-3452c56(zod@3.23.8) '@logto/connector-kit': specifier: workspace:^4.0.0 version: link:../toolkit/connector-kit @@ -2948,8 +2948,8 @@ importers: version: 3.23.8 devDependencies: '@logto/cloud': - specifier: 0.2.5-50ff8fe - version: 0.2.5-50ff8fe(zod@3.23.8) + specifier: 0.2.5-3452c56 + version: 0.2.5-3452c56(zod@3.23.8) '@silverhand/eslint-config': specifier: 6.0.1 version: 6.0.1(eslint@8.57.0)(prettier@3.0.0)(typescript@5.5.3) @@ -5076,16 +5076,8 @@ packages: '@logto/client@2.7.2': resolution: {integrity: sha512-jsmuDl9QpXfR3uLEMPE67tvYoL5XcjJi+4yGqucYPjd4GH6SUHp3N9skk8C/OyygnKDPLY+ttwD0LaIbpGvn+Q==} - '@logto/cloud@0.2.5-3b703da': - resolution: {integrity: sha512-VCevQnxP5910s/cDYAxoJRim9iH1yN/La0HAlOP6FhVGtZofYwTTfT9AQXC+dZScgydpcFWo4k/6MYOFRtZCLg==} - engines: {node: ^20.9.0} - - '@logto/cloud@0.2.5-50ff8fe': - resolution: {integrity: sha512-EMIGnx3swILEcSvYsAlPg9E1srtPcZxHxVH+D/dTrg8ctHbRAJkFbeuQFhwHGvs1dfgULd9MKtaAkL2qckExMw==} - engines: {node: ^20.9.0} - - '@logto/cloud@0.2.5-923c26f': - resolution: {integrity: sha512-NAK9/T7HxEfE2djO6VTekMziOXH6NtbAzwumZcZo0bqIUDGiKlUvted/KY6iqpCdfFOF4aIyKp+pvlQIjj1T6Q==} + '@logto/cloud@0.2.5-3452c56': + resolution: {integrity: sha512-19MGifwYGxjQMPrm6monfoQyOp9UTL/chtZE0JugppNwvvLyqr3Nx0maCHuwrydLt0ImBSgVmPW1cJVvu2tVPg==} engines: {node: ^20.9.0} '@logto/js@4.1.4': @@ -14719,21 +14711,7 @@ snapshots: camelcase-keys: 7.0.2 jose: 5.6.3 - '@logto/cloud@0.2.5-3b703da(zod@3.23.8)': - dependencies: - '@silverhand/essentials': 2.9.1 - '@withtyped/server': 0.13.6(zod@3.23.8) - transitivePeerDependencies: - - zod - - '@logto/cloud@0.2.5-50ff8fe(zod@3.23.8)': - dependencies: - '@silverhand/essentials': 2.9.1 - '@withtyped/server': 0.13.6(zod@3.23.8) - transitivePeerDependencies: - - zod - - '@logto/cloud@0.2.5-923c26f(zod@3.23.8)': + '@logto/cloud@0.2.5-3452c56(zod@3.23.8)': dependencies: '@silverhand/essentials': 2.9.1 '@withtyped/server': 0.13.6(zod@3.23.8)