diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts index 68db25391..fc1be7e98 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser.ts @@ -30,16 +30,48 @@ export class GraphqlQueryParser { this.fieldMetadataMap = fieldMetadataMap; } - parseFilter( - recordFilter: RecordFilter, - ): FindOptionsWhere | FindOptionsWhere[] { + parseFilter(recordFilter: RecordFilter): { + parsedFilters: + | FindOptionsWhere + | FindOptionsWhere[]; + withDeleted: boolean; + } { const graphqlQueryFilterParser = new GraphqlQueryFilterParser( this.fieldMetadataMap, ); const parsedFilter = graphqlQueryFilterParser.parse(recordFilter); - return parsedFilter; + const hasDeletedAtFilter = this.checkForDeletedAtFilter(parsedFilter); + + return { + parsedFilters: parsedFilter, + withDeleted: hasDeletedAtFilter, + }; + } + + private checkForDeletedAtFilter( + filter: FindOptionsWhere | FindOptionsWhere[], + ): boolean { + if (Array.isArray(filter)) { + return filter.some(this.checkForDeletedAtFilter); + } + + for (const [key, value] of Object.entries(filter)) { + if (key === 'deletedAt') { + return true; + } + + if (typeof value === 'object' && value !== null) { + if ( + this.checkForDeletedAtFilter(value as FindOptionsWhere) + ) { + return true; + } + } + } + + return false; } parseOrder( diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service.ts index f25e5688f..5608d5ed5 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service.ts @@ -79,7 +79,8 @@ export class GraphqlQueryFindManyResolverService { args.orderBy ?? [], isForwardPagination, ); - const where = graphqlQueryParser.parseFilter(args.filter ?? ({} as Filter)); + const { parsedFilters: where, withDeleted } = + graphqlQueryParser.parseFilter(args.filter ?? ({} as Filter)); const cursor = this.getCursor(args); const limit = args.first ?? args.last ?? QUERY_MAX_RECORDS; @@ -92,10 +93,11 @@ export class GraphqlQueryFindManyResolverService { order, select, take: limit + 1, + withDeleted, }; const totalCount = isDefined(selectedFields.totalCount) - ? await repository.count({ where }) + ? await repository.count({ where, withDeleted }) : 0; if (cursor) { diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service.ts index 4f478565e..c1609e0c0 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service.ts @@ -63,11 +63,13 @@ export class GraphqlQueryFindOneResolverService { objectMetadataItem, selectedFields, ); - const where = graphqlQueryParser.parseFilter(args.filter ?? ({} as Filter)); + const { parsedFilters: where, withDeleted } = + graphqlQueryParser.parseFilter(args.filter ?? ({} as Filter)); const objectRecord = (await repository.findOne({ where, select, + withDeleted, })) as ObjectRecord; const limit = QUERY_MAX_RECORDS; diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-index.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-index.decorator.ts index 5ede935f4..bf2e43201 100644 --- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-index.decorator.ts +++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-index.decorator.ts @@ -38,9 +38,9 @@ export function WorkspaceIndex( metadataArgsStorage.addIndexes({ name: `IDX_${generateDeterministicIndexName([ convertClassNameToObjectMetadataName(target.constructor.name), - propertyKey.toString(), + ...[propertyKey.toString(), 'deletedAt'], ])}`, - columns: [propertyKey.toString()], + columns: [propertyKey.toString(), 'deletedAt'], target: target.constructor, gate, }); diff --git a/packages/twenty-server/src/engine/twenty-orm/factories/entity-schema-column.factory.ts b/packages/twenty-server/src/engine/twenty-orm/factories/entity-schema-column.factory.ts index a72fe08c7..db67298f1 100644 --- a/packages/twenty-server/src/engine/twenty-orm/factories/entity-schema-column.factory.ts +++ b/packages/twenty-server/src/engine/twenty-orm/factories/entity-schema-column.factory.ts @@ -79,6 +79,7 @@ export class EntitySchemaColumnFactory { nullable: fieldMetadata.isNullable, createDate: key === 'createdAt', updateDate: key === 'updatedAt', + deleteDate: key === 'deletedAt', array: fieldMetadata.type === FieldMetadataType.MULTI_SELECT, default: defaultValue, };