diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index 1666109fad..e6c91c2cc6 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -1266,6 +1266,37 @@ type Analytics { success: Boolean! } +type ApprovedAccessDomain { + id: UUID! + domain: String! + isValidated: Boolean! + createdAt: DateTime! +} + +type FileWithSignedUrl { + id: UUID! + path: String! + size: Float! + createdAt: DateTime! + url: String! +} + +type EnterpriseLicenseInfoDTO { + isValid: Boolean! + licensee: String + expiresAt: DateTime + subscriptionId: String +} + +type EnterpriseSubscriptionStatusDTO { + status: String! + licensee: String + expiresAt: DateTime + cancelAt: DateTime + currentPeriodEnd: DateTime + isCancellationScheduled: Boolean! +} + type BillingSubscriptionSchedulePhaseItem { price: String! quantity: Float @@ -1419,42 +1450,11 @@ type BillingUpdate { billingSubscriptions: [BillingSubscription!]! } -type EnterpriseLicenseInfoDTO { - isValid: Boolean! - licensee: String - expiresAt: DateTime - subscriptionId: String -} - -type EnterpriseSubscriptionStatusDTO { - status: String! - licensee: String - expiresAt: DateTime - cancelAt: DateTime - currentPeriodEnd: DateTime - isCancellationScheduled: Boolean! -} - type OnboardingStepSuccess { """Boolean that confirms query was dispatched""" success: Boolean! } -type ApprovedAccessDomain { - id: UUID! - domain: String! - isValidated: Boolean! - createdAt: DateTime! -} - -type FileWithSignedUrl { - id: UUID! - path: String! - size: Float! - createdAt: DateTime! - url: String! -} - type WorkspaceInvitation { id: UUID! email: String! diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index ae9eb191ac..9d1e5527cc 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -989,6 +989,41 @@ export interface Analytics { __typename: 'Analytics' } +export interface ApprovedAccessDomain { + id: Scalars['UUID'] + domain: Scalars['String'] + isValidated: Scalars['Boolean'] + createdAt: Scalars['DateTime'] + __typename: 'ApprovedAccessDomain' +} + +export interface FileWithSignedUrl { + id: Scalars['UUID'] + path: Scalars['String'] + size: Scalars['Float'] + createdAt: Scalars['DateTime'] + url: Scalars['String'] + __typename: 'FileWithSignedUrl' +} + +export interface EnterpriseLicenseInfoDTO { + isValid: Scalars['Boolean'] + licensee?: Scalars['String'] + expiresAt?: Scalars['DateTime'] + subscriptionId?: Scalars['String'] + __typename: 'EnterpriseLicenseInfoDTO' +} + +export interface EnterpriseSubscriptionStatusDTO { + status: Scalars['String'] + licensee?: Scalars['String'] + expiresAt?: Scalars['DateTime'] + cancelAt?: Scalars['DateTime'] + currentPeriodEnd?: Scalars['DateTime'] + isCancellationScheduled: Scalars['Boolean'] + __typename: 'EnterpriseSubscriptionStatusDTO' +} + export interface BillingSubscriptionSchedulePhaseItem { price: Scalars['String'] quantity?: Scalars['Float'] @@ -1134,47 +1169,12 @@ export interface BillingUpdate { __typename: 'BillingUpdate' } -export interface EnterpriseLicenseInfoDTO { - isValid: Scalars['Boolean'] - licensee?: Scalars['String'] - expiresAt?: Scalars['DateTime'] - subscriptionId?: Scalars['String'] - __typename: 'EnterpriseLicenseInfoDTO' -} - -export interface EnterpriseSubscriptionStatusDTO { - status: Scalars['String'] - licensee?: Scalars['String'] - expiresAt?: Scalars['DateTime'] - cancelAt?: Scalars['DateTime'] - currentPeriodEnd?: Scalars['DateTime'] - isCancellationScheduled: Scalars['Boolean'] - __typename: 'EnterpriseSubscriptionStatusDTO' -} - export interface OnboardingStepSuccess { /** Boolean that confirms query was dispatched */ success: Scalars['Boolean'] __typename: 'OnboardingStepSuccess' } -export interface ApprovedAccessDomain { - id: Scalars['UUID'] - domain: Scalars['String'] - isValidated: Scalars['Boolean'] - createdAt: Scalars['DateTime'] - __typename: 'ApprovedAccessDomain' -} - -export interface FileWithSignedUrl { - id: Scalars['UUID'] - path: Scalars['String'] - size: Scalars['Float'] - createdAt: Scalars['DateTime'] - url: Scalars['String'] - __typename: 'FileWithSignedUrl' -} - export interface WorkspaceInvitation { id: Scalars['UUID'] email: Scalars['String'] @@ -4239,6 +4239,45 @@ export interface AnalyticsGenqlSelection{ __scalar?: boolean | number } +export interface ApprovedAccessDomainGenqlSelection{ + id?: boolean | number + domain?: boolean | number + isValidated?: boolean | number + createdAt?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface FileWithSignedUrlGenqlSelection{ + id?: boolean | number + path?: boolean | number + size?: boolean | number + createdAt?: boolean | number + url?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface EnterpriseLicenseInfoDTOGenqlSelection{ + isValid?: boolean | number + licensee?: boolean | number + expiresAt?: boolean | number + subscriptionId?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface EnterpriseSubscriptionStatusDTOGenqlSelection{ + status?: boolean | number + licensee?: boolean | number + expiresAt?: boolean | number + cancelAt?: boolean | number + currentPeriodEnd?: boolean | number + isCancellationScheduled?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + export interface BillingSubscriptionSchedulePhaseItemGenqlSelection{ price?: boolean | number quantity?: boolean | number @@ -4386,26 +4425,6 @@ export interface BillingUpdateGenqlSelection{ __scalar?: boolean | number } -export interface EnterpriseLicenseInfoDTOGenqlSelection{ - isValid?: boolean | number - licensee?: boolean | number - expiresAt?: boolean | number - subscriptionId?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface EnterpriseSubscriptionStatusDTOGenqlSelection{ - status?: boolean | number - licensee?: boolean | number - expiresAt?: boolean | number - cancelAt?: boolean | number - currentPeriodEnd?: boolean | number - isCancellationScheduled?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - export interface OnboardingStepSuccessGenqlSelection{ /** Boolean that confirms query was dispatched */ success?: boolean | number @@ -4413,25 +4432,6 @@ export interface OnboardingStepSuccessGenqlSelection{ __scalar?: boolean | number } -export interface ApprovedAccessDomainGenqlSelection{ - id?: boolean | number - domain?: boolean | number - isValidated?: boolean | number - createdAt?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface FileWithSignedUrlGenqlSelection{ - id?: boolean | number - path?: boolean | number - size?: boolean | number - createdAt?: boolean | number - url?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - export interface WorkspaceInvitationGenqlSelection{ id?: boolean | number email?: boolean | number @@ -7493,6 +7493,38 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null + const ApprovedAccessDomain_possibleTypes: string[] = ['ApprovedAccessDomain'] + export const isApprovedAccessDomain = (obj?: { __typename?: any } | null): obj is ApprovedAccessDomain => { + if (!obj?.__typename) throw new Error('__typename is missing in "isApprovedAccessDomain"') + return ApprovedAccessDomain_possibleTypes.includes(obj.__typename) + } + + + + const FileWithSignedUrl_possibleTypes: string[] = ['FileWithSignedUrl'] + export const isFileWithSignedUrl = (obj?: { __typename?: any } | null): obj is FileWithSignedUrl => { + if (!obj?.__typename) throw new Error('__typename is missing in "isFileWithSignedUrl"') + return FileWithSignedUrl_possibleTypes.includes(obj.__typename) + } + + + + const EnterpriseLicenseInfoDTO_possibleTypes: string[] = ['EnterpriseLicenseInfoDTO'] + export const isEnterpriseLicenseInfoDTO = (obj?: { __typename?: any } | null): obj is EnterpriseLicenseInfoDTO => { + if (!obj?.__typename) throw new Error('__typename is missing in "isEnterpriseLicenseInfoDTO"') + return EnterpriseLicenseInfoDTO_possibleTypes.includes(obj.__typename) + } + + + + const EnterpriseSubscriptionStatusDTO_possibleTypes: string[] = ['EnterpriseSubscriptionStatusDTO'] + export const isEnterpriseSubscriptionStatusDTO = (obj?: { __typename?: any } | null): obj is EnterpriseSubscriptionStatusDTO => { + if (!obj?.__typename) throw new Error('__typename is missing in "isEnterpriseSubscriptionStatusDTO"') + return EnterpriseSubscriptionStatusDTO_possibleTypes.includes(obj.__typename) + } + + + const BillingSubscriptionSchedulePhaseItem_possibleTypes: string[] = ['BillingSubscriptionSchedulePhaseItem'] export const isBillingSubscriptionSchedulePhaseItem = (obj?: { __typename?: any } | null): obj is BillingSubscriptionSchedulePhaseItem => { if (!obj?.__typename) throw new Error('__typename is missing in "isBillingSubscriptionSchedulePhaseItem"') @@ -7621,22 +7653,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null - const EnterpriseLicenseInfoDTO_possibleTypes: string[] = ['EnterpriseLicenseInfoDTO'] - export const isEnterpriseLicenseInfoDTO = (obj?: { __typename?: any } | null): obj is EnterpriseLicenseInfoDTO => { - if (!obj?.__typename) throw new Error('__typename is missing in "isEnterpriseLicenseInfoDTO"') - return EnterpriseLicenseInfoDTO_possibleTypes.includes(obj.__typename) - } - - - - const EnterpriseSubscriptionStatusDTO_possibleTypes: string[] = ['EnterpriseSubscriptionStatusDTO'] - export const isEnterpriseSubscriptionStatusDTO = (obj?: { __typename?: any } | null): obj is EnterpriseSubscriptionStatusDTO => { - if (!obj?.__typename) throw new Error('__typename is missing in "isEnterpriseSubscriptionStatusDTO"') - return EnterpriseSubscriptionStatusDTO_possibleTypes.includes(obj.__typename) - } - - - const OnboardingStepSuccess_possibleTypes: string[] = ['OnboardingStepSuccess'] export const isOnboardingStepSuccess = (obj?: { __typename?: any } | null): obj is OnboardingStepSuccess => { if (!obj?.__typename) throw new Error('__typename is missing in "isOnboardingStepSuccess"') @@ -7645,22 +7661,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null - const ApprovedAccessDomain_possibleTypes: string[] = ['ApprovedAccessDomain'] - export const isApprovedAccessDomain = (obj?: { __typename?: any } | null): obj is ApprovedAccessDomain => { - if (!obj?.__typename) throw new Error('__typename is missing in "isApprovedAccessDomain"') - return ApprovedAccessDomain_possibleTypes.includes(obj.__typename) - } - - - - const FileWithSignedUrl_possibleTypes: string[] = ['FileWithSignedUrl'] - export const isFileWithSignedUrl = (obj?: { __typename?: any } | null): obj is FileWithSignedUrl => { - if (!obj?.__typename) throw new Error('__typename is missing in "isFileWithSignedUrl"') - return FileWithSignedUrl_possibleTypes.includes(obj.__typename) - } - - - const WorkspaceInvitation_possibleTypes: string[] = ['WorkspaceInvitation'] export const isWorkspaceInvitation = (obj?: { __typename?: any } | null): obj is WorkspaceInvitation => { if (!obj?.__typename) throw new Error('__typename is missing in "isWorkspaceInvitation"') diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts index e564c1808f..b95194143c 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/types.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts @@ -39,11 +39,11 @@ export default { 95, 101, 115, - 120, - 121, - 122, 124, - 132, + 125, + 126, + 128, + 136, 147, 150, 152, @@ -105,13 +105,13 @@ export default { 1 ], "metadata": [ - 119 + 123 ], "on_BillingLicensedProduct": [ - 128 + 132 ], "on_BillingMeteredProduct": [ - 129 + 133 ], "__typename": [ 1 @@ -1700,10 +1700,10 @@ export default { 157 ], "billingSubscriptions": [ - 131 + 135 ], "currentBillingSubscription": [ - 131 + 135 ], "billingEntitlements": [ 253 @@ -2621,259 +2621,36 @@ export default { 1 ] }, - "BillingSubscriptionSchedulePhaseItem": { - "price": [ - 1 - ], - "quantity": [ - 11 - ], - "__typename": [ - 1 - ] - }, - "BillingSubscriptionSchedulePhase": { - "start_date": [ - 11 - ], - "end_date": [ - 11 - ], - "items": [ - 117 - ], - "__typename": [ - 1 - ] - }, - "BillingProductMetadata": { - "planKey": [ - 120 - ], - "priceUsageBased": [ - 121 - ], - "productKey": [ - 122 - ], - "__typename": [ - 1 - ] - }, - "BillingPlanKey": {}, - "BillingUsageType": {}, - "BillingProductKey": {}, - "BillingPriceLicensed": { - "recurringInterval": [ - 124 - ], - "unitAmount": [ - 11 - ], - "stripePriceId": [ - 1 - ], - "priceUsageType": [ - 121 - ], - "__typename": [ - 1 - ] - }, - "SubscriptionInterval": {}, - "BillingPriceTier": { - "upTo": [ - 11 - ], - "flatAmount": [ - 11 - ], - "unitAmount": [ - 11 - ], - "__typename": [ - 1 - ] - }, - "BillingPriceMetered": { - "tiers": [ - 125 - ], - "recurringInterval": [ - 124 - ], - "stripePriceId": [ - 1 - ], - "priceUsageType": [ - 121 - ], - "__typename": [ - 1 - ] - }, - "BillingProduct": { - "name": [ - 1 - ], - "description": [ - 1 - ], - "images": [ - 1 - ], - "metadata": [ - 119 - ], - "__typename": [ - 1 - ] - }, - "BillingLicensedProduct": { - "name": [ - 1 - ], - "description": [ - 1 - ], - "images": [ - 1 - ], - "metadata": [ - 119 - ], - "prices": [ - 123 - ], - "__typename": [ - 1 - ] - }, - "BillingMeteredProduct": { - "name": [ - 1 - ], - "description": [ - 1 - ], - "images": [ - 1 - ], - "metadata": [ - 119 - ], - "prices": [ - 126 - ], - "__typename": [ - 1 - ] - }, - "BillingSubscriptionItem": { + "ApprovedAccessDomain": { "id": [ 3 ], - "hasReachedCurrentPeriodCap": [ - 6 - ], - "quantity": [ - 11 - ], - "stripePriceId": [ + "domain": [ 1 ], - "billingProduct": [ - 0 + "isValidated": [ + 6 + ], + "createdAt": [ + 4 ], "__typename": [ 1 ] }, - "BillingSubscription": { + "FileWithSignedUrl": { "id": [ 3 ], - "status": [ - 132 + "path": [ + 1 ], - "interval": [ - 124 + "size": [ + 11 ], - "billingSubscriptionItems": [ - 130 - ], - "currentPeriodEnd": [ + "createdAt": [ 4 ], - "metadata": [ - 15 - ], - "phases": [ - 118 - ], - "__typename": [ - 1 - ] - }, - "SubscriptionStatus": {}, - "BillingEndTrialPeriod": { - "status": [ - 132 - ], - "hasPaymentMethod": [ - 6 - ], - "billingPortalUrl": [ - 1 - ], - "__typename": [ - 1 - ] - }, - "BillingMeteredProductUsage": { - "productKey": [ - 122 - ], - "periodStart": [ - 4 - ], - "periodEnd": [ - 4 - ], - "usedCredits": [ - 11 - ], - "grantedCredits": [ - 11 - ], - "rolloverCredits": [ - 11 - ], - "totalGrantedCredits": [ - 11 - ], - "unitPriceCents": [ - 11 - ], - "__typename": [ - 1 - ] - }, - "BillingPlan": { - "planKey": [ - 120 - ], - "licensedProducts": [ - 128 - ], - "meteredProducts": [ - 129 - ], - "__typename": [ - 1 - ] - }, - "BillingSession": { "url": [ 1 ], @@ -2881,17 +2658,6 @@ export default { 1 ] }, - "BillingUpdate": { - "currentBillingSubscription": [ - 131 - ], - "billingSubscriptions": [ - 131 - ], - "__typename": [ - 1 - ] - }, "EnterpriseLicenseInfoDTO": { "isValid": [ 6 @@ -2932,6 +2698,277 @@ export default { 1 ] }, + "BillingSubscriptionSchedulePhaseItem": { + "price": [ + 1 + ], + "quantity": [ + 11 + ], + "__typename": [ + 1 + ] + }, + "BillingSubscriptionSchedulePhase": { + "start_date": [ + 11 + ], + "end_date": [ + 11 + ], + "items": [ + 121 + ], + "__typename": [ + 1 + ] + }, + "BillingProductMetadata": { + "planKey": [ + 124 + ], + "priceUsageBased": [ + 125 + ], + "productKey": [ + 126 + ], + "__typename": [ + 1 + ] + }, + "BillingPlanKey": {}, + "BillingUsageType": {}, + "BillingProductKey": {}, + "BillingPriceLicensed": { + "recurringInterval": [ + 128 + ], + "unitAmount": [ + 11 + ], + "stripePriceId": [ + 1 + ], + "priceUsageType": [ + 125 + ], + "__typename": [ + 1 + ] + }, + "SubscriptionInterval": {}, + "BillingPriceTier": { + "upTo": [ + 11 + ], + "flatAmount": [ + 11 + ], + "unitAmount": [ + 11 + ], + "__typename": [ + 1 + ] + }, + "BillingPriceMetered": { + "tiers": [ + 129 + ], + "recurringInterval": [ + 128 + ], + "stripePriceId": [ + 1 + ], + "priceUsageType": [ + 125 + ], + "__typename": [ + 1 + ] + }, + "BillingProduct": { + "name": [ + 1 + ], + "description": [ + 1 + ], + "images": [ + 1 + ], + "metadata": [ + 123 + ], + "__typename": [ + 1 + ] + }, + "BillingLicensedProduct": { + "name": [ + 1 + ], + "description": [ + 1 + ], + "images": [ + 1 + ], + "metadata": [ + 123 + ], + "prices": [ + 127 + ], + "__typename": [ + 1 + ] + }, + "BillingMeteredProduct": { + "name": [ + 1 + ], + "description": [ + 1 + ], + "images": [ + 1 + ], + "metadata": [ + 123 + ], + "prices": [ + 130 + ], + "__typename": [ + 1 + ] + }, + "BillingSubscriptionItem": { + "id": [ + 3 + ], + "hasReachedCurrentPeriodCap": [ + 6 + ], + "quantity": [ + 11 + ], + "stripePriceId": [ + 1 + ], + "billingProduct": [ + 0 + ], + "__typename": [ + 1 + ] + }, + "BillingSubscription": { + "id": [ + 3 + ], + "status": [ + 136 + ], + "interval": [ + 128 + ], + "billingSubscriptionItems": [ + 134 + ], + "currentPeriodEnd": [ + 4 + ], + "metadata": [ + 15 + ], + "phases": [ + 122 + ], + "__typename": [ + 1 + ] + }, + "SubscriptionStatus": {}, + "BillingEndTrialPeriod": { + "status": [ + 136 + ], + "hasPaymentMethod": [ + 6 + ], + "billingPortalUrl": [ + 1 + ], + "__typename": [ + 1 + ] + }, + "BillingMeteredProductUsage": { + "productKey": [ + 126 + ], + "periodStart": [ + 4 + ], + "periodEnd": [ + 4 + ], + "usedCredits": [ + 11 + ], + "grantedCredits": [ + 11 + ], + "rolloverCredits": [ + 11 + ], + "totalGrantedCredits": [ + 11 + ], + "unitPriceCents": [ + 11 + ], + "__typename": [ + 1 + ] + }, + "BillingPlan": { + "planKey": [ + 124 + ], + "licensedProducts": [ + 132 + ], + "meteredProducts": [ + 133 + ], + "__typename": [ + 1 + ] + }, + "BillingSession": { + "url": [ + 1 + ], + "__typename": [ + 1 + ] + }, + "BillingUpdate": { + "currentBillingSubscription": [ + 135 + ], + "billingSubscriptions": [ + 135 + ], + "__typename": [ + 1 + ] + }, "OnboardingStepSuccess": { "success": [ 6 @@ -2940,43 +2977,6 @@ export default { 1 ] }, - "ApprovedAccessDomain": { - "id": [ - 3 - ], - "domain": [ - 1 - ], - "isValidated": [ - 6 - ], - "createdAt": [ - 4 - ], - "__typename": [ - 1 - ] - }, - "FileWithSignedUrl": { - "id": [ - 3 - ], - "path": [ - 1 - ], - "size": [ - 11 - ], - "createdAt": [ - 4 - ], - "url": [ - 1 - ], - "__typename": [ - 1 - ] - }, "WorkspaceInvitation": { "id": [ 3 @@ -6481,10 +6481,10 @@ export default { } ], "enterpriseSubscriptionStatus": [ - 139 + 120 ], "billingPortalSession": [ - 136, + 140, { "returnUrlPath": [ 1 @@ -6492,16 +6492,16 @@ export default { } ], "listPlans": [ - 135 + 139 ], "getMeteredProductsUsage": [ - 134 + 138 ], "findWorkspaceInvitations": [ 143 ], "getApprovedAccessDomains": [ - 141 + 117 ], "getPageLayoutTabs": [ 113, @@ -7533,7 +7533,7 @@ export default { } ], "uploadEmailAttachmentFile": [ - 142, + 118, { "file": [ 388, @@ -7542,7 +7542,7 @@ export default { } ], "uploadAIChatFile": [ - 142, + 118, { "file": [ 388, @@ -7551,7 +7551,7 @@ export default { } ], "uploadWorkflowFile": [ - 142, + 118, { "file": [ 388, @@ -7560,7 +7560,7 @@ export default { } ], "uploadWorkspaceLogo": [ - 142, + 118, { "file": [ 388, @@ -7569,7 +7569,7 @@ export default { } ], "uploadWorkspaceMemberProfilePicture": [ - 142, + 118, { "file": [ 388, @@ -7578,7 +7578,7 @@ export default { } ], "uploadFilesFieldFile": [ - 142, + 118, { "file": [ 388, @@ -7591,7 +7591,7 @@ export default { } ], "uploadFilesFieldFileByUniversalIdentifier": [ - 142, + 118, { "file": [ 388, @@ -7936,7 +7936,7 @@ export default { 6 ], "setEnterpriseKey": [ - 138, + 119, { "enterpriseKey": [ 1, @@ -7945,20 +7945,20 @@ export default { } ], "skipSyncEmailOnboardingStep": [ - 140 + 142 ], "skipBookOnboardingStep": [ - 140 + 142 ], "checkoutSession": [ - 136, + 140, { "recurringInterval": [ - 124, + 128, "SubscriptionInterval!" ], "plan": [ - 120, + 124, "BillingPlanKey!" ], "requirePaymentMethod": [ @@ -7971,19 +7971,19 @@ export default { } ], "switchSubscriptionInterval": [ - 137 + 141 ], "switchBillingPlan": [ - 137 + 141 ], "cancelSwitchBillingPlan": [ - 137 + 141 ], "cancelSwitchBillingInterval": [ - 137 + 141 ], "setMeteredSubscriptionPrice": [ - 137, + 141, { "priceId": [ 1, @@ -7992,10 +7992,10 @@ export default { } ], "endSubscriptionTrialPeriod": [ - 133 + 137 ], "cancelSwitchMeteredPrice": [ - 137 + 141 ], "deleteWorkspaceInvitation": [ 1, @@ -8028,7 +8028,7 @@ export default { } ], "createApprovedAccessDomain": [ - 141, + 117, { "input": [ 420, @@ -8046,7 +8046,7 @@ export default { } ], "validateApprovedAccessDomain": [ - 141, + 117, { "input": [ 422, diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 42f28f1953..e926ee5b3b 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -212,6 +212,35 @@ export class OnboardingService { ); } + async completeOnboardingProfileStepIfNameProvided({ + userId, + workspaceId, + firstName, + lastName, + }: { + userId?: string; + workspaceId: string; + firstName?: string; + lastName?: string; + }) { + if (!isDefined(userId)) { + return; + } + + const hasProvidedNamePart = + (isDefined(firstName) && firstName !== '') || + (isDefined(lastName) && lastName !== ''); + if (!hasProvidedNamePart) { + return; + } + + await this.setOnboardingCreateProfilePending({ + userId, + workspaceId, + value: false, + }); + } + async setOnboardingBookOnboardingPending({ workspaceId, value, diff --git a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts index 7a3d4c775b..6739394079 100644 --- a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts @@ -522,13 +522,20 @@ export class UserResolver { workspaceMemberRepository.save(workspaceMemberUpdatePayload), ); - if (isDefined(input.update.locale)) { - const targetUserWorkspace = - await this.userWorkspaceService.getUserWorkspaceForUserOrThrow({ - userId: workspaceMember.userId, - workspaceId: workspace.id, - }); + await this.onboardingService.completeOnboardingProfileStepIfNameProvided({ + userId: workspaceMember.userId, + workspaceId: workspace.id, + firstName: workspaceMemberUpdatePayload.name?.firstName, + lastName: workspaceMemberUpdatePayload.name?.lastName, + }); + const targetUserWorkspace = + await this.userWorkspaceService.getUserWorkspaceForUserOrThrow({ + userId: workspaceMember.userId, + workspaceId: workspace.id, + }); + + if (isDefined(input.update.locale)) { await this.userWorkspaceService.updateUserWorkspaceLocaleForUserWorkspace( { locale: input.update.locale as UserWorkspaceEntity['locale'], diff --git a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.post-query.hook.ts b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.post-query.hook.ts index d959b4e200..01a2272f54 100644 --- a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.post-query.hook.ts +++ b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.post-query.hook.ts @@ -7,8 +7,6 @@ import { type WorkspacePostQueryHookInstance } from 'src/engine/api/graphql/work import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator'; import { WorkspaceQueryHookType } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type'; -import { isApiKeyAuthContext } from 'src/engine/core-modules/auth/guards/is-api-key-auth-context.guard'; -import { isUserAuthContext } from 'src/engine/core-modules/auth/guards/is-user-auth-context.guard'; import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type'; import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; @@ -18,7 +16,6 @@ import { PermissionsExceptionCode, } from 'src/engine/metadata-modules/permissions/permissions.exception'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; -import { WorkspaceMemberPreQueryHookService } from 'src/modules/workspace-member/query-hooks/workspace-member-pre-query-hook.service'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @WorkspaceQueryHook({ @@ -32,7 +29,6 @@ export class WorkspaceMemberDeleteOnePostQueryHook private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager, @InjectRepository(UserWorkspaceEntity) private readonly userWorkspaceRepository: Repository, - private readonly workspaceMemberPreQueryHookService: WorkspaceMemberPreQueryHookService, private readonly userWorkspaceService: UserWorkspaceService, ) {} @@ -52,22 +48,6 @@ export class WorkspaceMemberDeleteOnePostQueryHook assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError); - await this.workspaceMemberPreQueryHookService.validateWorkspaceMemberUpdatePermissionOrThrow( - { - userWorkspaceId: isUserAuthContext(authContext) - ? authContext.userWorkspaceId - : undefined, - workspaceMemberId: isUserAuthContext(authContext) - ? authContext.workspaceMemberId - : undefined, - targettedWorkspaceMemberId, - workspaceId: workspace.id, - apiKey: isApiKeyAuthContext(authContext) - ? authContext.apiKey - : undefined, - }, - ); - const workspaceMember = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( async () => { diff --git a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-pre-query-hook.service.ts b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-pre-query-hook.service.ts deleted file mode 100644 index ea84a7bc69..0000000000 --- a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-pre-query-hook.service.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -import { PermissionFlagType } from 'twenty-shared/constants'; -import { isDefined } from 'twenty-shared/utils'; - -import { type FlatApiKey } from 'src/engine/core-modules/api-key/types/flat-api-key.type'; -import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; -import { - PermissionsException, - PermissionsExceptionCode, - PermissionsExceptionMessage, -} from 'src/engine/metadata-modules/permissions/permissions.exception'; -import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; - -@Injectable() -export class WorkspaceMemberPreQueryHookService { - constructor( - private readonly permissionsService: PermissionsService, - private readonly onboardingService: OnboardingService, - ) {} - - async validateWorkspaceMemberUpdatePermissionOrThrow({ - userWorkspaceId, - workspaceMemberId, - targettedWorkspaceMemberId, - workspaceId, - apiKey, - }: { - userWorkspaceId?: string; - workspaceMemberId?: string; - targettedWorkspaceMemberId?: string; - workspaceId: string; - apiKey?: FlatApiKey | null; - }) { - if (isDefined(apiKey)) { - return; - } - - if (!userWorkspaceId) { - throw new PermissionsException( - PermissionsExceptionMessage.USER_WORKSPACE_NOT_FOUND, - PermissionsExceptionCode.USER_WORKSPACE_NOT_FOUND, - ); - } - - if ( - isDefined(targettedWorkspaceMemberId) && - workspaceMemberId === targettedWorkspaceMemberId - ) { - return; - } - - if ( - await this.permissionsService.userHasWorkspaceSettingPermission({ - userWorkspaceId, - workspaceId, - setting: PermissionFlagType.WORKSPACE_MEMBERS, - apiKeyId: apiKey ?? undefined, - }) - ) { - return; - } - - throw new PermissionsException( - PermissionsExceptionMessage.PERMISSION_DENIED, - PermissionsExceptionCode.PERMISSION_DENIED, - ); - } - - async completeOnboardingProfileStepIfNameProvided({ - userId, - workspaceId, - firstName, - lastName, - }: { - userId?: string; - workspaceId: string; - firstName?: string; - lastName?: string; - }) { - if (!userId) { - return; - } - - if (firstName === '' && lastName === '') { - return; - } - - if (!isDefined(firstName) && !isDefined(lastName)) { - return; - } - - await this.onboardingService.setOnboardingCreateProfilePending({ - userId, - workspaceId, - value: false, - }); - } -} diff --git a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-query-hook.module.ts b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-query-hook.module.ts index f223c0adf8..54a9fb139c 100644 --- a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-query-hook.module.ts +++ b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-query-hook.module.ts @@ -3,10 +3,8 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { CoreEntityCacheModule } from 'src/engine/core-entity-cache/core-entity-cache.module'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; -import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module'; import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module'; -import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; import { WorkspaceMemberCreateManyPreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-create-many.pre-query.hook'; import { WorkspaceMemberCreateOnePreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-create-one.pre-query.hook'; import { WorkspaceMemberDeleteManyPreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-delete-many.pre-query.hook'; @@ -14,15 +12,12 @@ import { WorkspaceMemberDeleteOnePostQueryHook } from 'src/modules/workspace-mem import { WorkspaceMemberDeleteOnePreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-delete-one.pre-query.hook'; import { WorkspaceMemberDestroyManyPreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-destroy-many.pre-query.hook'; import { WorkspaceMemberDestroyOnePreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-destroy-one.pre-query.hook'; -import { WorkspaceMemberPreQueryHookService } from 'src/modules/workspace-member/query-hooks/workspace-member-pre-query-hook.service'; import { WorkspaceMemberRestoreManyPreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-restore-many.pre-query.hook'; import { WorkspaceMemberRestoreOnePreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-restore-one.pre-query.hook'; import { WorkspaceMemberUpdateManyPreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-update-many.pre-query.hook'; -import { WorkspaceMemberUpdateOnePreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-update-one.pre-query.hook'; @Module({ providers: [ - WorkspaceMemberPreQueryHookService, WorkspaceMemberCreateOnePreQueryHook, WorkspaceMemberCreateManyPreQueryHook, WorkspaceMemberDeleteOnePreQueryHook, @@ -32,14 +27,11 @@ import { WorkspaceMemberUpdateOnePreQueryHook } from 'src/modules/workspace-memb WorkspaceMemberDestroyManyPreQueryHook, WorkspaceMemberRestoreOnePreQueryHook, WorkspaceMemberRestoreManyPreQueryHook, - WorkspaceMemberUpdateOnePreQueryHook, WorkspaceMemberUpdateManyPreQueryHook, ], imports: [ CoreEntityCacheModule, FeatureFlagModule, - OnboardingModule, - PermissionsModule, UserWorkspaceModule, TypeOrmModule.forFeature([UserWorkspaceEntity]), ], diff --git a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-update-one.pre-query.hook.ts b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-update-one.pre-query.hook.ts deleted file mode 100644 index c7f4857a50..0000000000 --- a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-update-one.pre-query.hook.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { assertIsDefinedOrThrow } from 'twenty-shared/utils'; - -import { type WorkspacePreQueryHookInstance } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/interfaces/workspace-query-hook.interface'; -import { type UpdateOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; - -import { WorkspaceQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator'; -import { isApiKeyAuthContext } from 'src/engine/core-modules/auth/guards/is-api-key-auth-context.guard'; -import { isUserAuthContext } from 'src/engine/core-modules/auth/guards/is-user-auth-context.guard'; -import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type'; -import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; -import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception'; -import { WorkspaceMemberPreQueryHookService } from 'src/modules/workspace-member/query-hooks/workspace-member-pre-query-hook.service'; - -@WorkspaceQueryHook(`workspaceMember.updateOne`) -export class WorkspaceMemberUpdateOnePreQueryHook - implements WorkspacePreQueryHookInstance -{ - constructor( - private readonly workspaceMemberPreQueryHookService: WorkspaceMemberPreQueryHookService, - private readonly userWorkspaceService: UserWorkspaceService, - ) {} - - async execute( - authContext: WorkspaceAuthContext, - _objectName: string, - payload: UpdateOneResolverArgs, - ): Promise { - const workspace = authContext.workspace; - - assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError); - - await this.workspaceMemberPreQueryHookService.validateWorkspaceMemberUpdatePermissionOrThrow( - { - userWorkspaceId: isUserAuthContext(authContext) - ? authContext.userWorkspaceId - : undefined, - workspaceMemberId: isUserAuthContext(authContext) - ? authContext.workspaceMemberId - : undefined, - targettedWorkspaceMemberId: payload.id, - workspaceId: workspace.id, - apiKey: isApiKeyAuthContext(authContext) - ? authContext.apiKey - : undefined, - }, - ); - - await this.workspaceMemberPreQueryHookService.completeOnboardingProfileStepIfNameProvided( - { - userId: isUserAuthContext(authContext) - ? authContext.user.id - : undefined, - workspaceId: workspace.id, - firstName: payload.data.name?.firstName, - lastName: payload.data.name?.lastName, - }, - ); - - return payload; - } -} diff --git a/packages/twenty-server/test/integration/graphql/suites/settings-permissions/update-workspace-member-settings-onboarding.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/settings-permissions/update-workspace-member-settings-onboarding.integration-spec.ts new file mode 100644 index 0000000000..33f6ef2fe6 --- /dev/null +++ b/packages/twenty-server/test/integration/graphql/suites/settings-permissions/update-workspace-member-settings-onboarding.integration-spec.ts @@ -0,0 +1,275 @@ +import { randomUUID } from 'crypto'; + +import gql from 'graphql-tag'; +import request from 'supertest'; +import { getAuthTokensFromLoginToken } from 'test/integration/graphql/utils/get-auth-tokens-from-login-token.util'; +import { deleteUser } from 'test/integration/graphql/utils/delete-user.util'; +import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util'; +import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; + +import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum'; +import { SEED_APPLE_WORKSPACE_ID } from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant'; + +const APPLE_WORKSPACE_INVITE_HASH = 'apple.dev-invite-hash'; + +describe('updateWorkspaceMemberSettings and profile onboarding', () => { + let newUserAccessToken: string | undefined; + let originalIsPublicInviteLinkEnabled: boolean; + + beforeAll(async () => { + const currentWorkspaceQuery = gql` + query CurrentWorkspacePublicInvite { + currentWorkspace { + isPublicInviteLinkEnabled + } + } + `; + + const response = await makeMetadataAPIRequest( + { + query: currentWorkspaceQuery, + variables: {}, + }, + APPLE_JANE_ADMIN_ACCESS_TOKEN, + ); + + expect(response.body.errors).toBeUndefined(); + expect(response.body.data.currentWorkspace).toBeDefined(); + + originalIsPublicInviteLinkEnabled = + response.body.data.currentWorkspace.isPublicInviteLinkEnabled; + }); + + afterAll(async () => { + const restoreMutation = gql` + mutation RestoreWorkspacePublicInvite($data: UpdateWorkspaceInput!) { + updateWorkspace(data: $data) { + id + isPublicInviteLinkEnabled + } + } + `; + + await makeMetadataAPIRequest( + { + query: restoreMutation, + variables: { + data: { + isPublicInviteLinkEnabled: originalIsPublicInviteLinkEnabled, + }, + }, + }, + APPLE_JANE_ADMIN_ACCESS_TOKEN, + ); + }); + + afterEach(async () => { + if (!newUserAccessToken) { + return; + } + + await deleteUser({ + accessToken: newUserAccessToken, + expectToFail: false, + }); + newUserAccessToken = undefined; + }); + + it('should clear PROFILE_CREATION onboarding after saving first and last name via updateWorkspaceMemberSettings', async () => { + const client = request(`http://localhost:${APP_PORT}`); + + const enablePublicInviteLinkMutation = { + query: ` + mutation UpdateWorkspace { + updateWorkspace(data: { isPublicInviteLinkEnabled: true }) { + id + isPublicInviteLinkEnabled + } + } + `, + }; + + await client + .post('/metadata') + .set('Authorization', `Bearer ${APPLE_JANE_ADMIN_ACCESS_TOKEN}`) + .send(enablePublicInviteLinkMutation) + .expect(200) + .expect((response) => { + expect(response.body.errors).toBeUndefined(); + expect(response.body.data.updateWorkspace.isPublicInviteLinkEnabled).toBe( + true, + ); + }); + + const uniqueEmail = `profile-onboarding-${randomUUID()}@example.com`; + const password = 'Password123!'; + + const signUpInWorkspaceMutation = gql` + mutation SignUpInWorkspace( + $email: String! + $password: String! + $workspaceInviteHash: String + $workspaceId: UUID + ) { + signUpInWorkspace( + email: $email + password: $password + workspaceInviteHash: $workspaceInviteHash + workspaceId: $workspaceId + ) { + loginToken { + token + } + workspace { + id + workspaceUrls { + subdomainUrl + } + } + } + } + `; + + const signUpResponse = await makeMetadataAPIRequest( + { + query: signUpInWorkspaceMutation, + variables: { + email: uniqueEmail, + password, + workspaceInviteHash: APPLE_WORKSPACE_INVITE_HASH, + workspaceId: SEED_APPLE_WORKSPACE_ID, + }, + }, + undefined, + ); + + expect(signUpResponse.status).toBe(200); + expect(signUpResponse.body.errors).toBeUndefined(); + + const signUpPayload = signUpResponse.body.data.signUpInWorkspace; + + expect(signUpPayload.loginToken.token).toBeDefined(); + + await testDataSource.query( + 'UPDATE core."user" SET "isEmailVerified" = true WHERE email = $1', + [uniqueEmail], + ); + + const origin = + signUpPayload.workspace.workspaceUrls?.subdomainUrl ?? + 'http://localhost:3001'; + + const { + data: { getAuthTokensFromLoginToken: authTokensData }, + } = await getAuthTokensFromLoginToken({ + loginToken: signUpPayload.loginToken.token, + origin, + expectToFail: false, + }); + + newUserAccessToken = + authTokensData.tokens.accessOrWorkspaceAgnosticToken.token; + + const currentUserWithOnboardingQuery = gql` + query CurrentUserWithOnboarding { + currentUser { + id + onboardingStatus + } + } + `; + + const beforeNameUpdateResponse = await makeMetadataAPIRequest( + { + query: currentUserWithOnboardingQuery, + variables: {}, + }, + newUserAccessToken, + ); + + expect(beforeNameUpdateResponse.status).toBe(200); + expect(beforeNameUpdateResponse.body.errors).toBeUndefined(); + expect(beforeNameUpdateResponse.body.data.currentUser.onboardingStatus).toBe( + OnboardingStatus.PROFILE_CREATION, + ); + + const workspaceMemberQuery = gql` + query WorkspaceMemberForProfileOnboarding( + $workspaceMemberFilter: WorkspaceMemberFilterInput! + ) { + workspaceMember(filter: $workspaceMemberFilter) { + id + } + } + `; + + const workspaceMemberResponse = await makeGraphqlAPIRequest( + { + query: workspaceMemberQuery, + variables: { + workspaceMemberFilter: { + userEmail: { + eq: uniqueEmail, + }, + }, + }, + }, + newUserAccessToken, + ); + + expect(workspaceMemberResponse.status).toBe(200); + expect(workspaceMemberResponse.body.errors).toBeUndefined(); + + const workspaceMemberId = + workspaceMemberResponse.body.data.workspaceMember?.id; + + expect(workspaceMemberId).toBeDefined(); + + const updateWorkspaceMemberSettingsMutation = gql` + mutation UpdateWorkspaceMemberSettings( + $input: UpdateWorkspaceMemberSettingsInput! + ) { + updateWorkspaceMemberSettings(input: $input) + } + `; + + const updateNameResponse = await makeMetadataAPIRequest( + { + query: updateWorkspaceMemberSettingsMutation, + variables: { + input: { + workspaceMemberId, + update: { + name: { + firstName: 'Onboarding', + lastName: 'Integration', + }, + }, + }, + }, + }, + newUserAccessToken, + ); + + expect(updateNameResponse.status).toBe(200); + expect(updateNameResponse.body.errors).toBeUndefined(); + expect(updateNameResponse.body.data.updateWorkspaceMemberSettings).toBe(true); + + const afterNameUpdateResponse = await makeMetadataAPIRequest( + { + query: currentUserWithOnboardingQuery, + variables: {}, + }, + newUserAccessToken, + ); + + expect(afterNameUpdateResponse.status).toBe(200); + expect(afterNameUpdateResponse.body.errors).toBeUndefined(); + expect( + afterNameUpdateResponse.body.data.currentUser.onboardingStatus, + ).not.toBe(OnboardingStatus.PROFILE_CREATION); + expect(afterNameUpdateResponse.body.data.currentUser.onboardingStatus).toBe( + OnboardingStatus.COMPLETED, + ); + }); +}); diff --git a/packages/twenty-server/test/integration/graphql/suites/settings-permissions/workspace-members.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/settings-permissions/workspace-members.integration-spec.ts index c6bf71fbc1..7d07330e72 100644 --- a/packages/twenty-server/test/integration/graphql/suites/settings-permissions/workspace-members.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/settings-permissions/workspace-members.integration-spec.ts @@ -123,7 +123,7 @@ describe('workspace members permissions', () => { }); }); - it('should deny updateOne on /graphql when member updates themself', async () => { + it('should deny /graphql updateOne on own record for standard field name.firstName when member lacks WORKSPACE_MEMBERS', async () => { const graphqlOperation = updateOneOperationFactory({ objectMetadataSingularName: 'workspaceMember', gqlFields: WORKSPACE_MEMBER_GQL_FIELDS, @@ -146,7 +146,31 @@ describe('workspace members permissions', () => { expect(response.body.errors[0].extensions.code).toBe(ErrorCode.FORBIDDEN); }); - it('should deny update on custom field for another workspace member on /graphql (member role)', async () => { + it('should deny /graphql updateOne on own record for a custom field when member lacks WORKSPACE_MEMBERS', async () => { + const graphqlOperation = updateOneOperationFactory({ + objectMetadataSingularName: 'workspaceMember', + gqlFields: ` + id + ${customFieldName} + `, + recordId: WORKSPACE_MEMBER_DATA_SEED_IDS.JONY, + data: { + [customFieldName]: 'self-custom-value', + }, + }); + + const response = + await makeGraphqlAPIRequestWithMemberRole(graphqlOperation); + + expect(response.body.data).toStrictEqual({ updateWorkspaceMember: null }); + expect(response.body.errors).toBeDefined(); + expect(response.body.errors[0].message).toBe( + PermissionsExceptionMessage.PERMISSION_DENIED, + ); + expect(response.body.errors[0].extensions.code).toBe(ErrorCode.FORBIDDEN); + }); + + it('should deny /graphql updateOne on another workspace member for a custom field when member lacks WORKSPACE_MEMBERS', async () => { const customFieldValue = 'Ile-de-france'; const graphqlOperation = updateOneOperationFactory({ objectMetadataSingularName: 'workspaceMember',