[permissions] Adapt FE to non-readable fields (#13440)
Fixes https://github.com/twentyhq/core-team-issues/issues/1261 and https://github.com/twentyhq/core-team-issues/issues/1153 In this PR we - remove fields user do not have read access on from queries (findMany, findDuplicates, returned fields from create or update etc.) - remove fields user do not have read access on from views This is behind a feature flag
This commit is contained in:
@@ -1803,6 +1803,7 @@ export type ObjectPermission = {
|
||||
canSoftDeleteObjectRecords?: Maybe<Scalars['Boolean']>;
|
||||
canUpdateObjectRecords?: Maybe<Scalars['Boolean']>;
|
||||
objectMetadataId: Scalars['String'];
|
||||
restrictedFields?: Maybe<Scalars['JSON']>;
|
||||
};
|
||||
|
||||
export type ObjectPermissionInput = {
|
||||
@@ -3657,7 +3658,7 @@ export type UpdateLabPublicFeatureFlagMutationVariables = Exact<{
|
||||
|
||||
export type UpdateLabPublicFeatureFlagMutation = { __typename?: 'Mutation', updateLabPublicFeatureFlag: { __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean } };
|
||||
|
||||
export type ObjectPermissionFragmentFragment = { __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null };
|
||||
export type ObjectPermissionFragmentFragment = { __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null };
|
||||
|
||||
export type PermissionFlagFragmentFragment = { __typename?: 'PermissionFlag', id: string, flag: PermissionFlagType, roleId: string };
|
||||
|
||||
@@ -3697,7 +3698,7 @@ export type UpsertObjectPermissionsMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UpsertObjectPermissionsMutation = { __typename?: 'Mutation', upsertObjectPermissions: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> };
|
||||
export type UpsertObjectPermissionsMutation = { __typename?: 'Mutation', upsertObjectPermissions: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> };
|
||||
|
||||
export type UpsertPermissionFlagsMutationVariables = Exact<{
|
||||
upsertPermissionFlagsInput: UpsertPermissionFlagsInput;
|
||||
@@ -3709,7 +3710,7 @@ export type UpsertPermissionFlagsMutation = { __typename?: 'Mutation', upsertPer
|
||||
export type GetRolesQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetRolesQuery = { __typename?: 'Query', getRoles: Array<{ __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }>, permissionFlags?: Array<{ __typename?: 'PermissionFlag', id: string, flag: PermissionFlagType, roleId: string }> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> | null }> };
|
||||
export type GetRolesQuery = { __typename?: 'Query', getRoles: Array<{ __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }>, permissionFlags?: Array<{ __typename?: 'PermissionFlag', id: string, flag: PermissionFlagType, roleId: string }> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null }> };
|
||||
|
||||
export type CreateApprovedAccessDomainMutationVariables = Exact<{
|
||||
input: CreateApprovedAccessDomainInput;
|
||||
@@ -3840,7 +3841,7 @@ export type VerifyTwoFactorAuthenticationMethodForAuthenticatedUserMutationVaria
|
||||
|
||||
export type VerifyTwoFactorAuthenticationMethodForAuthenticatedUserMutation = { __typename?: 'Mutation', verifyTwoFactorAuthenticationMethodForAuthenticatedUser: { __typename?: 'VerifyTwoFactorAuthenticationMethodOutput', success: boolean } };
|
||||
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: any, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array<PermissionFlagType> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: any, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: any, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, metadata: any }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: any } | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } };
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: any, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array<PermissionFlagType> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: any, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: any, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, metadata: any }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: any } | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } };
|
||||
|
||||
export type WorkspaceUrlsFragmentFragment = { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null };
|
||||
|
||||
@@ -3859,7 +3860,7 @@ export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProf
|
||||
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: any, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array<PermissionFlagType> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: any, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: any, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, metadata: any }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: any } | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } } };
|
||||
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: any, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array<PermissionFlagType> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: any, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItem', id: any, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, billingProduct?: { __typename?: 'BillingProduct', name: string, description: string, metadata: { __typename?: 'BillingProductMetadata', planKey: BillingPlanKey, priceUsageBased: BillingUsageType, productKey: BillingProductKey } } | null }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, metadata: any }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: any } | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } } };
|
||||
|
||||
export type ActivateWorkflowVersionMutationVariables = Exact<{
|
||||
workflowVersionId: Scalars['String'];
|
||||
@@ -4123,6 +4124,7 @@ export const ObjectPermissionFragmentFragmentDoc = gql`
|
||||
canUpdateObjectRecords
|
||||
canSoftDeleteObjectRecords
|
||||
canDestroyObjectRecords
|
||||
restrictedFields
|
||||
}
|
||||
`;
|
||||
export const WorkspaceUrlsFragmentFragmentDoc = gql`
|
||||
|
||||
@@ -1714,6 +1714,7 @@ export type ObjectPermission = {
|
||||
canSoftDeleteObjectRecords?: Maybe<Scalars['Boolean']>;
|
||||
canUpdateObjectRecords?: Maybe<Scalars['Boolean']>;
|
||||
objectMetadataId: Scalars['String'];
|
||||
restrictedFields?: Maybe<Scalars['JSON']>;
|
||||
};
|
||||
|
||||
export type ObjectPermissionInput = {
|
||||
|
||||
+38
-31
@@ -71,7 +71,8 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isPinned: true,
|
||||
Icon: IconPlus,
|
||||
shouldBeRegistered: ({ objectPermissions, isSoftDeleteFilterActive }) =>
|
||||
objectPermissions.canUpdateObjectRecords && !isSoftDeleteFilterActive,
|
||||
(objectPermissions.canUpdateObjectRecords && !isSoftDeleteFilterActive) ??
|
||||
false,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION],
|
||||
component: <CreateNewTableRecordNoSelectionRecordAction />,
|
||||
},
|
||||
@@ -233,11 +234,12 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isSoftDeleteFilterActive,
|
||||
objectPermissions,
|
||||
}) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord.isRemote &&
|
||||
!isSoftDeleteFilterActive &&
|
||||
objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isDefined(selectedRecord?.deletedAt),
|
||||
(isDefined(selectedRecord) &&
|
||||
!selectedRecord.isRemote &&
|
||||
!isSoftDeleteFilterActive &&
|
||||
objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isDefined(selectedRecord?.deletedAt)) ??
|
||||
false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
@@ -260,11 +262,12 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isSoftDeleteFilterActive,
|
||||
numberOfSelectedRecords,
|
||||
}) =>
|
||||
objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isRemote &&
|
||||
!isSoftDeleteFilterActive &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT,
|
||||
(objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isRemote &&
|
||||
!isSoftDeleteFilterActive &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT) ??
|
||||
false,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <DeleteMultipleRecordsAction />,
|
||||
},
|
||||
@@ -324,9 +327,10 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
accent: 'danger',
|
||||
isPinned: true,
|
||||
shouldBeRegistered: ({ selectedRecord, objectPermissions, isRemote }) =>
|
||||
objectPermissions.canDestroyObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(selectedRecord?.deletedAt),
|
||||
(objectPermissions.canDestroyObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(selectedRecord?.deletedAt)) ??
|
||||
false,
|
||||
availableOn: [
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
ActionViewType.SHOW_PAGE,
|
||||
@@ -373,12 +377,13 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isSoftDeleteFilterActive,
|
||||
numberOfSelectedRecords,
|
||||
}) =>
|
||||
objectPermissions.canDestroyObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(isSoftDeleteFilterActive) &&
|
||||
isSoftDeleteFilterActive &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT,
|
||||
(objectPermissions.canDestroyObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(isSoftDeleteFilterActive) &&
|
||||
isSoftDeleteFilterActive &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT) ??
|
||||
false,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <DestroyMultipleRecordsAction />,
|
||||
},
|
||||
@@ -399,11 +404,12 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isShowPage,
|
||||
isSoftDeleteFilterActive,
|
||||
}) =>
|
||||
!isRemote &&
|
||||
isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canSoftDeleteObjectRecords &&
|
||||
((isDefined(isShowPage) && isShowPage) ||
|
||||
(isDefined(isSoftDeleteFilterActive) && isSoftDeleteFilterActive)),
|
||||
(!isRemote &&
|
||||
isDefined(selectedRecord?.deletedAt) &&
|
||||
objectPermissions.canSoftDeleteObjectRecords &&
|
||||
((isDefined(isShowPage) && isShowPage) ||
|
||||
(isDefined(isSoftDeleteFilterActive) && isSoftDeleteFilterActive))) ??
|
||||
false,
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
@@ -426,12 +432,13 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
||||
isSoftDeleteFilterActive,
|
||||
numberOfSelectedRecords,
|
||||
}) =>
|
||||
objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(isSoftDeleteFilterActive) &&
|
||||
isSoftDeleteFilterActive &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT,
|
||||
(objectPermissions.canSoftDeleteObjectRecords &&
|
||||
!isRemote &&
|
||||
isDefined(isSoftDeleteFilterActive) &&
|
||||
isSoftDeleteFilterActive &&
|
||||
isDefined(numberOfSelectedRecords) &&
|
||||
numberOfSelectedRecords < BACKEND_BATCH_REQUEST_MAX_COUNT) ??
|
||||
false,
|
||||
availableOn: [ActionViewType.INDEX_PAGE_BULK_SELECTION],
|
||||
component: <RestoreMultipleRecordsAction />,
|
||||
},
|
||||
|
||||
+11
-10
@@ -81,16 +81,17 @@ export const useRelatedRecordActions = ({
|
||||
objectPermissions,
|
||||
getTargetObjectWritePermission,
|
||||
}) =>
|
||||
isDefined(selectedRecord) &&
|
||||
!selectedRecord.isRemote &&
|
||||
objectPermissions.canUpdateObjectRecords &&
|
||||
getTargetObjectWritePermission(
|
||||
targetObjectNameSingular === CoreObjectNameSingular.TaskTarget
|
||||
? CoreObjectNameSingular.Task
|
||||
: targetObjectNameSingular === CoreObjectNameSingular.NoteTarget
|
||||
? CoreObjectNameSingular.Note
|
||||
: targetObjectNameSingular,
|
||||
),
|
||||
(isDefined(selectedRecord) &&
|
||||
!selectedRecord.isRemote &&
|
||||
objectPermissions.canUpdateObjectRecords &&
|
||||
getTargetObjectWritePermission(
|
||||
targetObjectNameSingular === CoreObjectNameSingular.TaskTarget
|
||||
? CoreObjectNameSingular.Task
|
||||
: targetObjectNameSingular === CoreObjectNameSingular.NoteTarget
|
||||
? CoreObjectNameSingular.Note
|
||||
: targetObjectNameSingular,
|
||||
)) ??
|
||||
false,
|
||||
availableOn: [
|
||||
ActionViewType.SHOW_PAGE,
|
||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||
|
||||
+2
-2
@@ -1,13 +1,13 @@
|
||||
import { ActionViewType } from '@/action-menu/actions/types/ActionViewType';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { ObjectPermissions } from '@/object-record/cache/types/ObjectPermissions';
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
||||
import { ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
export type ShouldBeRegisteredFunctionParams = {
|
||||
objectMetadataItem?: ObjectMetadataItem;
|
||||
objectPermissions: ObjectPermissions;
|
||||
objectPermissions: ObjectPermission;
|
||||
recordFilters?: RecordFilter[];
|
||||
isShowPage?: boolean;
|
||||
isSoftDeleteFilterActive?: boolean;
|
||||
|
||||
@@ -28,7 +28,7 @@ type AttachmentListProps = {
|
||||
targetableObject: ActivityTargetableObject;
|
||||
title: string;
|
||||
attachments: Attachment[];
|
||||
button?: ReactElement | false;
|
||||
button?: ReactElement | false | null;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
|
||||
@@ -8,7 +8,7 @@ import { NoteCard } from './NoteCard';
|
||||
type NoteListProps = {
|
||||
title: string;
|
||||
notes: Note[];
|
||||
button?: ReactElement | false;
|
||||
button?: ReactElement | false | null;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import { encodeCursor } from '@/apollo/utils/encodeCursor';
|
||||
import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache';
|
||||
import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNodeFromRecord';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ObjectPermission } from '~/generated-metadata/graphql';
|
||||
import { ObjectPermission } from '~/generated/graphql';
|
||||
import { parseApolloStoreFieldName } from '~/utils/parseApolloStoreFieldName';
|
||||
|
||||
/*
|
||||
|
||||
+25
@@ -5,7 +5,11 @@ import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailab
|
||||
|
||||
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
|
||||
import { availableFieldMetadataItemsForSortFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForSortFamilySelector';
|
||||
import { getReadRestrictedFieldMetadataIdsFromObjectPermissions } from '@/object-metadata/utils/getReadRestrictedFieldMetadataIdsFromObjectPermissions';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { formatFieldMetadataItemAsColumnDefinition } from '../utils/formatFieldMetadataItemAsColumnDefinition';
|
||||
|
||||
export const useColumnDefinitionsFromFieldMetadata = (
|
||||
@@ -27,6 +31,24 @@ export const useColumnDefinitionsFromFieldMetadata = (
|
||||
}),
|
||||
);
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
let restrictedFieldMetadataIds: string[] = [];
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
if (isFieldsPermissionsEnabled) {
|
||||
restrictedFieldMetadataIds =
|
||||
getReadRestrictedFieldMetadataIdsFromObjectPermissions({
|
||||
objectPermissions: [
|
||||
objectPermissionsByObjectMetadataId[objectMetadataItem.id],
|
||||
],
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
});
|
||||
}
|
||||
|
||||
const columnDefinitions: ColumnDefinition<FieldMetadata>[] =
|
||||
activeFieldMetadataItems
|
||||
.map((field, index) =>
|
||||
@@ -37,6 +59,9 @@ export const useColumnDefinitionsFromFieldMetadata = (
|
||||
}),
|
||||
)
|
||||
.filter(filterAvailableTableColumns)
|
||||
.filter((column) => {
|
||||
return !restrictedFieldMetadataIds.includes(column.fieldMetadataId);
|
||||
})
|
||||
.map((column) => {
|
||||
const existsInFilterDefinitions = filterableFieldMetadataItems.some(
|
||||
(fieldMetadataItem) =>
|
||||
|
||||
+24
-3
@@ -1,6 +1,8 @@
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getFilterFilterableFieldMetadataItems } from '@/object-metadata/utils/getFilterFilterableFieldMetadataItems';
|
||||
import { getReadRestrictedFieldMetadataIdsFromObjectPermissions } from '@/object-metadata/utils/getReadRestrictedFieldMetadataIdsFromObjectPermissions';
|
||||
import { checkIfFeatureFlagIsEnabledOnWorkspace } from '@/workspace/utils/checkIfFeatureFlagIsEnabledOnWorkspace';
|
||||
import { selectorFamily } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -18,7 +20,6 @@ export const availableFieldMetadataItemsForFilterFamilySelector =
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.id === objectMetadataItemId,
|
||||
);
|
||||
|
||||
if (!isDefined(objectMetadataItem)) {
|
||||
return [];
|
||||
}
|
||||
@@ -28,14 +29,34 @@ export const availableFieldMetadataItemsForFilterFamilySelector =
|
||||
currentWorkspace,
|
||||
);
|
||||
|
||||
const isFieldsPermissionsEnabled =
|
||||
checkIfFeatureFlagIsEnabledOnWorkspace(
|
||||
FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED,
|
||||
currentWorkspace,
|
||||
);
|
||||
|
||||
const filterFilterableFieldMetadataItems =
|
||||
getFilterFilterableFieldMetadataItems({
|
||||
isJsonFilterEnabled: isJsonFeatureFlagEnabled,
|
||||
});
|
||||
|
||||
const availableFieldMetadataItemsForFilter =
|
||||
objectMetadataItem.fields.filter(filterFilterableFieldMetadataItems);
|
||||
let restrictedFieldMetadataIds: string[] = [];
|
||||
|
||||
if (isFieldsPermissionsEnabled) {
|
||||
const currentUserWorkspace = get(currentUserWorkspaceState);
|
||||
|
||||
restrictedFieldMetadataIds =
|
||||
getReadRestrictedFieldMetadataIdsFromObjectPermissions({
|
||||
objectPermissions: currentUserWorkspace?.objectPermissions,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
});
|
||||
}
|
||||
|
||||
const availableFieldMetadataItemsForFilter = objectMetadataItem.fields
|
||||
.filter(filterFilterableFieldMetadataItems)
|
||||
.filter((field) => {
|
||||
return !restrictedFieldMetadataIds.includes(field.id);
|
||||
});
|
||||
return availableFieldMetadataItemsForFilter;
|
||||
},
|
||||
});
|
||||
|
||||
+27
-2
@@ -1,7 +1,12 @@
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { filterSortableFieldMetadataItems } from '@/object-metadata/utils/filterSortableFieldMetadataItems';
|
||||
import { getReadRestrictedFieldMetadataIdsFromObjectPermissions } from '@/object-metadata/utils/getReadRestrictedFieldMetadataIdsFromObjectPermissions';
|
||||
import { checkIfFeatureFlagIsEnabledOnWorkspace } from '@/workspace/utils/checkIfFeatureFlagIsEnabledOnWorkspace';
|
||||
import { selectorFamily } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const availableFieldMetadataItemsForSortFamilySelector = selectorFamily({
|
||||
key: 'availableFieldMetadataItemsForSortFamilySelector',
|
||||
@@ -18,8 +23,28 @@ export const availableFieldMetadataItemsForSortFamilySelector = selectorFamily({
|
||||
return [];
|
||||
}
|
||||
|
||||
const availableFieldMetadataItemsForSort =
|
||||
objectMetadataItem.fields.filter(filterSortableFieldMetadataItems);
|
||||
const currentWorkspace = get(currentWorkspaceState);
|
||||
const isFieldsPermissionsEnabled = checkIfFeatureFlagIsEnabledOnWorkspace(
|
||||
FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED,
|
||||
currentWorkspace,
|
||||
);
|
||||
|
||||
let restrictedFieldMetadataIds: string[] = [];
|
||||
if (isFieldsPermissionsEnabled) {
|
||||
const currentUserWorkspace = get(currentUserWorkspaceState);
|
||||
|
||||
restrictedFieldMetadataIds =
|
||||
getReadRestrictedFieldMetadataIdsFromObjectPermissions({
|
||||
objectPermissions: currentUserWorkspace?.objectPermissions,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
});
|
||||
}
|
||||
|
||||
const availableFieldMetadataItemsForSort = objectMetadataItem.fields
|
||||
.filter(filterSortableFieldMetadataItems)
|
||||
.filter((field) => {
|
||||
return !restrictedFieldMetadataIds.includes(field.id);
|
||||
});
|
||||
|
||||
return availableFieldMetadataItemsForSort;
|
||||
},
|
||||
|
||||
+6
-3
@@ -1,11 +1,10 @@
|
||||
import { ObjectPermissions } from '@/object-record/cache/types/ObjectPermissions';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ObjectPermission } from '~/generated-metadata/graphql';
|
||||
import { ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
export const getObjectPermissionsForObject = (
|
||||
objectPermissionsByObjectMetadataId: Record<string, ObjectPermission>,
|
||||
objectMetadataId: string,
|
||||
): ObjectPermissions => {
|
||||
): ObjectPermission => {
|
||||
const objectPermissions =
|
||||
objectPermissionsByObjectMetadataId[objectMetadataId];
|
||||
|
||||
@@ -15,6 +14,8 @@ export const getObjectPermissionsForObject = (
|
||||
canUpdateObjectRecords: true,
|
||||
canSoftDeleteObjectRecords: true,
|
||||
canDestroyObjectRecords: true,
|
||||
restrictedFields: {},
|
||||
objectMetadataId,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,5 +25,7 @@ export const getObjectPermissionsForObject = (
|
||||
canSoftDeleteObjectRecords:
|
||||
objectPermissions.canSoftDeleteObjectRecords ?? true,
|
||||
canDestroyObjectRecords: objectPermissions.canDestroyObjectRecords ?? true,
|
||||
restrictedFields: objectPermissions.restrictedFields ?? {},
|
||||
objectMetadataId,
|
||||
};
|
||||
};
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { RestrictedField } from 'twenty-shared/types';
|
||||
import { ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
export const getReadRestrictedFieldMetadataIdsFromObjectPermissions = ({
|
||||
objectPermissions,
|
||||
objectMetadataId,
|
||||
}: {
|
||||
objectPermissions?: ObjectPermission[] | null;
|
||||
objectMetadataId: string;
|
||||
}) => {
|
||||
const restrictedFields = objectPermissions?.find(
|
||||
(permission) => permission.objectMetadataId === objectMetadataId,
|
||||
)?.restrictedFields;
|
||||
|
||||
return Object.entries(restrictedFields ?? {})
|
||||
.filter(
|
||||
([_fieldMetadataId, restrictedField]) =>
|
||||
(restrictedField as RestrictedField).canRead === false,
|
||||
)
|
||||
.map(([fieldMetadataId]) => fieldMetadataId);
|
||||
};
|
||||
+4
@@ -23,6 +23,7 @@ type MapFieldMetadataToGraphQLQueryArgs = {
|
||||
relationRecordGqlFields?: RecordGqlFields;
|
||||
computeReferences?: boolean;
|
||||
objectPermissionsByObjectMetadataId: Record<string, ObjectPermission>;
|
||||
isFieldsPermissionsEnabled?: boolean;
|
||||
};
|
||||
// TODO: change ObjectMetadataItems mock before refactoring with relation computed field
|
||||
export const mapFieldMetadataToGraphQLQuery = ({
|
||||
@@ -32,6 +33,7 @@ export const mapFieldMetadataToGraphQLQuery = ({
|
||||
relationRecordGqlFields,
|
||||
computeReferences = false,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
}: MapFieldMetadataToGraphQLQueryArgs): string => {
|
||||
const fieldType = fieldMetadata.type;
|
||||
|
||||
@@ -81,6 +83,7 @@ ${mapObjectMetadataToGraphQLQuery({
|
||||
computeReferences: computeReferences,
|
||||
isRootLevel: false,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
})}`;
|
||||
}
|
||||
|
||||
@@ -117,6 +120,7 @@ ${mapObjectMetadataToGraphQLQuery({
|
||||
computeReferences,
|
||||
isRootLevel: false,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
})}
|
||||
}
|
||||
}`;
|
||||
|
||||
+19
-2
@@ -1,3 +1,4 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery';
|
||||
@@ -6,7 +7,7 @@ import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
|
||||
import { isRecordGqlFieldsNode } from '@/object-record/graphql/utils/isRecordGraphlFieldsNode';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ObjectPermission } from '~/generated-metadata/graphql';
|
||||
import { ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
type MapObjectMetadataToGraphQLQueryArgs = {
|
||||
objectMetadataItems: ObjectMetadataItem[];
|
||||
@@ -18,6 +19,7 @@ type MapObjectMetadataToGraphQLQueryArgs = {
|
||||
computeReferences?: boolean;
|
||||
isRootLevel?: boolean;
|
||||
objectPermissionsByObjectMetadataId: Record<string, ObjectPermission>;
|
||||
isFieldsPermissionsEnabled?: boolean;
|
||||
};
|
||||
|
||||
export const mapObjectMetadataToGraphQLQuery = ({
|
||||
@@ -27,6 +29,7 @@ export const mapObjectMetadataToGraphQLQuery = ({
|
||||
computeReferences = false,
|
||||
isRootLevel = true,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled = false,
|
||||
}: MapObjectMetadataToGraphQLQueryArgs): string => {
|
||||
if (
|
||||
!isRootLevel &&
|
||||
@@ -42,10 +45,22 @@ export const mapObjectMetadataToGraphQLQuery = ({
|
||||
}
|
||||
}
|
||||
|
||||
const filterReadableFields = (field: FieldMetadataItem) => {
|
||||
if (isFieldsPermissionsEnabled !== true) {
|
||||
return true;
|
||||
}
|
||||
const objectPermission = getObjectPermissionsForObject(
|
||||
objectPermissionsByObjectMetadataId,
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
return objectPermission.restrictedFields?.[field.id]?.canRead !== false;
|
||||
};
|
||||
|
||||
const manyToOneRelationFields = objectMetadataItem?.fields
|
||||
.filter((field) => field.isActive)
|
||||
.filter((field) => field.type === FieldMetadataType.RELATION)
|
||||
.filter((field) => isDefined(field.settings?.joinColumnName));
|
||||
.filter((field) => isDefined(field.settings?.joinColumnName))
|
||||
.filter(filterReadableFields);
|
||||
|
||||
const manyToOneRelationGqlFieldWithFieldMetadata =
|
||||
manyToOneRelationFields.map((field) => ({
|
||||
@@ -56,6 +71,7 @@ export const mapObjectMetadataToGraphQLQuery = ({
|
||||
const gqlFieldWithFieldMetadataThatCouldBeQueried = [
|
||||
...objectMetadataItem.fields
|
||||
.filter((fieldMetadata) => fieldMetadata.isActive)
|
||||
.filter(filterReadableFields)
|
||||
.map((fieldMetadata) => ({
|
||||
gqlField: fieldMetadata.name,
|
||||
fieldMetadata,
|
||||
@@ -99,6 +115,7 @@ export const mapObjectMetadataToGraphQLQuery = ({
|
||||
relationRecordGqlFields,
|
||||
computeReferences,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
});
|
||||
})
|
||||
.filter((field) => field !== '')
|
||||
|
||||
+6
@@ -11,7 +11,9 @@ import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphq
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { prefillRecord } from '@/object-record/utils/prefillRecord';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const useCreateOneRecordInCache = <T extends ObjectRecord>({
|
||||
objectMetadataItem,
|
||||
@@ -25,6 +27,9 @@ export const useCreateOneRecordInCache = <T extends ObjectRecord>({
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
return (record: ObjectRecord) => {
|
||||
const prefilledRecord = prefillRecord({
|
||||
@@ -46,6 +51,7 @@ export const useCreateOneRecordInCache = <T extends ObjectRecord>({
|
||||
computeReferences: true,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
})}
|
||||
`;
|
||||
|
||||
|
||||
Vendored
+7
@@ -7,7 +7,9 @@ import { RecordGqlOperationVariables } from '@/object-record/graphql/types/Recor
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const useReadFindManyRecordsQueryInCache = ({
|
||||
objectMetadataItem,
|
||||
@@ -20,6 +22,10 @@ export const useReadFindManyRecordsQueryInCache = ({
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
const readFindManyRecordsQueryInCache = <
|
||||
T extends ObjectRecord = ObjectRecord,
|
||||
>({
|
||||
@@ -34,6 +40,7 @@ export const useReadFindManyRecordsQueryInCache = ({
|
||||
objectMetadataItems,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
});
|
||||
|
||||
const existingRecordsQueryResult =
|
||||
|
||||
Vendored
+7
@@ -8,6 +8,8 @@ import { RecordGqlOperationVariables } from '@/object-record/graphql/types/Recor
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const useUpsertFindManyRecordsQueryInCache = ({
|
||||
objectMetadataItem,
|
||||
@@ -19,6 +21,10 @@ export const useUpsertFindManyRecordsQueryInCache = ({
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
const upsertFindManyRecordsQueryInCache = <
|
||||
T extends ObjectRecord = ObjectRecord,
|
||||
>({
|
||||
@@ -38,6 +44,7 @@ export const useUpsertFindManyRecordsQueryInCache = ({
|
||||
recordGqlFields,
|
||||
computeReferences,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
});
|
||||
|
||||
const newObjectRecordConnection = getRecordConnectionFromRecords({
|
||||
|
||||
+4
-1
@@ -7,7 +7,7 @@ import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { ObjectPermission } from '~/generated-metadata/graphql';
|
||||
import { ObjectPermission } from '~/generated/graphql';
|
||||
import { isEmptyObject } from '~/utils/isEmptyObject';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
@@ -18,6 +18,7 @@ export type GetRecordFromCacheArgs = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
recordGqlFields?: RecordGqlFields;
|
||||
objectPermissionsByObjectMetadataId: Record<string, ObjectPermission>;
|
||||
isFieldsPermissionsEnabled?: boolean;
|
||||
};
|
||||
export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
|
||||
objectMetadataItem,
|
||||
@@ -26,6 +27,7 @@ export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
|
||||
recordId,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled = false,
|
||||
}: GetRecordFromCacheArgs) => {
|
||||
if (isUndefinedOrNull(objectMetadataItem)) {
|
||||
return null;
|
||||
@@ -43,6 +45,7 @@ export const getRecordFromCache = <T extends ObjectRecord = ObjectRecord>({
|
||||
objectMetadataItem,
|
||||
recordGqlFields: appliedRecordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
},
|
||||
)}
|
||||
`;
|
||||
|
||||
+4
-1
@@ -7,7 +7,7 @@ import { getRecordNodeFromRecord } from '@/object-record/cache/utils/getRecordNo
|
||||
import { RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { ObjectPermission } from '~/generated-metadata/graphql';
|
||||
import { ObjectPermission } from '~/generated/graphql';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const updateRecordFromCache = <T extends ObjectRecord>({
|
||||
@@ -17,6 +17,7 @@ export const updateRecordFromCache = <T extends ObjectRecord>({
|
||||
recordGqlFields,
|
||||
record,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
}: {
|
||||
objectMetadataItems: ObjectMetadataItem[];
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
@@ -24,6 +25,7 @@ export const updateRecordFromCache = <T extends ObjectRecord>({
|
||||
recordGqlFields: Record<string, boolean>;
|
||||
record: T;
|
||||
objectPermissionsByObjectMetadataId: Record<string, ObjectPermission>;
|
||||
isFieldsPermissionsEnabled?: boolean;
|
||||
}) => {
|
||||
if (isUndefinedOrNull(objectMetadataItem)) {
|
||||
return null;
|
||||
@@ -39,6 +41,7 @@ export const updateRecordFromCache = <T extends ObjectRecord>({
|
||||
computeReferences: true,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
},
|
||||
)}
|
||||
`;
|
||||
|
||||
@@ -8,7 +8,9 @@ import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation';
|
||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { getCreateManyRecordsMutationResponseField } from '@/object-record/utils/getCreateManyRecordsMutationResponseField';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useCreateManyRecordsMutation = ({
|
||||
@@ -26,6 +28,10 @@ export const useCreateManyRecordsMutation = ({
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
if (isUndefinedOrNull(objectMetadataItem)) {
|
||||
return { createManyRecordsMutation: EMPTY_MUTATION };
|
||||
}
|
||||
@@ -46,6 +52,7 @@ export const useCreateManyRecordsMutation = ({
|
||||
objectMetadataItem,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
},
|
||||
)}
|
||||
}`;
|
||||
|
||||
@@ -9,7 +9,9 @@ import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/getCreateOneRecordMutationResponseField';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useCreateOneRecordMutation = ({
|
||||
@@ -33,6 +35,10 @@ export const useCreateOneRecordMutation = ({
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
if (isUndefinedOrNull(objectMetadataItem)) {
|
||||
return { createOneRecordMutation: EMPTY_MUTATION };
|
||||
}
|
||||
@@ -50,6 +56,7 @@ export const useCreateOneRecordMutation = ({
|
||||
objectMetadataItem,
|
||||
recordGqlFields: appliedRecordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
})}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -7,7 +7,9 @@ import { isAggregationEnabled } from '@/object-metadata/utils/isAggregationEnabl
|
||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { getFindDuplicateRecordsQueryResponseField } from '@/object-record/utils/getFindDuplicateRecordsQueryResponseField';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const useFindDuplicateRecordsQuery = ({
|
||||
objectNameSingular,
|
||||
@@ -22,6 +24,10 @@ export const useFindDuplicateRecordsQuery = ({
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
const findDuplicateRecordsQuery = gql`
|
||||
query FindDuplicate${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
@@ -34,6 +40,7 @@ export const useFindDuplicateRecordsQuery = ({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
})}
|
||||
cursor
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
generateFindManyRecordsQuery,
|
||||
QueryCursorDirection,
|
||||
} from '@/object-record/utils/generateFindManyRecordsQuery';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const useFindManyRecordsQuery = ({
|
||||
objectNameSingular,
|
||||
@@ -28,6 +30,10 @@ export const useFindManyRecordsQuery = ({
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
const findManyRecordsQuery = generateFindManyRecordsQuery({
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
@@ -35,6 +41,7 @@ export const useFindManyRecordsQuery = ({
|
||||
computeReferences,
|
||||
cursorDirection,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -6,7 +6,9 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat
|
||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const useFindOneRecordQuery = ({
|
||||
objectNameSingular,
|
||||
@@ -25,6 +27,10 @@ export const useFindOneRecordQuery = ({
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
const findOneRecordQuery = gql`
|
||||
query FindOne${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
@@ -48,6 +54,7 @@ export const useFindOneRecordQuery = ({
|
||||
objectMetadataItem,
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
})}
|
||||
},
|
||||
`;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ObjectPermission } from '~/generated-metadata/graphql';
|
||||
import { ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
type useObjectPermissionsReturnType = {
|
||||
objectPermissionsByObjectMetadataId: Record<string, ObjectPermission>;
|
||||
|
||||
@@ -9,7 +9,9 @@ import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { getUpdateOneRecordMutationResponseField } from '@/object-record/utils/getUpdateOneRecordMutationResponseField';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useUpdateOneRecordMutation = ({
|
||||
@@ -29,6 +31,10 @@ export const useUpdateOneRecordMutation = ({
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
if (isUndefinedOrNull(objectMetadataItem)) {
|
||||
return { updateOneRecordMutation: EMPTY_MUTATION };
|
||||
}
|
||||
@@ -54,6 +60,7 @@ export const useUpdateOneRecordMutation = ({
|
||||
computeReferences,
|
||||
recordGqlFields: appliedRecordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
},
|
||||
)}
|
||||
}
|
||||
|
||||
+7
@@ -9,8 +9,10 @@ import { RecordGqlOperationSignature } from '@/object-record/graphql/types/Recor
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { getCombinedFindManyRecordsQueryFilteringPart } from '@/object-record/multiple-objects/utils/getCombinedFindManyRecordsQueryFilteringPart';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { isNonEmptyArray } from '~/utils/isNonEmptyArray';
|
||||
|
||||
export const useGenerateCombinedFindManyRecordsQuery = ({
|
||||
@@ -21,6 +23,10 @@ export const useGenerateCombinedFindManyRecordsQuery = ({
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
if (!isNonEmptyArray(operationSignatures)) {
|
||||
return null;
|
||||
}
|
||||
@@ -112,6 +118,7 @@ export const useGenerateCombinedFindManyRecordsQuery = ({
|
||||
objectMetadataItem,
|
||||
}),
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
})}
|
||||
cursor
|
||||
}
|
||||
|
||||
+7
@@ -13,8 +13,10 @@ import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions
|
||||
import { CombinedFindManyRecordsQueryResult } from '@/object-record/multiple-objects/types/CombinedFindManyRecordsQueryResult';
|
||||
import { generateCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables';
|
||||
import { getCombinedFindManyRecordsQueryFilteringPart } from '@/object-record/multiple-objects/utils/getCombinedFindManyRecordsQueryFilteringPart';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
export const usePerformCombinedFindManyRecords = () => {
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
@@ -22,6 +24,10 @@ export const usePerformCombinedFindManyRecords = () => {
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
const generateCombinedFindManyRecordsQuery = (
|
||||
operationSignatures: RecordGqlOperationSignature[],
|
||||
objectMetadataItemsValue: ObjectMetadataItem[],
|
||||
@@ -98,6 +104,7 @@ export const usePerformCombinedFindManyRecords = () => {
|
||||
objectMetadataItem,
|
||||
}),
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
})}
|
||||
cursor
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ export const ObjectOptionsDropdownFieldsContent = () => {
|
||||
handleColumnVisibilityChange,
|
||||
handleReorderColumns,
|
||||
visibleTableColumns,
|
||||
} = useObjectOptionsForTable(recordIndexId);
|
||||
} = useObjectOptionsForTable(recordIndexId, objectMetadataItem.id);
|
||||
|
||||
const {
|
||||
visibleBoardFields,
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ export const ObjectOptionsDropdownHiddenFieldsContent = () => {
|
||||
});
|
||||
|
||||
const { handleColumnVisibilityChange, hiddenTableColumns } =
|
||||
useObjectOptionsForTable(recordIndexId);
|
||||
useObjectOptionsForTable(recordIndexId, objectMetadataItem.id);
|
||||
|
||||
const { hiddenBoardFields, handleBoardFieldVisibilityChange } =
|
||||
useObjectOptionsForBoard({
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ describe('useObjectOptionsForTable', () => {
|
||||
];
|
||||
|
||||
const renderWithRecoil = () =>
|
||||
renderHook(() => useObjectOptionsForTable('instance-id'), {
|
||||
renderHook(() => useObjectOptionsForTable('instance-id', 'object-id'), {
|
||||
wrapper: ({ children }) => (
|
||||
<RecordTableComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'instance-id', onColumnsChange: jest.fn() }}
|
||||
|
||||
+5
-2
@@ -7,7 +7,10 @@ import { visibleTableColumnsComponentSelector } from '@/object-record/record-tab
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { moveArrayItem } from '~/utils/array/moveArrayItem';
|
||||
|
||||
export const useObjectOptionsForTable = (recordTableId: string) => {
|
||||
export const useObjectOptionsForTable = (
|
||||
recordTableId: string,
|
||||
objectMetadataId: string,
|
||||
) => {
|
||||
const hiddenTableColumns = useRecoilComponentValueV2(
|
||||
hiddenTableColumnsComponentSelector,
|
||||
recordTableId,
|
||||
@@ -18,7 +21,7 @@ export const useObjectOptionsForTable = (recordTableId: string) => {
|
||||
);
|
||||
|
||||
const { handleColumnVisibilityChange, handleColumnReorder } = useTableColumns(
|
||||
{ recordTableId: recordTableId },
|
||||
{ recordTableId, objectMetadataId },
|
||||
);
|
||||
|
||||
const handleReorderColumns: OnDragEndResponder = useCallback(
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { ObjectPermission } from '~/generated-metadata/graphql';
|
||||
import { ObjectPermission } from '~/generated/graphql';
|
||||
import { createRequiredContext } from '~/utils/createRequiredContext';
|
||||
|
||||
export type RecordIndexContextValue = {
|
||||
|
||||
+5
-1
@@ -108,7 +108,11 @@ export const useLoadRecordIndexStates = () => {
|
||||
columnDefinitions,
|
||||
});
|
||||
|
||||
setTableColumns(newFieldDefinitions, recordIndexId);
|
||||
setTableColumns(
|
||||
newFieldDefinitions,
|
||||
recordIndexId,
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
|
||||
const existingRecordIndexFieldDefinitions = snapshot
|
||||
.getLoadable(recordIndexFieldDefinitionsState)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { createRequiredContext } from '~/utils/createRequiredContext';
|
||||
|
||||
export type RecordTableContextValue = {
|
||||
type RecordTableContextValue = {
|
||||
recordTableId: string;
|
||||
viewBarId: string;
|
||||
objectNameSingular: string;
|
||||
|
||||
+41
-4
@@ -1,14 +1,26 @@
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { tableColumnsComponentState } from '@/object-record/record-table/states/tableColumnsComponentState';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { RestrictedField } from 'twenty-shared/types';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
export const useSetTableColumns = () => {
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
const setTableColumns = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(columns: ColumnDefinition<FieldMetadata>[], recordTableId: string) => {
|
||||
(
|
||||
columns: ColumnDefinition<FieldMetadata>[],
|
||||
recordTableId: string,
|
||||
objectMetadataId: string,
|
||||
) => {
|
||||
const tableColumns = getSnapshotValue(
|
||||
snapshot,
|
||||
tableColumnsComponentState.atomFamily({
|
||||
@@ -16,17 +28,42 @@ export const useSetTableColumns = () => {
|
||||
}),
|
||||
);
|
||||
|
||||
if (isDeeplyEqual(tableColumns, columns)) {
|
||||
let columnsToSet = columns;
|
||||
|
||||
if (isFieldsPermissionsEnabled) {
|
||||
const restrictedFields = getSnapshotValue(
|
||||
snapshot,
|
||||
currentUserWorkspaceState,
|
||||
)?.objectPermissions?.find(
|
||||
(permission) => permission.objectMetadataId === objectMetadataId,
|
||||
)?.restrictedFields;
|
||||
|
||||
const restrictedFieldMetadataIds = Object.entries(
|
||||
restrictedFields ?? {},
|
||||
)
|
||||
.filter(
|
||||
([_fieldMetadataId, restrictedField]) =>
|
||||
(restrictedField as RestrictedField).canRead === false,
|
||||
)
|
||||
.map(([fieldMetadataId]) => fieldMetadataId);
|
||||
const nonRestrictedColumns = columns.filter(
|
||||
(column) =>
|
||||
!restrictedFieldMetadataIds?.includes(column.fieldMetadataId),
|
||||
);
|
||||
columnsToSet = nonRestrictedColumns;
|
||||
}
|
||||
|
||||
if (isDeeplyEqual(tableColumns, columnsToSet)) {
|
||||
return;
|
||||
}
|
||||
set(
|
||||
tableColumnsComponentState.atomFamily({
|
||||
instanceId: recordTableId,
|
||||
}),
|
||||
columns,
|
||||
columnsToSet,
|
||||
);
|
||||
},
|
||||
[],
|
||||
[isFieldsPermissionsEnabled],
|
||||
);
|
||||
|
||||
return { setTableColumns };
|
||||
|
||||
+4
-3
@@ -16,9 +16,10 @@ import { ColumnDefinition } from '../types/ColumnDefinition';
|
||||
|
||||
type useRecordTableProps = {
|
||||
recordTableId?: string;
|
||||
objectMetadataId: string;
|
||||
};
|
||||
|
||||
export const useTableColumns = (props?: useRecordTableProps) => {
|
||||
export const useTableColumns = (props: useRecordTableProps) => {
|
||||
const { onColumnsChange } = useRecordTable({
|
||||
recordTableId: props?.recordTableId,
|
||||
});
|
||||
@@ -52,11 +53,11 @@ export const useTableColumns = (props?: useRecordTableProps) => {
|
||||
|
||||
const handleColumnsChange = useCallback(
|
||||
async (columns: ColumnDefinition<FieldMetadata>[]) => {
|
||||
setTableColumns(columns, instanceId);
|
||||
setTableColumns(columns, instanceId, props.objectMetadataId);
|
||||
|
||||
await onColumnsChange?.(columns);
|
||||
},
|
||||
[setTableColumns, instanceId, onColumnsChange],
|
||||
[setTableColumns, instanceId, onColumnsChange, props.objectMetadataId],
|
||||
);
|
||||
|
||||
const handleColumnVisibilityChange = useCallback(
|
||||
|
||||
+3
-1
@@ -24,6 +24,7 @@ import { ColumnDefinition } from '../../types/ColumnDefinition';
|
||||
|
||||
export type RecordTableColumnHeadDropdownMenuProps = {
|
||||
column: ColumnDefinition<FieldMetadata>;
|
||||
objectMetadataId: string;
|
||||
};
|
||||
|
||||
const StyledDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
|
||||
@@ -32,6 +33,7 @@ const StyledDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
|
||||
|
||||
export const RecordTableColumnHeadDropdownMenu = ({
|
||||
column,
|
||||
objectMetadataId,
|
||||
}: RecordTableColumnHeadDropdownMenuProps) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
@@ -52,7 +54,7 @@ export const RecordTableColumnHeadDropdownMenu = ({
|
||||
column.fieldMetadataId !== lastVisibleColumn?.fieldMetadataId && canMove;
|
||||
|
||||
const { handleColumnVisibilityChange, handleMoveTableColumn } =
|
||||
useTableColumns();
|
||||
useTableColumns({ objectMetadataId });
|
||||
|
||||
const dropdownId = column.fieldMetadataId + '-header';
|
||||
|
||||
|
||||
+8
-1
@@ -8,9 +8,11 @@ import { RecordTableColumnHeadDropdownMenu } from './RecordTableColumnHeadDropdo
|
||||
|
||||
type RecordTableColumnHeadWithDropdownProps = {
|
||||
column: ColumnDefinition<FieldMetadata>;
|
||||
objectMetadataId: string;
|
||||
};
|
||||
|
||||
export const RecordTableColumnHeadWithDropdown = ({
|
||||
objectMetadataId,
|
||||
column,
|
||||
}: RecordTableColumnHeadWithDropdownProps) => {
|
||||
const { toggleScrollXWrapper, toggleScrollYWrapper } =
|
||||
@@ -32,7 +34,12 @@ export const RecordTableColumnHeadWithDropdown = ({
|
||||
onClose={handleDropdownClose}
|
||||
dropdownId={column.fieldMetadataId + '-header'}
|
||||
clickableComponent={<RecordTableColumnHead column={column} />}
|
||||
dropdownComponents={<RecordTableColumnHeadDropdownMenu column={column} />}
|
||||
dropdownComponents={
|
||||
<RecordTableColumnHeadDropdownMenu
|
||||
column={column}
|
||||
objectMetadataId={objectMetadataId}
|
||||
/>
|
||||
}
|
||||
dropdownOffset={{ x: -1 }}
|
||||
dropdownPlacement="bottom-start"
|
||||
/>
|
||||
|
||||
+7
-2
@@ -137,7 +137,9 @@ export const RecordTableHeaderCell = ({
|
||||
>(null);
|
||||
const [resizedFieldKey, setResizedFieldKey] = useState<string | null>(null);
|
||||
|
||||
const { handleColumnsChange } = useTableColumns();
|
||||
const { handleColumnsChange } = useTableColumns({
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
});
|
||||
|
||||
const handleResizeHandlerStart = useCallback<PointerEventListener>(
|
||||
({ x }) => {
|
||||
@@ -256,7 +258,10 @@ export const RecordTableHeaderCell = ({
|
||||
isFirstRowActiveOrFocused={isFirstRowActiveOrFocused}
|
||||
>
|
||||
<StyledColumnHeadContainer>
|
||||
<RecordTableColumnHeadWithDropdown column={column} />
|
||||
<RecordTableColumnHeadWithDropdown
|
||||
column={column}
|
||||
objectMetadataId={objectMetadataItem.id}
|
||||
/>
|
||||
{(useIsMobile() || iconVisibility) &&
|
||||
!!column.isLabelIdentifier &&
|
||||
!isReadOnly &&
|
||||
|
||||
+3
-1
@@ -30,7 +30,9 @@ export const RecordTableHeaderPlusButtonContent = () => {
|
||||
);
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
const { handleColumnVisibilityChange } = useTableColumns();
|
||||
const { handleColumnVisibilityChange } = useTableColumns({
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
});
|
||||
|
||||
const handleAddColumn = useCallback(
|
||||
(column: ColumnDefinition<FieldMetadata>) => {
|
||||
|
||||
+4
-1
@@ -4,7 +4,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { ObjectPermission } from '~/generated-metadata/graphql';
|
||||
import { ObjectPermission } from '~/generated/graphql';
|
||||
|
||||
export type QueryCursorDirection = 'before' | 'after';
|
||||
|
||||
@@ -15,6 +15,7 @@ export const generateFindManyRecordsQuery = ({
|
||||
computeReferences,
|
||||
cursorDirection,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItems: ObjectMetadataItem[];
|
||||
@@ -22,6 +23,7 @@ export const generateFindManyRecordsQuery = ({
|
||||
computeReferences?: boolean;
|
||||
cursorDirection?: QueryCursorDirection;
|
||||
objectPermissionsByObjectMetadataId: Record<string, ObjectPermission>;
|
||||
isFieldsPermissionsEnabled?: boolean;
|
||||
}) => gql`
|
||||
query FindMany${capitalize(
|
||||
objectMetadataItem.namePlural,
|
||||
@@ -42,6 +44,7 @@ query FindMany${capitalize(
|
||||
recordGqlFields,
|
||||
computeReferences,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
})}
|
||||
cursor
|
||||
}
|
||||
|
||||
+1
@@ -7,5 +7,6 @@ export const OBJECT_PERMISSION_FRAGMENT = gql`
|
||||
canUpdateObjectRecords
|
||||
canSoftDeleteObjectRecords
|
||||
canDestroyObjectRecords
|
||||
restrictedFields
|
||||
}
|
||||
`;
|
||||
|
||||
+1
@@ -61,6 +61,7 @@ export const SignInBackgroundMockContainerEffect = ({
|
||||
columnDefinitions: SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS,
|
||||
}),
|
||||
recordTableId,
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
|
||||
if (contextStoreCurrentObjectMetadataItemId !== objectMetadataItem.id) {
|
||||
|
||||
@@ -16,8 +16,10 @@ import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { relationFilterValueSchemaObject } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { ViewFilterOperand } from 'twenty-shared/src/types/ViewFilterOperand';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
const filterQueryParamsSchema = z.object({
|
||||
viewId: z.string().optional(),
|
||||
@@ -69,6 +71,10 @@ export const useViewFromQueryParams = () => {
|
||||
isDefined(filterQueryParams) &&
|
||||
Object.entries(filterQueryParams).length > 0;
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isFieldsPermissionsEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
const getFiltersFromQueryParams = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
@@ -125,6 +131,7 @@ export const useViewFromQueryParams = () => {
|
||||
objectMetadataItem: relationObjectMetadataItem,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
}),
|
||||
variables: {
|
||||
filter: {
|
||||
@@ -186,6 +193,7 @@ export const useViewFromQueryParams = () => {
|
||||
objectMetadataItem.fields,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
isFieldsPermissionsEnabled,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+9
-6
@@ -64,9 +64,18 @@ export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseR
|
||||
|
||||
let objectRecords: Partial<ObjectRecord>[] = [];
|
||||
|
||||
const columnsToSelect = buildColumnsToSelect({
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
|
||||
if (executionArgs.args.ids) {
|
||||
objectRecords = (await existingRecordsQueryBuilder
|
||||
.where({ id: In(executionArgs.args.ids) })
|
||||
.setFindOptions({
|
||||
select: columnsToSelect,
|
||||
})
|
||||
.getMany()) as ObjectRecord[];
|
||||
} else if (executionArgs.args.data && !isEmpty(executionArgs.args.data)) {
|
||||
objectRecords = executionArgs.args.data;
|
||||
@@ -92,12 +101,6 @@ export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseR
|
||||
});
|
||||
}
|
||||
|
||||
const columnsToSelect = buildColumnsToSelect({
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
});
|
||||
|
||||
const duplicateRecordsQueryBuilder =
|
||||
executionArgs.repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
|
||||
+8
@@ -1,5 +1,8 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
import { RestrictedFields } from 'twenty-shared/types';
|
||||
|
||||
@ObjectType('ObjectPermission')
|
||||
export class ObjectPermissionDTO {
|
||||
@Field({ nullable: false })
|
||||
@@ -16,4 +19,9 @@ export class ObjectPermissionDTO {
|
||||
|
||||
@Field({ nullable: true })
|
||||
canDestroyObjectRecords?: boolean;
|
||||
|
||||
@Field(() => GraphQLJSON, {
|
||||
nullable: true,
|
||||
})
|
||||
restrictedFields?: RestrictedFields;
|
||||
}
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ export const fromUserWorkspacePermissionsToUserWorkspacePermissionsDto = ({
|
||||
canUpdateObjectRecords: permissions.canUpdate,
|
||||
canSoftDeleteObjectRecords: permissions.canSoftDelete,
|
||||
canDestroyObjectRecords: permissions.canDestroy,
|
||||
restrictedFields: permissions.restrictedFields,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export type RestrictedField = {
|
||||
canRead?: boolean | null;
|
||||
canUpdate?: boolean | null;
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
export type RestrictedFields = Record<
|
||||
string,
|
||||
{ canRead?: boolean | null; canUpdate?: boolean | null }
|
||||
>;
|
||||
import { RestrictedField } from './RestrictedField';
|
||||
|
||||
export type RestrictedFields = Record<string, RestrictedField>;
|
||||
|
||||
@@ -14,6 +14,7 @@ export type { IsExactly } from './IsExactly';
|
||||
export type { NullablePartial } from './NullablePartial';
|
||||
export type { ObjectRecordsPermissions } from './ObjectRecordsPermissions';
|
||||
export type { ObjectRecordsPermissionsByRoleId } from './ObjectRecordsPermissionsByRoleId';
|
||||
export type { RestrictedField } from './RestrictedField';
|
||||
export type { RestrictedFields } from './RestrictedFields';
|
||||
export type { StepFilterGroup, StepFilter } from './StepFilters';
|
||||
export { StepLogicalOperator } from './StepFilters';
|
||||
|
||||
Reference in New Issue
Block a user