martmull
2024-08-23 16:01:40 +02:00
committed by GitHub
parent ee6180a76f
commit f9af25b57e
8 changed files with 64 additions and 25 deletions

View File

@ -3,7 +3,6 @@ import {
createBrowserRouter, createBrowserRouter,
createRoutesFromElements, createRoutesFromElements,
Outlet, Outlet,
redirect,
Route, Route,
RouterProvider, RouterProvider,
Routes, Routes,
@ -192,14 +191,12 @@ const createRouter = (
path={SettingsPath.AccountsEmails} path={SettingsPath.AccountsEmails}
element={<SettingsAccountsEmails />} element={<SettingsAccountsEmails />}
/> />
<Route {isBillingEnabled && (
path={SettingsPath.Billing} <Route
element={<SettingsBilling />} path={SettingsPath.Billing}
loader={() => { element={<SettingsBilling />}
if (!isBillingEnabled) return redirect(AppPath.Index); />
return null; )}
}}
/>
<Route <Route
path={SettingsPath.WorkspaceMembersPage} path={SettingsPath.WorkspaceMembersPage}
element={<SettingsWorkspaceMembers />} element={<SettingsWorkspaceMembers />}
@ -324,15 +321,19 @@ const createRouter = (
export const App = () => { export const App = () => {
const billing = useRecoilValue(billingState); const billing = useRecoilValue(billingState);
const isFreeAccessEnabled = useIsFeatureEnabled('IS_FREE_ACCESS_ENABLED');
const isCRMMigrationEnabled = useIsFeatureEnabled('IS_CRM_MIGRATION_ENABLED'); const isCRMMigrationEnabled = useIsFeatureEnabled('IS_CRM_MIGRATION_ENABLED');
const isServerlessFunctionSettingsEnabled = useIsFeatureEnabled( const isServerlessFunctionSettingsEnabled = useIsFeatureEnabled(
'IS_FUNCTION_SETTINGS_ENABLED', 'IS_FUNCTION_SETTINGS_ENABLED',
); );
const isBillingPageEnabled =
billing?.isBillingEnabled && !isFreeAccessEnabled;
return ( return (
<RouterProvider <RouterProvider
router={createRouter( router={createRouter(
billing?.isBillingEnabled, isBillingPageEnabled,
isCRMMigrationEnabled, isCRMMigrationEnabled,
isServerlessFunctionSettingsEnabled, isServerlessFunctionSettingsEnabled,
)} )}

View File

@ -215,6 +215,15 @@ export type ExchangeAuthCode = {
refreshToken: AuthToken; refreshToken: AuthToken;
}; };
export type ExecuteServerlessFunctionInput = {
/** Id of the serverless function to execute */
id: Scalars['UUID'];
/** Payload in JSON format */
payload?: InputMaybe<Scalars['JSON']>;
/** Version of the serverless function to execute */
version?: Scalars['String'];
};
export type FeatureFlag = { export type FeatureFlag = {
__typename?: 'FeatureFlag'; __typename?: 'FeatureFlag';
id: Scalars['UUID']; id: Scalars['UUID'];
@ -287,6 +296,13 @@ export type FullName = {
lastName: Scalars['String']; 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 = { export type InvalidatePassword = {
__typename?: 'InvalidatePassword'; __typename?: 'InvalidatePassword';
/** Boolean that confirms query was dispatched */ /** Boolean that confirms query was dispatched */
@ -343,8 +359,9 @@ export type Mutation = {
generateJWT: AuthTokens; generateJWT: AuthTokens;
generateTransientToken: TransientToken; generateTransientToken: TransientToken;
impersonate: Verify; impersonate: Verify;
publishServerlessFunction: ServerlessFunction;
renewToken: AuthTokens; renewToken: AuthTokens;
runWorkflowVersion: WorkflowTriggerResult; runWorkflowVersion: WorkflowRun;
sendInviteLink: SendInviteLink; sendInviteLink: SendInviteLink;
signUp: LoginToken; signUp: LoginToken;
skipSyncEmailOnboardingStep: OnboardingStepSuccess; skipSyncEmailOnboardingStep: OnboardingStepSuccess;
@ -431,8 +448,7 @@ export type MutationExchangeAuthorizationCodeArgs = {
export type MutationExecuteOneServerlessFunctionArgs = { export type MutationExecuteOneServerlessFunctionArgs = {
id: Scalars['UUID']; input: ExecuteServerlessFunctionInput;
payload?: InputMaybe<Scalars['JSON']>;
}; };
@ -452,6 +468,11 @@ export type MutationImpersonateArgs = {
}; };
export type MutationPublishServerlessFunctionArgs = {
input: PublishServerlessFunctionInput;
};
export type MutationRenewTokenArgs = { export type MutationRenewTokenArgs = {
appToken: Scalars['String']; appToken: Scalars['String'];
}; };
@ -594,6 +615,11 @@ export type ProductPricesEntity = {
totalNumberOfPrices: Scalars['Int']; totalNumberOfPrices: Scalars['Int'];
}; };
export type PublishServerlessFunctionInput = {
/** The id of the function. */
id: Scalars['ID'];
};
export type Query = { export type Query = {
__typename?: 'Query'; __typename?: 'Query';
billingPortalSession: SessionEntity; billingPortalSession: SessionEntity;
@ -606,6 +632,7 @@ export type Query = {
getAISQLQuery: AisqlQueryResult; getAISQLQuery: AisqlQueryResult;
getPostgresCredentials?: Maybe<PostgresCredentials>; getPostgresCredentials?: Maybe<PostgresCredentials>;
getProductPrices: ProductPricesEntity; getProductPrices: ProductPricesEntity;
getServerlessFunctionSourceCode: Scalars['String'];
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal;
getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal; getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal;
@ -649,6 +676,11 @@ export type QueryGetProductPricesArgs = {
}; };
export type QueryGetServerlessFunctionSourceCodeArgs = {
input: GetServerlessFunctionSourceCodeInput;
};
export type QueryGetTimelineCalendarEventsFromCompanyIdArgs = { export type QueryGetTimelineCalendarEventsFromCompanyIdArgs = {
companyId: Scalars['UUID']; companyId: Scalars['UUID'];
page: Scalars['Int']; page: Scalars['Int'];
@ -768,9 +800,9 @@ export type ServerlessFunction = {
createdAt: Scalars['DateTime']; createdAt: Scalars['DateTime'];
description?: Maybe<Scalars['String']>; description?: Maybe<Scalars['String']>;
id: Scalars['UUID']; id: Scalars['UUID'];
latestVersion?: Maybe<Scalars['String']>;
name: Scalars['String']; name: Scalars['String'];
runtime: Scalars['String']; runtime: Scalars['String'];
sourceCodeFullPath: Scalars['String'];
sourceCodeHash: Scalars['String']; sourceCodeHash: Scalars['String'];
syncStatus: ServerlessFunctionSyncStatus; syncStatus: ServerlessFunctionSyncStatus;
updatedAt: Scalars['DateTime']; updatedAt: Scalars['DateTime'];
@ -1054,10 +1086,9 @@ export type Verify = {
user: User; user: User;
}; };
export type WorkflowTriggerResult = { export type WorkflowRun = {
__typename?: 'WorkflowTriggerResult'; __typename?: 'WorkflowRun';
/** Execution result in JSON format */ workflowRunId: Scalars['UUID'];
result?: Maybe<Scalars['JSON']>;
}; };
export type Workspace = { export type Workspace = {

View File

@ -33,7 +33,10 @@ export const SettingsNavigationDrawerItems = () => {
const isFunctionSettingsEnabled = useIsFeatureEnabled( const isFunctionSettingsEnabled = useIsFeatureEnabled(
'IS_FUNCTION_SETTINGS_ENABLED', 'IS_FUNCTION_SETTINGS_ENABLED',
); );
const isFreeAccessEnabled = useIsFeatureEnabled('IS_FREE_ACCESS_ENABLED');
const isCRMMigrationEnabled = useIsFeatureEnabled('IS_CRM_MIGRATION_ENABLED'); const isCRMMigrationEnabled = useIsFeatureEnabled('IS_CRM_MIGRATION_ENABLED');
const isBillingPageEnabled =
billing?.isBillingEnabled && !isFreeAccessEnabled;
return ( return (
<> <>
@ -84,7 +87,7 @@ export const SettingsNavigationDrawerItems = () => {
path={SettingsPath.WorkspaceMembersPage} path={SettingsPath.WorkspaceMembersPage}
Icon={IconUsers} Icon={IconUsers}
/> />
{billing?.isBillingEnabled && ( {isBillingPageEnabled && (
<SettingsNavigationDrawerItem <SettingsNavigationDrawerItem
label="Billing" label="Billing"
path={SettingsPath.Billing} path={SettingsPath.Billing}

View File

@ -24,7 +24,7 @@ describe('useServerlessFunctionUpdateFormState', () => {
); );
useGetOneServerlessFunctionMock.useGetOneServerlessFunction.mockReturnValue( useGetOneServerlessFunctionMock.useGetOneServerlessFunction.mockReturnValue(
{ {
serverlessFunction: { sourceCodeFullPath: undefined }, serverlessFunction: { name: 'name' },
}, },
); );
const useGetOneServerlessFunctionSourceCodeMock = jest.requireMock( const useGetOneServerlessFunctionSourceCodeMock = jest.requireMock(

View File

@ -7,4 +7,5 @@ export type FeatureFlagKey =
| 'IS_FUNCTION_SETTINGS_ENABLED' | 'IS_FUNCTION_SETTINGS_ENABLED'
| 'IS_COPILOT_ENABLED' | 'IS_COPILOT_ENABLED'
| 'IS_CRM_MIGRATION_ENABLED' | 'IS_CRM_MIGRATION_ENABLED'
| 'IS_FREE_ACCESS_ENABLED'
| 'IS_MESSAGE_THREAD_SUBSCRIBER_ENABLED'; | 'IS_MESSAGE_THREAD_SUBSCRIBER_ENABLED';

View File

@ -38,7 +38,6 @@ const meta: Meta<PageDecoratorArgs> = {
description: '', description: '',
syncStatus: 'READY', syncStatus: 'READY',
runtime: 'nodejs18.x', runtime: 'nodejs18.x',
sourceCodeFullPath: SOURCE_CODE_FULL_PATH,
sourceCodeHash: '42d2734b3dc8a7b45a16803ed7f417bc', sourceCodeHash: '42d2734b3dc8a7b45a16803ed7f417bc',
updatedAt: '2024-02-24T10:23:10.673Z', updatedAt: '2024-02-24T10:23:10.673Z',
createdAt: '2024-02-24T10:23:10.673Z', createdAt: '2024-02-24T10:23:10.673Z',

View File

@ -43,7 +43,7 @@ export class BillingResolver {
@Args() { returnUrlPath }: BillingSessionInput, @Args() { returnUrlPath }: BillingSessionInput,
) { ) {
return { return {
url: await this.billingPortalWorkspaceService.computeBillingPortalSessionURL( url: await this.billingPortalWorkspaceService.computeBillingPortalSessionURLOrThrow(
user.defaultWorkspaceId, user.defaultWorkspaceId,
returnUrlPath, returnUrlPath,
), ),

View File

@ -65,18 +65,22 @@ export class BillingPortalWorkspaceService {
return session.url; return session.url;
} }
async computeBillingPortalSessionURL( async computeBillingPortalSessionURLOrThrow(
workspaceId: string, workspaceId: string,
returnUrlPath?: string, returnUrlPath?: string,
) { ) {
const currentSubscriptionItem = const currentSubscription =
await this.billingSubscriptionService.getCurrentBillingSubscriptionOrThrow( await this.billingSubscriptionService.getCurrentBillingSubscriptionOrThrow(
{ {
workspaceId, workspaceId,
}, },
); );
const stripeCustomerId = currentSubscriptionItem.stripeCustomerId; if (!currentSubscription) {
throw new Error('Error: missing subscription');
}
const stripeCustomerId = currentSubscription.stripeCustomerId;
if (!stripeCustomerId) { if (!stripeCustomerId) {
throw new Error('Error: missing stripeCustomerId'); throw new Error('Error: missing stripeCustomerId');