Add delete and restore event handling for table and board (#17489)
This PR adds what is required to handle soft-delete and restore SSE events in virtualized table and board. Restore is not handled in board for now as it requires respecting sorts when inserting record ids. Since virtualized table is refetching small chunks, we just refetch for now. The long term goal is to handle event handling without refetching in all main components, and also handle SSE events that have the same origin that the current tab. But for now we implement what is easily doable. # QA Delete between table and board (delete only) : https://github.com/user-attachments/assets/715dd44a-007a-44ab-bf49-5ef039cd57c3 Delete and restore between table and table : https://github.com/user-attachments/assets/f2122519-e969-491f-b71c-018d0f85bd86
This commit is contained in:
+17
-4
@@ -78,12 +78,14 @@ export const isRecordMatchingRLSRowLevelPermissionPredicate = ({
|
||||
filter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
shouldIgnoreSoftDeleteDefaultFilter,
|
||||
}: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
record: any;
|
||||
filter: RecordGqlOperationFilter;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
shouldIgnoreSoftDeleteDefaultFilter?: boolean;
|
||||
}): boolean => {
|
||||
if (Object.keys(filter).length === 0 && record.deletedAt === null) {
|
||||
return true;
|
||||
@@ -96,6 +98,7 @@ export const isRecordMatchingRLSRowLevelPermissionPredicate = ({
|
||||
filter: { [filterKey]: value },
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
shouldIgnoreSoftDeleteDefaultFilter,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -117,6 +120,7 @@ export const isRecordMatchingRLSRowLevelPermissionPredicate = ({
|
||||
filter: andFilter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
shouldIgnoreSoftDeleteDefaultFilter,
|
||||
}),
|
||||
)
|
||||
);
|
||||
@@ -134,6 +138,7 @@ export const isRecordMatchingRLSRowLevelPermissionPredicate = ({
|
||||
filter: orFilter,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
shouldIgnoreSoftDeleteDefaultFilter,
|
||||
}),
|
||||
)
|
||||
);
|
||||
@@ -146,6 +151,7 @@ export const isRecordMatchingRLSRowLevelPermissionPredicate = ({
|
||||
filter: filterValue,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
shouldIgnoreSoftDeleteDefaultFilter,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -166,14 +172,21 @@ export const isRecordMatchingRLSRowLevelPermissionPredicate = ({
|
||||
filter: filterValue,
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
shouldIgnoreSoftDeleteDefaultFilter,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (isLeafFilter(filter)) {
|
||||
if (isDefined(record.deletedAt) && filter.deletedAt === undefined) {
|
||||
return false;
|
||||
}
|
||||
const shouldTakeDeletedAtIntoAccount =
|
||||
shouldIgnoreSoftDeleteDefaultFilter !== true;
|
||||
|
||||
const shouldRejectMatchingBecauseRecordIsSoftDeleted =
|
||||
isLeafFilter(filter) &&
|
||||
shouldTakeDeletedAtIntoAccount &&
|
||||
isDefined(record.deletedAt);
|
||||
|
||||
if (shouldRejectMatchingBecauseRecordIsSoftDeleted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const objectFields = getFlatFieldsFromFlatObjectMetadata(
|
||||
|
||||
+11
-5
@@ -8,6 +8,7 @@ import {
|
||||
} from 'twenty-shared/types';
|
||||
import { combineFilters, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { type SerializableAuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { type FlatWorkspaceMemberMaps } from 'src/engine/core-modules/user/types/flat-workspace-member-maps.type';
|
||||
import { transformEventToWebhookEvent } from 'src/engine/metadata-modules/webhook/utils/transform-event-to-webhook-event';
|
||||
@@ -252,10 +253,10 @@ export class WorkspaceEventEmitterService {
|
||||
}
|
||||
|
||||
private filterRestrictedFieldsFromEvent(
|
||||
event: ObjectRecordEvent & { objectNameSingular: string },
|
||||
event: ObjectRecordSubscriptionEvent,
|
||||
restrictedFields: RestrictedFieldsPermissions | undefined,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
): ObjectRecordEvent & { objectNameSingular: string } {
|
||||
): ObjectRecordSubscriptionEvent {
|
||||
if (!restrictedFields || Object.keys(restrictedFields).length === 0) {
|
||||
return event;
|
||||
}
|
||||
@@ -311,7 +312,7 @@ export class WorkspaceEventEmitterService {
|
||||
return {
|
||||
...event,
|
||||
properties: filteredProperties,
|
||||
} as ObjectRecordEvent & { objectNameSingular: string };
|
||||
} as ObjectRecordSubscriptionEvent;
|
||||
}
|
||||
|
||||
private getMatchingQueryIds(
|
||||
@@ -322,7 +323,7 @@ export class WorkspaceEventEmitterService {
|
||||
variables?: { filter?: RecordGqlOperationFilter };
|
||||
}
|
||||
>,
|
||||
event: ObjectRecordEvent & { objectNameSingular: string },
|
||||
event: ObjectRecordSubscriptionEvent,
|
||||
subscriberRLSFilter: RecordGqlOperationFilter | null,
|
||||
objectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
@@ -351,7 +352,7 @@ export class WorkspaceEventEmitterService {
|
||||
objectNameSingular: string;
|
||||
variables?: { filter?: RecordGqlOperationFilter };
|
||||
},
|
||||
event: ObjectRecordEvent & { objectNameSingular: string },
|
||||
event: ObjectRecordSubscriptionEvent,
|
||||
subscriberRLSFilter: RecordGqlOperationFilter | null,
|
||||
objectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
@@ -384,11 +385,16 @@ export class WorkspaceEventEmitterService {
|
||||
return true;
|
||||
}
|
||||
|
||||
const shouldIgnoreSoftDeleteDefaultFilter =
|
||||
event.action === DatabaseEventAction.DELETED ||
|
||||
event.action === DatabaseEventAction.RESTORED;
|
||||
|
||||
return isRecordMatchingRLSRowLevelPermissionPredicate({
|
||||
record,
|
||||
filter: combinedFilter,
|
||||
flatObjectMetadata: objectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
shouldIgnoreSoftDeleteDefaultFilter,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user