mirror of
				https://github.com/lingble/twenty.git
				synced 2025-10-31 12:47:58 +00:00 
			
		
		
		
	Fix CSV export missing last page (#7167)
This commit is contained in:
		| @@ -142,10 +142,6 @@ export const useTableData = ({ | |||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     const MAXIMUM_REQUESTS = isDefined(totalCount) |  | ||||||
|       ? Math.min(maximumRequests, totalCount / pageSize) |  | ||||||
|       : maximumRequests; |  | ||||||
|  |  | ||||||
|     const fetchNextPage = async () => { |     const fetchNextPage = async () => { | ||||||
|       setInflight(true); |       setInflight(true); | ||||||
|       setPreviousRecordCount(records.length); |       setPreviousRecordCount(records.length); | ||||||
| @@ -167,8 +163,8 @@ export const useTableData = ({ | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ( |     if ( | ||||||
|       pageCount >= MAXIMUM_REQUESTS || |       pageCount >= maximumRequests || | ||||||
|       (isDefined(totalCount) && records.length === totalCount) |       (isDefined(totalCount) && records.length >= totalCount) | ||||||
|     ) { |     ) { | ||||||
|       setPageCount(0); |       setPageCount(0); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,43 +0,0 @@ | |||||||
| import { FindOperator, Not } from 'typeorm'; |  | ||||||
|  |  | ||||||
| import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; |  | ||||||
|  |  | ||||||
| import { GraphqlQueryFilterFieldParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/graphql-query-filter-field.parser'; |  | ||||||
| import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; |  | ||||||
|  |  | ||||||
| describe('GraphqlQueryFilterFieldParser', () => { |  | ||||||
|   let parser: GraphqlQueryFilterFieldParser; |  | ||||||
|   let mockFieldMetadataMap: Record<string, FieldMetadataInterface>; |  | ||||||
|  |  | ||||||
|   beforeEach(() => { |  | ||||||
|     mockFieldMetadataMap = { |  | ||||||
|       simpleField: { |  | ||||||
|         id: '1', |  | ||||||
|         name: 'simpleField', |  | ||||||
|         type: FieldMetadataType.TEXT, |  | ||||||
|         label: 'Simple Field', |  | ||||||
|         objectMetadataId: 'obj1', |  | ||||||
|       }, |  | ||||||
|     }; |  | ||||||
|     parser = new GraphqlQueryFilterFieldParser(mockFieldMetadataMap); |  | ||||||
|   }); |  | ||||||
|   it('should parse simple field correctly', () => { |  | ||||||
|     const result = parser.parse('simpleField', 'value', false); |  | ||||||
|  |  | ||||||
|     expect(result).toEqual({ simpleField: 'value' }); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   it('should negate simple field correctly', () => { |  | ||||||
|     const result = parser.parse('simpleField', 'value', true); |  | ||||||
|  |  | ||||||
|     expect(result).toEqual({ simpleField: Not('value') }); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   it('should parse object value using operator parser', () => { |  | ||||||
|     const result = parser.parse('simpleField', { like: '%value%' }, false); |  | ||||||
|  |  | ||||||
|     expect(result).toEqual({ |  | ||||||
|       simpleField: new FindOperator('like', '%%value%%'), |  | ||||||
|     }); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
| @@ -1,131 +0,0 @@ | |||||||
| import { |  | ||||||
|   FindOperator, |  | ||||||
|   ILike, |  | ||||||
|   In, |  | ||||||
|   IsNull, |  | ||||||
|   LessThan, |  | ||||||
|   LessThanOrEqual, |  | ||||||
|   Like, |  | ||||||
|   MoreThan, |  | ||||||
|   MoreThanOrEqual, |  | ||||||
|   Not, |  | ||||||
| } from 'typeorm'; |  | ||||||
|  |  | ||||||
| import { GraphqlQueryRunnerException } from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception'; |  | ||||||
| import { GraphqlQueryFilterOperatorParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/graphql-query-filter-operator.parser'; |  | ||||||
|  |  | ||||||
| describe('GraphqlQueryFilterOperatorParser', () => { |  | ||||||
|   let parser: GraphqlQueryFilterOperatorParser; |  | ||||||
|  |  | ||||||
|   beforeEach(() => { |  | ||||||
|     parser = new GraphqlQueryFilterOperatorParser(); |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   describe('parseOperator', () => { |  | ||||||
|     it('should parse eq operator correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ eq: 'value' }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBe('value'); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse neq operator correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ neq: 'value' }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(Not('value')); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse gt operator correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ gt: 5 }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(MoreThan(5)); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse gte operator correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ gte: 5 }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(MoreThanOrEqual(5)); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse lt operator correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ lt: 5 }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(LessThan(5)); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse lte operator correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ lte: 5 }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(LessThanOrEqual(5)); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse in operator correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ in: [1, 2, 3] }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(In([1, 2, 3])); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse is operator with NULL correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ is: 'NULL' }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(IsNull()); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse is operator with non-NULL value correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ is: 'NOT_NULL' }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(Not(IsNull())); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse like operator correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ like: 'test' }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(Like('%test%')); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse ilike operator correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ ilike: 'test' }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(ILike('%test%')); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse startsWith operator correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ startsWith: 'test' }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(ILike('test%')); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should parse endsWith operator correctly', () => { |  | ||||||
|       const result = parser.parseOperator({ endsWith: 'test' }, false); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(ILike('%test')); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should negate the operator when isNegated is true', () => { |  | ||||||
|       const result = parser.parseOperator({ eq: 'value' }, true); |  | ||||||
|  |  | ||||||
|       expect(result).toBeInstanceOf(FindOperator); |  | ||||||
|       expect(result).toEqual(Not('value')); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     it('should throw an exception for unsupported operator', () => { |  | ||||||
|       expect(() => |  | ||||||
|         parser.parseOperator({ unsupported: 'value' }, false), |  | ||||||
|       ).toThrow(GraphqlQueryRunnerException); |  | ||||||
|       expect(() => |  | ||||||
|         parser.parseOperator({ unsupported: 'value' }, false), |  | ||||||
|       ).toThrow('Operator "unsupported" is not supported'); |  | ||||||
|     }); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | import { isArray } from 'class-validator'; | ||||||
| import { ObjectLiteral, WhereExpressionBuilder } from 'typeorm'; | import { ObjectLiteral, WhereExpressionBuilder } from 'typeorm'; | ||||||
|  |  | ||||||
| import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; | import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; | ||||||
| @@ -48,6 +49,10 @@ export class GraphqlQueryFilterFieldParser { | |||||||
|     } |     } | ||||||
|     const [[operator, value]] = Object.entries(filterValue); |     const [[operator, value]] = Object.entries(filterValue); | ||||||
|  |  | ||||||
|  |     if (operator === 'in' && (!isArray(value) || value.length === 0)) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const { sql, params } = this.computeWhereConditionParts( |     const { sql, params } = this.computeWhereConditionParts( | ||||||
|       fieldMetadata, |       fieldMetadata, | ||||||
|       operator, |       operator, | ||||||
|   | |||||||
| @@ -1,50 +0,0 @@ | |||||||
| import { OrderByDirection } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface'; |  | ||||||
|  |  | ||||||
| import { GraphqlQueryOrderFieldParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-order/graphql-query-order.parser'; |  | ||||||
| import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; |  | ||||||
| import { FieldMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util'; |  | ||||||
|  |  | ||||||
| describe('GraphqlQueryOrderFieldParser', () => { |  | ||||||
|   let parser: GraphqlQueryOrderFieldParser; |  | ||||||
|   const fieldMetadataMap: FieldMetadataMap = {}; |  | ||||||
|  |  | ||||||
|   beforeEach(() => { |  | ||||||
|     fieldMetadataMap['name'] = { |  | ||||||
|       id: 'name-id', |  | ||||||
|       name: 'name', |  | ||||||
|       type: FieldMetadataType.TEXT, |  | ||||||
|       label: 'Name', |  | ||||||
|       objectMetadataId: 'object-id', |  | ||||||
|     }; |  | ||||||
|     fieldMetadataMap['age'] = { |  | ||||||
|       id: 'age-id', |  | ||||||
|       name: 'age', |  | ||||||
|       type: FieldMetadataType.NUMBER, |  | ||||||
|       label: 'Age', |  | ||||||
|       objectMetadataId: 'object-id', |  | ||||||
|     }; |  | ||||||
|     fieldMetadataMap['address'] = { |  | ||||||
|       id: 'address-id', |  | ||||||
|       name: 'address', |  | ||||||
|       type: FieldMetadataType.ADDRESS, |  | ||||||
|       label: 'Address', |  | ||||||
|       objectMetadataId: 'object-id', |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     parser = new GraphqlQueryOrderFieldParser(fieldMetadataMap); |  | ||||||
|   }); |  | ||||||
|   describe('parse', () => { |  | ||||||
|     it('should parse simple order by fields', () => { |  | ||||||
|       const orderBy = [ |  | ||||||
|         { name: OrderByDirection.AscNullsFirst }, |  | ||||||
|         { age: OrderByDirection.DescNullsLast }, |  | ||||||
|       ]; |  | ||||||
|       const result = parser.parse(orderBy); |  | ||||||
|  |  | ||||||
|       expect(result).toEqual({ |  | ||||||
|         name: { direction: 'ASC', nulls: 'FIRST' }, |  | ||||||
|         age: { direction: 'DESC', nulls: 'LAST' }, |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
		Reference in New Issue
	
	Block a user
	 Charles Bochet
					Charles Bochet