Update store on data model mutation (#18684)

- enrich SSE events with relations
- remove queries from sse metadata events
- on sse event, manage store
- on object/field metadata changes, manage store

TODO left:
- fix other metadata items

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Thomas Trompette
2026-03-17 19:32:19 +01:00
committed by GitHub
parent e62dfae741
commit 994215e0dc
35 changed files with 1399 additions and 925 deletions
@@ -10,7 +10,6 @@ import { type IndexMetadataInterface } from 'src/engine/metadata-modules/index-m
import { I18nService } from 'src/engine/core-modules/i18n/i18n.service';
import { type IDataloaders } from 'src/engine/dataloaders/dataloader.interface';
import { filterMorphRelationDuplicateFields } from 'src/engine/dataloaders/utils/filter-morph-relation-duplicate-fields.util';
import { pickMorphGroupSurvivor } from 'src/engine/dataloaders/utils/pick-morph-group-survivor.util';
import { FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
import { type FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/relation.dto';
@@ -21,11 +20,10 @@ import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { findManyFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps.util';
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { findAllOthersMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-all-others-morph-relation-flat-field-metadatas-or-throw.util';
import { fromFlatFieldMetadataToFieldMetadataDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-flat-field-metadata-to-field-metadata-dto.util';
import { fromMorphOrRelationFlatFieldMetadataToRelationDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-morph-or-relation-flat-field-metadata-to-relation-dto.util';
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
import { resolveMorphRelationsFromFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/resolve-morph-relations-from-flat-field-metadata.util';
import { resolveRelationFromFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/resolve-relation-from-flat-field-metadata.util';
import { fromFlatObjectMetadataToObjectMetadataDto } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-to-object-metadata-dto.util';
import { getMorphNameFromMorphFieldMetadataName } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-morph-name-from-morph-field-metadata-name.util';
import { fromFlatViewFieldGroupToViewFieldGroupDto } from 'src/engine/metadata-modules/view-field-group/utils/from-flat-view-field-group-to-view-field-group-dto.util';
@@ -158,7 +156,6 @@ export class DataloaderService {
private createRelationLoader() {
return new DataLoader<RelationLoaderPayload, RelationDTO | null>(
async (dataLoaderParams: RelationLoaderPayload[]) => {
const relationDtos: Array<RelationDTO | null> = [];
const workspaceId = dataLoaderParams[0].workspaceId;
const { flatFieldMetadataMaps, flatObjectMetadataMaps } =
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
@@ -168,7 +165,7 @@ export class DataloaderService {
},
);
for (const { fieldMetadataId } of dataLoaderParams) {
return dataLoaderParams.map(({ fieldMetadataId }) => {
const sourceFlatFieldMetadata =
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: fieldMetadataId,
@@ -181,85 +178,15 @@ export class DataloaderService {
FieldMetadataType.RELATION,
)
) {
relationDtos.push(null);
continue;
return null;
}
const sourceFlatObjectMetadata =
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: sourceFlatFieldMetadata.objectMetadataId,
flatEntityMaps: flatObjectMetadataMaps,
});
const targetFlatFieldMetadata =
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId:
sourceFlatFieldMetadata.relationTargetFieldMetadataId,
flatEntityMaps: flatFieldMetadataMaps,
});
if (!isMorphOrRelationFlatFieldMetadata(targetFlatFieldMetadata)) {
relationDtos.push(null);
continue;
}
const targetFlatObjectMetadata =
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: targetFlatFieldMetadata.objectMetadataId,
flatEntityMaps: flatObjectMetadataMaps,
});
if (
isFlatFieldMetadataOfType(
targetFlatFieldMetadata,
FieldMetadataType.MORPH_RELATION,
)
) {
const morphNameFromMorphFieldMetadataName =
getMorphNameFromMorphFieldMetadataName({
morphRelationFlatFieldMetadata: targetFlatFieldMetadata,
nameSingular: sourceFlatObjectMetadata.nameSingular,
namePlural: sourceFlatObjectMetadata.namePlural,
});
const allMorphFlatFieldMetadatas = [
targetFlatFieldMetadata,
...findAllOthersMorphRelationFlatFieldMetadatasOrThrow({
flatFieldMetadata: targetFlatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadata: targetFlatObjectMetadata,
}),
];
const survivorMorphField = pickMorphGroupSurvivor(
allMorphFlatFieldMetadatas,
);
relationDtos.push(
fromMorphOrRelationFlatFieldMetadataToRelationDto({
sourceFlatFieldMetadata,
sourceFlatObjectMetadata,
targetFlatFieldMetadata: {
...survivorMorphField,
name: morphNameFromMorphFieldMetadataName,
},
targetFlatObjectMetadata,
}),
);
continue;
}
relationDtos.push(
fromMorphOrRelationFlatFieldMetadataToRelationDto({
sourceFlatFieldMetadata,
targetFlatFieldMetadata,
targetFlatObjectMetadata,
sourceFlatObjectMetadata,
}),
);
}
return relationDtos;
return resolveRelationFromFlatFieldMetadata({
sourceFlatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadataMaps,
});
});
},
);
}
@@ -275,9 +202,8 @@ export class DataloaderService {
flatMapsKeys: ['flatFieldMetadataMaps', 'flatObjectMetadataMaps'],
},
);
const relationDtos: Array<RelationDTO[] | null> = [];
for (const { fieldMetadataId } of dataLoaderParams) {
return dataLoaderParams.map(({ fieldMetadataId }) => {
const morphFlatFieldMetadata =
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: fieldMetadataId,
@@ -290,70 +216,15 @@ export class DataloaderService {
FieldMetadataType.MORPH_RELATION,
)
) {
relationDtos.push(null);
continue;
return null;
}
const sourceFlatObjectMetadata =
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityMaps: flatObjectMetadataMaps,
flatEntityId: morphFlatFieldMetadata.objectMetadataId,
});
const relatedMorphFlatFieldMetadatas =
findAllOthersMorphRelationFlatFieldMetadatasOrThrow({
flatFieldMetadata: morphFlatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadata: sourceFlatObjectMetadata,
});
const allMorphFlatFieldMetadatas = [
return resolveMorphRelationsFromFlatFieldMetadata({
morphFlatFieldMetadata,
...relatedMorphFlatFieldMetadatas,
];
relationDtos.push(
allMorphFlatFieldMetadatas.flatMap((sourceFlatFieldMetadata) => {
const targetFlatFieldMetadata =
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId:
sourceFlatFieldMetadata.relationTargetFieldMetadataId,
flatEntityMaps: flatFieldMetadataMaps,
});
if (
!isMorphOrRelationFlatFieldMetadata(targetFlatFieldMetadata)
) {
return [];
}
const targetFlatObjectMetadata =
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId:
sourceFlatFieldMetadata.relationTargetObjectMetadataId,
flatEntityMaps: flatObjectMetadataMaps,
});
const morphNameFromMorphFieldMetadataName =
getMorphNameFromMorphFieldMetadataName({
morphRelationFlatFieldMetadata: sourceFlatFieldMetadata,
nameSingular: targetFlatObjectMetadata.nameSingular,
namePlural: targetFlatObjectMetadata.namePlural,
});
return fromMorphOrRelationFlatFieldMetadataToRelationDto({
sourceFlatFieldMetadata: {
...sourceFlatFieldMetadata,
name: morphNameFromMorphFieldMetadataName,
},
targetFlatFieldMetadata,
targetFlatObjectMetadata,
sourceFlatObjectMetadata,
});
}),
);
}
return relationDtos;
flatFieldMetadataMaps,
flatObjectMetadataMaps,
});
});
},
);
}
@@ -0,0 +1,73 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/relation.dto';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { findAllOthersMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-all-others-morph-relation-flat-field-metadatas-or-throw.util';
import { fromMorphOrRelationFlatFieldMetadataToRelationDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-morph-or-relation-flat-field-metadata-to-relation-dto.util';
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { getMorphNameFromMorphFieldMetadataName } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-morph-name-from-morph-field-metadata-name.util';
type ResolveMorphRelationsFromFlatFieldMetadataArgs = {
morphFlatFieldMetadata: FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>;
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
};
export const resolveMorphRelationsFromFlatFieldMetadata = ({
morphFlatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadataMaps,
}: ResolveMorphRelationsFromFlatFieldMetadataArgs): RelationDTO[] => {
const sourceFlatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityMaps: flatObjectMetadataMaps,
flatEntityId: morphFlatFieldMetadata.objectMetadataId,
});
const relatedMorphFlatFieldMetadatas =
findAllOthersMorphRelationFlatFieldMetadatasOrThrow({
flatFieldMetadata: morphFlatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadata: sourceFlatObjectMetadata,
});
const allMorphFlatFieldMetadatas = [
morphFlatFieldMetadata,
...relatedMorphFlatFieldMetadatas,
];
return allMorphFlatFieldMetadatas.flatMap((sourceFlatFieldMetadata) => {
const targetFlatFieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: sourceFlatFieldMetadata.relationTargetFieldMetadataId,
flatEntityMaps: flatFieldMetadataMaps,
});
if (!isMorphOrRelationFlatFieldMetadata(targetFlatFieldMetadata)) {
return [];
}
const targetFlatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: sourceFlatFieldMetadata.relationTargetObjectMetadataId,
flatEntityMaps: flatObjectMetadataMaps,
});
const morphNameFromMorphFieldMetadataName =
getMorphNameFromMorphFieldMetadataName({
morphRelationFlatFieldMetadata: sourceFlatFieldMetadata,
nameSingular: targetFlatObjectMetadata.nameSingular,
namePlural: targetFlatObjectMetadata.namePlural,
});
return fromMorphOrRelationFlatFieldMetadataToRelationDto({
sourceFlatFieldMetadata: {
...sourceFlatFieldMetadata,
name: morphNameFromMorphFieldMetadataName,
},
targetFlatFieldMetadata,
targetFlatObjectMetadata,
sourceFlatObjectMetadata,
});
});
};
@@ -0,0 +1,88 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { pickMorphGroupSurvivor } from 'src/engine/dataloaders/utils/pick-morph-group-survivor.util';
import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/relation.dto';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { findAllOthersMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-all-others-morph-relation-flat-field-metadatas-or-throw.util';
import { fromMorphOrRelationFlatFieldMetadataToRelationDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-morph-or-relation-flat-field-metadata-to-relation-dto.util';
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { getMorphNameFromMorphFieldMetadataName } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-morph-name-from-morph-field-metadata-name.util';
type ResolveRelationFromFlatFieldMetadataArgs = {
sourceFlatFieldMetadata: FlatFieldMetadata<FieldMetadataType.RELATION>;
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
};
export const resolveRelationFromFlatFieldMetadata = ({
sourceFlatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadataMaps,
}: ResolveRelationFromFlatFieldMetadataArgs): RelationDTO | null => {
const sourceFlatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: sourceFlatFieldMetadata.objectMetadataId,
flatEntityMaps: flatObjectMetadataMaps,
});
const targetFlatFieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: sourceFlatFieldMetadata.relationTargetFieldMetadataId,
flatEntityMaps: flatFieldMetadataMaps,
});
if (!isMorphOrRelationFlatFieldMetadata(targetFlatFieldMetadata)) {
return null;
}
const targetFlatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: targetFlatFieldMetadata.objectMetadataId,
flatEntityMaps: flatObjectMetadataMaps,
});
if (
isFlatFieldMetadataOfType(
targetFlatFieldMetadata,
FieldMetadataType.MORPH_RELATION,
)
) {
const morphNameFromMorphFieldMetadataName =
getMorphNameFromMorphFieldMetadataName({
morphRelationFlatFieldMetadata: targetFlatFieldMetadata,
nameSingular: sourceFlatObjectMetadata.nameSingular,
namePlural: sourceFlatObjectMetadata.namePlural,
});
const allMorphFlatFieldMetadatas = [
targetFlatFieldMetadata,
...findAllOthersMorphRelationFlatFieldMetadatasOrThrow({
flatFieldMetadata: targetFlatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadata: targetFlatObjectMetadata,
}),
];
const survivorMorphField = pickMorphGroupSurvivor(
allMorphFlatFieldMetadatas,
);
return fromMorphOrRelationFlatFieldMetadataToRelationDto({
sourceFlatFieldMetadata,
sourceFlatObjectMetadata,
targetFlatFieldMetadata: {
...survivorMorphField,
name: morphNameFromMorphFieldMetadataName,
},
targetFlatObjectMetadata,
});
}
return fromMorphOrRelationFlatFieldMetadataToRelationDto({
sourceFlatFieldMetadata,
targetFlatFieldMetadata,
targetFlatObjectMetadata,
sourceFlatObjectMetadata,
});
};
@@ -12,15 +12,6 @@ export class ObjectRecordEventWithQueryIdsDTO {
objectRecordEvent: ObjectRecordEventDTO;
}
@ObjectType('MetadataEventWithQueryIds')
export class MetadataEventWithQueryIdsDTO {
@Field(() => [String])
queryIds: string[];
@Field(() => MetadataEventDTO)
metadataEvent: MetadataEventDTO;
}
@ObjectType('EventSubscription')
export class EventSubscriptionDTO {
@Field(() => String)
@@ -29,6 +20,6 @@ export class EventSubscriptionDTO {
@Field(() => [ObjectRecordEventWithQueryIdsDTO])
objectRecordEventsWithQueryIds: ObjectRecordEventWithQueryIdsDTO[];
@Field(() => [MetadataEventWithQueryIdsDTO])
metadataEventsWithQueryIds: MetadataEventWithQueryIdsDTO[];
@Field(() => [MetadataEventDTO])
metadataEvents: MetadataEventDTO[];
}
@@ -6,8 +6,5 @@ export type EventStreamPayload = {
queryIds: string[];
objectRecordEvent: ObjectRecordSubscriptionEvent;
}[];
metadataEventsWithQueryIds: {
queryIds: string[];
metadataEvent: MetadataEvent;
}[];
metadataEvents: MetadataEvent[];
};
@@ -0,0 +1,64 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
import { resolveMorphRelationsFromFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/resolve-morph-relations-from-flat-field-metadata.util';
import { resolveRelationFromFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/resolve-relation-from-flat-field-metadata.util';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
type EnrichFieldMetadataEventArgs = {
record: Record<string, unknown>;
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
};
export const enrichFieldMetadataEventWithRelations = ({
record,
flatFieldMetadataMaps,
flatObjectMetadataMaps,
}: EnrichFieldMetadataEventArgs): Record<string, unknown> => {
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
flatEntityId: record.id as string,
flatEntityMaps: flatFieldMetadataMaps,
});
if (!isDefined(flatFieldMetadata)) {
return record;
}
try {
if (
isFlatFieldMetadataOfType(flatFieldMetadata, FieldMetadataType.RELATION)
) {
const relation = resolveRelationFromFlatFieldMetadata({
sourceFlatFieldMetadata: flatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadataMaps,
});
return isDefined(relation) ? { ...record, relation } : record;
}
if (
isFlatFieldMetadataOfType(
flatFieldMetadata,
FieldMetadataType.MORPH_RELATION,
)
) {
const morphRelations = resolveMorphRelationsFromFlatFieldMetadata({
morphFlatFieldMetadata: flatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadataMaps,
});
return { ...record, morphRelations };
}
} catch {
return record;
}
return record;
};
@@ -90,7 +90,7 @@ export class WorkspaceEventEmitterResolver {
return {
eventStreamId: variables.eventStreamId,
objectRecordEventsWithQueryIds: payload.objectRecordEventsWithQueryIds,
metadataEventsWithQueryIds: payload.metadataEventsWithQueryIds,
metadataEvents: payload.metadataEvents,
};
},
})
@@ -143,7 +143,7 @@ export class WorkspaceEventEmitterResolver {
return wrapAsyncIteratorWithLifecycle(iterator, {
initialValue: {
objectRecordEventsWithQueryIds: [],
metadataEventsWithQueryIds: [],
metadataEvents: [],
},
onHeartbeat: () =>
this.eventStreamService.refreshEventStreamTTL({
@@ -31,6 +31,7 @@ import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/typ
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { enrichFieldMetadataEventWithRelations } from 'src/engine/workspace-event-emitter/utils/enrich-field-metadata-event-with-relations.util';
import { UserWorkspaceRoleMap } from 'src/engine/metadata-modules/role-target/types/user-workspace-role-map';
import { type FlatRowLevelPermissionPredicateGroupMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type';
import { type FlatRowLevelPermissionPredicateMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type';
@@ -130,23 +131,29 @@ export class WorkspaceEventEmitterService {
? await this.fetchObjectRecordStreamContext(workspaceId)
: undefined;
const enrichedMetadataEventBatch = isMetadata
? await this.enrichFieldMetadataEventsWithRelations(
eventBatch as MetadataEventBatch,
)
: undefined;
for (const [streamChannelId, streamData] of streamsData) {
if (!isDefined(streamData)) {
streamIdsToRemove.push(streamChannelId);
continue;
}
if (Object.keys(streamData.queries).length === 0) {
continue;
}
if (isMetadata) {
if (isMetadata && isDefined(enrichedMetadataEventBatch)) {
await this.processMetadataStreamEvents(
streamChannelId,
streamData,
eventBatch as MetadataEventBatch,
enrichedMetadataEventBatch,
);
} else {
} else if (!isMetadata) {
if (Object.keys(streamData.queries).length === 0) {
continue;
}
if (!isDefined(objectRecordStreamContext)) {
continue;
}
@@ -186,19 +193,14 @@ export class WorkspaceEventEmitterService {
return;
}
const metadataEventsWithQueryIds = metadataEventBatch.events.map(
(metadataEvent) => ({
queryIds: [] as string[],
metadataEvent: {
...metadataEvent,
updatedCollectionHash: metadataEventBatch.updatedCollectionHash,
},
}),
);
const metadataEvents = metadataEventBatch.events.map((metadataEvent) => ({
...metadataEvent,
updatedCollectionHash: metadataEventBatch.updatedCollectionHash,
}));
const payload: EventStreamPayload = {
objectRecordEventsWithQueryIds: [],
metadataEventsWithQueryIds,
metadataEvents,
};
await this.subscriptionService.publishToEventStream({
@@ -208,6 +210,47 @@ export class WorkspaceEventEmitterService {
});
}
private async enrichFieldMetadataEventsWithRelations(
metadataEventBatch: MetadataEventBatch,
): Promise<MetadataEventBatch> {
if (metadataEventBatch.metadataName !== 'fieldMetadata') {
return metadataEventBatch;
}
const { flatFieldMetadataMaps, flatObjectMetadataMaps } =
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
{
workspaceId: metadataEventBatch.workspaceId,
flatMapsKeys: ['flatFieldMetadataMaps', 'flatObjectMetadataMaps'],
},
);
const enrichedEvents = metadataEventBatch.events.map((event) => {
if (
!('after' in event.properties) ||
!isDefined(event.properties.after)
) {
return event;
}
const enrichedAfter = enrichFieldMetadataEventWithRelations({
record: event.properties.after as Record<string, unknown>,
flatFieldMetadataMaps,
flatObjectMetadataMaps,
});
return {
...event,
properties: {
...event.properties,
after: enrichedAfter,
},
} as typeof event;
});
return { ...metadataEventBatch, events: enrichedEvents };
}
private async processObjectRecordStreamEvents(
streamChannelId: string,
streamData: EventStreamData,
@@ -315,7 +358,7 @@ export class WorkspaceEventEmitterService {
const payload: EventStreamPayload = {
objectRecordEventsWithQueryIds: matchedEvents,
metadataEventsWithQueryIds: [],
metadataEvents: [],
};
await this.subscriptionService.publishToEventStream({