From f9af25b57e15d6e51a35142a92b7736141e359b2 Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 23 Aug 2024 16:01:40 +0200 Subject: [PATCH] Fix sentry issue (#6719) https://twenty-v7.sentry.io/issues/5677123076/?environment=prod&project=4507072499810304&query=is%3Aunresolved+issue.priority%3A%5Bhigh%2C+medium%5D&referrer=issue-stream&statsPeriod=7d&stream_index=12 Removes billing section when is_free_access_enabled --- packages/twenty-front/src/App.tsx | 21 +++++---- .../twenty-front/src/generated/graphql.tsx | 47 +++++++++++++++---- .../SettingsNavigationDrawerItems.tsx | 5 +- ...eServerlessFunctionUpdateFormState.test.ts | 2 +- .../modules/workspace/types/FeatureFlagKey.ts | 1 + ...ttingsServerlessFunctionDetail.stories.tsx | 1 - .../core-modules/billing/billing.resolver.ts | 2 +- .../billing-portal.workspace-service.ts | 10 ++-- 8 files changed, 64 insertions(+), 25 deletions(-) diff --git a/packages/twenty-front/src/App.tsx b/packages/twenty-front/src/App.tsx index f3668ff0b2da..4f1267000478 100644 --- a/packages/twenty-front/src/App.tsx +++ b/packages/twenty-front/src/App.tsx @@ -3,7 +3,6 @@ import { createBrowserRouter, createRoutesFromElements, Outlet, - redirect, Route, RouterProvider, Routes, @@ -192,14 +191,12 @@ const createRouter = ( path={SettingsPath.AccountsEmails} element={} /> - } - loader={() => { - if (!isBillingEnabled) return redirect(AppPath.Index); - return null; - }} - /> + {isBillingEnabled && ( + } + /> + )} } @@ -324,15 +321,19 @@ const createRouter = ( export const App = () => { const billing = useRecoilValue(billingState); + const isFreeAccessEnabled = useIsFeatureEnabled('IS_FREE_ACCESS_ENABLED'); const isCRMMigrationEnabled = useIsFeatureEnabled('IS_CRM_MIGRATION_ENABLED'); const isServerlessFunctionSettingsEnabled = useIsFeatureEnabled( 'IS_FUNCTION_SETTINGS_ENABLED', ); + const isBillingPageEnabled = + billing?.isBillingEnabled && !isFreeAccessEnabled; + return ( ; + /** Version of the serverless function to execute */ + version?: Scalars['String']; +}; + export type FeatureFlag = { __typename?: 'FeatureFlag'; id: Scalars['UUID']; @@ -287,6 +296,13 @@ export type FullName = { lastName: Scalars['String']; }; +export type GetServerlessFunctionSourceCodeInput = { + /** The id of the function. */ + id: Scalars['ID']; + /** The version of the function */ + version?: Scalars['String']; +}; + export type InvalidatePassword = { __typename?: 'InvalidatePassword'; /** Boolean that confirms query was dispatched */ @@ -343,8 +359,9 @@ export type Mutation = { generateJWT: AuthTokens; generateTransientToken: TransientToken; impersonate: Verify; + publishServerlessFunction: ServerlessFunction; renewToken: AuthTokens; - runWorkflowVersion: WorkflowTriggerResult; + runWorkflowVersion: WorkflowRun; sendInviteLink: SendInviteLink; signUp: LoginToken; skipSyncEmailOnboardingStep: OnboardingStepSuccess; @@ -431,8 +448,7 @@ export type MutationExchangeAuthorizationCodeArgs = { export type MutationExecuteOneServerlessFunctionArgs = { - id: Scalars['UUID']; - payload?: InputMaybe; + input: ExecuteServerlessFunctionInput; }; @@ -452,6 +468,11 @@ export type MutationImpersonateArgs = { }; +export type MutationPublishServerlessFunctionArgs = { + input: PublishServerlessFunctionInput; +}; + + export type MutationRenewTokenArgs = { appToken: Scalars['String']; }; @@ -594,6 +615,11 @@ export type ProductPricesEntity = { totalNumberOfPrices: Scalars['Int']; }; +export type PublishServerlessFunctionInput = { + /** The id of the function. */ + id: Scalars['ID']; +}; + export type Query = { __typename?: 'Query'; billingPortalSession: SessionEntity; @@ -606,6 +632,7 @@ export type Query = { getAISQLQuery: AisqlQueryResult; getPostgresCredentials?: Maybe; getProductPrices: ProductPricesEntity; + getServerlessFunctionSourceCode: Scalars['String']; getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal; getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal; @@ -649,6 +676,11 @@ export type QueryGetProductPricesArgs = { }; +export type QueryGetServerlessFunctionSourceCodeArgs = { + input: GetServerlessFunctionSourceCodeInput; +}; + + export type QueryGetTimelineCalendarEventsFromCompanyIdArgs = { companyId: Scalars['UUID']; page: Scalars['Int']; @@ -768,9 +800,9 @@ export type ServerlessFunction = { createdAt: Scalars['DateTime']; description?: Maybe; id: Scalars['UUID']; + latestVersion?: Maybe; name: Scalars['String']; runtime: Scalars['String']; - sourceCodeFullPath: Scalars['String']; sourceCodeHash: Scalars['String']; syncStatus: ServerlessFunctionSyncStatus; updatedAt: Scalars['DateTime']; @@ -1054,10 +1086,9 @@ export type Verify = { user: User; }; -export type WorkflowTriggerResult = { - __typename?: 'WorkflowTriggerResult'; - /** Execution result in JSON format */ - result?: Maybe; +export type WorkflowRun = { + __typename?: 'WorkflowRun'; + workflowRunId: Scalars['UUID']; }; export type Workspace = { diff --git a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx index f68981a195e6..89bb9c0de214 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx @@ -33,7 +33,10 @@ export const SettingsNavigationDrawerItems = () => { const isFunctionSettingsEnabled = useIsFeatureEnabled( 'IS_FUNCTION_SETTINGS_ENABLED', ); + const isFreeAccessEnabled = useIsFeatureEnabled('IS_FREE_ACCESS_ENABLED'); const isCRMMigrationEnabled = useIsFeatureEnabled('IS_CRM_MIGRATION_ENABLED'); + const isBillingPageEnabled = + billing?.isBillingEnabled && !isFreeAccessEnabled; return ( <> @@ -84,7 +87,7 @@ export const SettingsNavigationDrawerItems = () => { path={SettingsPath.WorkspaceMembersPage} Icon={IconUsers} /> - {billing?.isBillingEnabled && ( + {isBillingPageEnabled && ( { ); useGetOneServerlessFunctionMock.useGetOneServerlessFunction.mockReturnValue( { - serverlessFunction: { sourceCodeFullPath: undefined }, + serverlessFunction: { name: 'name' }, }, ); const useGetOneServerlessFunctionSourceCodeMock = jest.requireMock( diff --git a/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts b/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts index 1185aa04f0c5..6ed0dd7f1e05 100644 --- a/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts +++ b/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts @@ -7,4 +7,5 @@ export type FeatureFlagKey = | 'IS_FUNCTION_SETTINGS_ENABLED' | 'IS_COPILOT_ENABLED' | 'IS_CRM_MIGRATION_ENABLED' + | 'IS_FREE_ACCESS_ENABLED' | 'IS_MESSAGE_THREAD_SUBSCRIBER_ENABLED'; diff --git a/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx b/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx index 219f10e73192..8543e0376a6e 100644 --- a/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx +++ b/packages/twenty-front/src/pages/settings/serverless-functions/__stories__/SettingsServerlessFunctionDetail.stories.tsx @@ -38,7 +38,6 @@ const meta: Meta = { description: '', syncStatus: 'READY', runtime: 'nodejs18.x', - sourceCodeFullPath: SOURCE_CODE_FULL_PATH, sourceCodeHash: '42d2734b3dc8a7b45a16803ed7f417bc', updatedAt: '2024-02-24T10:23:10.673Z', createdAt: '2024-02-24T10:23:10.673Z', diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts index a1bf997b8ca6..a60992cc7090 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts @@ -43,7 +43,7 @@ export class BillingResolver { @Args() { returnUrlPath }: BillingSessionInput, ) { return { - url: await this.billingPortalWorkspaceService.computeBillingPortalSessionURL( + url: await this.billingPortalWorkspaceService.computeBillingPortalSessionURLOrThrow( user.defaultWorkspaceId, returnUrlPath, ), diff --git a/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts b/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts index ca4685f07e30..d4a7a333f394 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts @@ -65,18 +65,22 @@ export class BillingPortalWorkspaceService { return session.url; } - async computeBillingPortalSessionURL( + async computeBillingPortalSessionURLOrThrow( workspaceId: string, returnUrlPath?: string, ) { - const currentSubscriptionItem = + const currentSubscription = await this.billingSubscriptionService.getCurrentBillingSubscriptionOrThrow( { workspaceId, }, ); - const stripeCustomerId = currentSubscriptionItem.stripeCustomerId; + if (!currentSubscription) { + throw new Error('Error: missing subscription'); + } + + const stripeCustomerId = currentSubscription.stripeCustomerId; if (!stripeCustomerId) { throw new Error('Error: missing stripeCustomerId');