Fix optimistic effect deletedAt (#7606)

In this PR, I'm fixing part of the impact of soft deletion on optimistic
rendering.

## Backend Vision

1) Backend endpoints will not return soft deleted records (having
deletedAt set) by default. To get the softDeleted records, we will pass
a { withSoftDelete: true } additional param in the query.
2) Record relations will NEVER contain softDeleted relations

## Backend current state

Right now, we have the following behavior:
- if the query filters do not mention deletedAt, we don't return
softDeletedRecords
- if the query filters mention deletedAt, we take it into consideration.
Meaning that if we want to have the softDeleted records in any way we
need to do { or: [ deletedAt: NULL, deletedAt: NOT_NULL] }

## Optimistic rendering strategy

1) useDestroyOne/Many is triggering destroyOptimisticEffects (previously
deleteOptimisticEffects)
2) UseDeleteOne/Many and useRestoreOne/Many are actually triggering
updateOptimisticEffects (as they only update deletedAt field) AND we
need updateOptimisticEffects to take into account deletedAt (future
withSoftDelete: true) filter.
This commit is contained in:
Charles Bochet
2024-10-11 20:23:01 +02:00
committed by GitHub
parent d350143c92
commit 7b96be6f8c
21 changed files with 406 additions and 256 deletions

View File

@ -82,62 +82,62 @@ export class GraphqlQueryFilterFieldParser {
switch (operator) {
case 'eq':
return {
sql: `${objectNameSingular}.${key} = :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}" = :${key}${uuid}`,
params: { [`${key}${uuid}`]: value },
};
case 'neq':
return {
sql: `${objectNameSingular}.${key} != :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}" != :${key}${uuid}`,
params: { [`${key}${uuid}`]: value },
};
case 'gt':
return {
sql: `${objectNameSingular}.${key} > :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}" > :${key}${uuid}`,
params: { [`${key}${uuid}`]: value },
};
case 'gte':
return {
sql: `${objectNameSingular}.${key} >= :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}" >= :${key}${uuid}`,
params: { [`${key}${uuid}`]: value },
};
case 'lt':
return {
sql: `${objectNameSingular}.${key} < :${key}${uuid}`,
sql: `"${objectNameSingular}".${key} < :${key}${uuid}`,
params: { [`${key}${uuid}`]: value },
};
case 'lte':
return {
sql: `${objectNameSingular}.${key} <= :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}" <= :${key}${uuid}`,
params: { [`${key}${uuid}`]: value },
};
case 'in':
return {
sql: `${objectNameSingular}.${key} IN (:...${key}${uuid})`,
sql: `"${objectNameSingular}"."${key}" IN (:...${key}${uuid})`,
params: { [`${key}${uuid}`]: value },
};
case 'is':
return {
sql: `${objectNameSingular}.${key} IS ${value === 'NULL' ? 'NULL' : 'NOT NULL'}`,
sql: `"${objectNameSingular}"."${key}" IS ${value === 'NULL' ? 'NULL' : 'NOT NULL'}`,
params: {},
};
case 'like':
return {
sql: `${objectNameSingular}.${key} LIKE :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
params: { [`${key}${uuid}`]: `${value}` },
};
case 'ilike':
return {
sql: `${objectNameSingular}.${key} ILIKE :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}" ILIKE :${key}${uuid}`,
params: { [`${key}${uuid}`]: `${value}` },
};
case 'startsWith':
return {
sql: `${objectNameSingular}.${key} LIKE :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
params: { [`${key}${uuid}`]: `${value}` },
};
case 'endsWith':
return {
sql: `${objectNameSingular}.${key} LIKE :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
params: { [`${key}${uuid}`]: `${value}` },
};
default:

View File

@ -58,7 +58,7 @@ export class GraphqlQueryDestroyOneResolverService
);
const nonFormattedDeletedObjectRecords = await queryBuilder
.where({
.where(`"${objectMetadataMapItem.nameSingular}".id = :id`, {
id: args.id,
})
.take(1)

View File

@ -16,6 +16,7 @@ import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/obj
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
@Injectable()
export class GraphqlQueryUpdateManyResolverService
@ -57,9 +58,14 @@ export class GraphqlQueryUpdateManyResolverService
objectMetadataMapItem.nameSingular,
);
const tableName = computeTableName(
objectMetadataMapItem.nameSingular,
objectMetadataMapItem.isCustom,
);
const withFilterQueryBuilder = graphqlQueryParser.applyFilterToBuilder(
queryBuilder,
objectMetadataMapItem.nameSingular,
tableName,
args.filter,
);