Files
twenty/packages/twenty-server/src/engine/twenty-orm/utils/compute-event-select-query-builder.util.ts
T
Etienne 300738e8cb Fix database event emission (#16759)
Fixes https://github.com/twentyhq/private-issues/issues/395


When calling `removeUserFromWorkspaceAndPotentiallyDeleteWorkspace` from
`user.service`,
```
        await workspaceMemberRepository.delete({
          userId: userWorkspace.userId,
        });
```
related database event is not emitted.

Same issue with update has been fixed
[here](https://github.com/twentyhq/twenty/pull/13287/changes)

The database event is not emitted because `await
eventSelectQueryBuilder.getOne()` in `workspace-delete-query-builder`
returns `null`. This happens because the `selectQueryBuilder` has no
entity—the database request is sent and the raw result is not `null`,
but the entity is `null`, causing the final result to be null.

It can be fixed the same way update has been fixed, updating the
`workspace-entity-manager`
- Pros : consistant with update but we should not forget to fix
softDelete and restore
- Cons : `workspace-entity-manager` is a copy of typeORM logic +
permission injection. Should it be more ?
 
Alternatively (as featured in this PR), it can be fixed by updating
computeEventSelectQueryBuilder, inspired by TypeORM's logic in
typeorm/query-builder/QueryBuilder.js at line 67.
- Pros : it fit with typeORM logic + It fixes all repository operations
2026-01-02 14:23:47 +01:00

60 lines
2.5 KiB
TypeScript

import { type ObjectsPermissions } from 'twenty-shared/types';
import { type ObjectLiteral } from 'typeorm';
import { type QueryExpressionMap } from 'typeorm/query-builder/QueryExpressionMap';
import { type FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { type WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-delete-query-builder';
import { type WorkspaceInsertQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-insert-query-builder';
import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
import { type WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder';
import { type WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder';
type EventSelectQueryBuilderFactoryArgs<T extends ObjectLiteral> = {
queryBuilder:
| WorkspaceUpdateQueryBuilder<T>
| WorkspaceSoftDeleteQueryBuilder<T>
| WorkspaceDeleteQueryBuilder<T>
| WorkspaceInsertQueryBuilder<T>;
authContext: AuthContext;
internalContext: WorkspaceInternalContext;
featureFlagMap: FeatureFlagMap;
expressionMap: QueryExpressionMap;
objectRecordsPermissions: ObjectsPermissions;
};
export const computeEventSelectQueryBuilder = <T extends ObjectLiteral>({
queryBuilder,
authContext,
featureFlagMap,
internalContext,
expressionMap,
objectRecordsPermissions,
}: EventSelectQueryBuilderFactoryArgs<T>): WorkspaceSelectQueryBuilder<T> => {
const eventSelectQueryBuilder = new WorkspaceSelectQueryBuilder(
queryBuilder as unknown as WorkspaceSelectQueryBuilder<T>,
objectRecordsPermissions,
internalContext,
true,
authContext,
featureFlagMap,
);
eventSelectQueryBuilder.expressionMap.wheres = expressionMap.wheres;
eventSelectQueryBuilder.expressionMap.aliases = expressionMap.aliases;
eventSelectQueryBuilder.setParameters(expressionMap.parameters);
if (
eventSelectQueryBuilder.expressionMap.selects.length === 0 &&
eventSelectQueryBuilder.expressionMap.mainAlias
) {
eventSelectQueryBuilder.expressionMap.selects = [
{ selection: eventSelectQueryBuilder.expressionMap.mainAlias.name },
];
}
return eventSelectQueryBuilder;
};