Skip to content

Commit 0d6c9c8

Browse files
committed
Add some tests for relation-query-builder
1 parent 62f9d12 commit 0d6c9c8

File tree

7 files changed

+107
-66
lines changed

7 files changed

+107
-66
lines changed

packages/query-typeorm/__tests__/__fixtures__/seeds.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,21 @@ export const TEST_RELATIONS: TestRelation[] = TEST_ENTITIES.reduce(
3131
relationName: `${te.stringType}-test-relation-one`,
3232
testEntityId: te.testEntityPk,
3333
uniDirectionalTestEntityId: te.testEntityPk,
34+
numberType: 42,
3435
},
3536
{
3637
testRelationPk: `test-relations-${te.testEntityPk}-2`,
3738
relationName: `${te.stringType}-test-relation-two`,
3839
testEntityId: te.testEntityPk,
3940
uniDirectionalTestEntityId: te.testEntityPk,
41+
numberType: 42,
4042
},
4143
{
4244
testRelationPk: `test-relations-${te.testEntityPk}-3`,
4345
relationName: `${te.stringType}-test-relation-three`,
4446
testEntityId: te.testEntityPk,
4547
uniDirectionalTestEntityId: te.testEntityPk,
48+
numberType: 42,
4649
},
4750
],
4851
[] as TestRelation[],

packages/query-typeorm/__tests__/__fixtures__/test-relation.entity.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ export class TestRelation {
1717
@Column({ name: 'uni_directional_test_entity_id', nullable: true })
1818
uniDirectionalTestEntityId?: string;
1919

20+
@Column({ name: 'number_type' })
21+
numberType?: number;
22+
2023
@ManyToOne(() => TestEntity, (te) => te.testRelations, { onDelete: 'CASCADE' })
2124
@JoinColumn({ name: 'test_entity_id' })
2225
testEntity?: TestEntity;

packages/query-typeorm/__tests__/query/__snapshots__/relation-query.builder.spec.ts.snap

Lines changed: 23 additions & 14 deletions
Large diffs are not rendered by default.

packages/query-typeorm/__tests__/query/relation-query.builder.spec.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import { Class, Query, SortDirection, SortNulls } from '@nestjs-query/core';
2+
import { RelationQueryBuilder } from '../../src/query';
23
import { closeTestConnection, createTestConnection, getTestConnection } from '../__fixtures__/connection.fixture';
34
import { TestRelation } from '../__fixtures__/test-relation.entity';
45
import { TestEntity } from '../__fixtures__/test.entity';
5-
import { RelationQueryBuilder } from '../../src/query';
6+
import { getCustomFilterRegistry } from '../utils';
67

78
describe('RelationQueryBuilder', (): void => {
89
beforeEach(createTestConnection);
910
afterEach(closeTestConnection);
1011

12+
const customFilterRegistry = getCustomFilterRegistry();
13+
1114
const getRelationQueryBuilder = <Entity, Relation>(
1215
EntityClass: Class<Entity>,
1316
relationName: string,
1417
): RelationQueryBuilder<Entity, Relation> =>
15-
new RelationQueryBuilder(getTestConnection().getRepository(EntityClass), relationName);
18+
new RelationQueryBuilder(getTestConnection().getRepository(EntityClass), relationName, { customFilterRegistry });
1619

1720
const expectSQLSnapshot = <Entity, Relation>(
1821
EntityClass: Class<Entity>,
@@ -161,5 +164,12 @@ describe('RelationQueryBuilder', (): void => {
161164
});
162165
});
163166
});
167+
describe('with custom filters', () => {
168+
// TODO Fix typings to avoid usage of any
169+
it('should accept custom filters', (): void => {
170+
const query: Query<TestRelation> = { filter: { numberType: { isMultipleOf: 5 } } } as any;
171+
expectSQLSnapshot(TestEntity, testEntity, 'testEntityRelation', query);
172+
});
173+
});
164174
});
165175
});

packages/query-typeorm/__tests__/query/where.builder.spec.ts

Lines changed: 3 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Filter } from '@nestjs-query/core';
2-
import { randomString } from '../../src/common';
3-
import { CustomFilterRegistry, CustomFilterResult, WhereBuilder } from '../../src/query';
2+
import { WhereBuilder } from '../../src/query';
43
import { closeTestConnection, createTestConnection, getTestConnection } from '../__fixtures__/connection.fixture';
54
import { TestEntity } from '../__fixtures__/test.entity';
5+
import { getCustomFilterRegistry } from '../utils';
66

77
describe('WhereBuilder', (): void => {
88
beforeEach(createTestConnection);
@@ -12,51 +12,7 @@ describe('WhereBuilder', (): void => {
1212
const getQueryBuilder = () => getRepo().createQueryBuilder();
1313
const createWhereBuilder = () => new WhereBuilder<TestEntity>();
1414

15-
const customFilterRegistry = new CustomFilterRegistry();
16-
// Test for (operation) filter registration (this is valid for all fields of all entities)
17-
customFilterRegistry.setFilter(
18-
{ operation: 'isMultipleOf' },
19-
{
20-
apply(field, cmp, val: number, alias): CustomFilterResult {
21-
alias = alias ? alias : '';
22-
const pname = `param${randomString()}`;
23-
return {
24-
sql: `("${alias}"."${field}" % :${pname}) == 0`,
25-
params: { [pname]: val },
26-
};
27-
},
28-
},
29-
);
30-
// Test for (class, field, operation) filter overriding the previous operation filter on a specific field
31-
customFilterRegistry.setFilter(
32-
{ operation: 'isMultipleOf', Entity: TestEntity, field: 'dateType' },
33-
{
34-
apply(field, cmp, val: number, alias): CustomFilterResult {
35-
alias = alias ? alias : '';
36-
const pname = `param${randomString()}`;
37-
return {
38-
sql: `(EXTRACT(EPOCH FROM "${alias}"."${field}") / 3600 / 24) % :${pname}) == 0`,
39-
params: { [pname]: val },
40-
};
41-
},
42-
},
43-
);
44-
// Test for (class, field, operation) filter on a virtual property 'fakePointType' that does not really exist on the entity
45-
customFilterRegistry.setFilter(
46-
{ operation: 'distanceFrom', Entity: TestEntity, field: 'fakePointType' },
47-
{
48-
apply(field, cmp, val: { point: { lat: number; lng: number }; radius: number }, alias): CustomFilterResult {
49-
alias = alias ? alias : '';
50-
const plat = `param${randomString()}`;
51-
const plng = `param${randomString()}`;
52-
const prad = `param${randomString()}`;
53-
return {
54-
sql: `ST_Distance("${alias}"."${field}", ST_MakePoint(:${plat},:${plng})) <= :${prad}`,
55-
params: { [plat]: val.point.lat, [plng]: val.point.lng, [prad]: val.radius },
56-
};
57-
},
58-
},
59-
);
15+
const customFilterRegistry = getCustomFilterRegistry();
6016

6117
const expectSQLSnapshot = (filter: Filter<TestEntity>): void => {
6218
const selectQueryBuilder = createWhereBuilder().build(
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { randomString } from '../src/common';
2+
import { CustomFilterRegistry, CustomFilterResult } from '../src/query';
3+
import { TestEntity } from './__fixtures__/test.entity';
4+
5+
export function getCustomFilterRegistry(): CustomFilterRegistry {
6+
const customFilterRegistry = new CustomFilterRegistry();
7+
// Test for (operation) filter registration (this is valid for all fields of all entities)
8+
customFilterRegistry.setFilter(
9+
{ operation: 'isMultipleOf' },
10+
{
11+
apply(field, cmp, val: number, alias): CustomFilterResult {
12+
alias = alias ? alias : '';
13+
const pname = `param${randomString()}`;
14+
return {
15+
sql: `("${alias}"."${field}" % :${pname}) == 0`,
16+
params: { [pname]: val },
17+
};
18+
},
19+
},
20+
);
21+
// Test for (class, field, operation) filter overriding the previous operation filter on a specific field
22+
customFilterRegistry.setFilter(
23+
{ operation: 'isMultipleOf', Entity: TestEntity, field: 'dateType' },
24+
{
25+
apply(field, cmp, val: number, alias): CustomFilterResult {
26+
alias = alias ? alias : '';
27+
const pname = `param${randomString()}`;
28+
return {
29+
sql: `(EXTRACT(EPOCH FROM "${alias}"."${field}") / 3600 / 24) % :${pname}) == 0`,
30+
params: { [pname]: val },
31+
};
32+
},
33+
},
34+
);
35+
// Test for (class, field, operation) filter on a virtual property 'fakePointType' that does not really exist on the entity
36+
customFilterRegistry.setFilter(
37+
{ operation: 'distanceFrom', Entity: TestEntity, field: 'fakePointType' },
38+
{
39+
apply(field, cmp, val: { point: { lat: number; lng: number }; radius: number }, alias): CustomFilterResult {
40+
alias = alias ? alias : '';
41+
const plat = `param${randomString()}`;
42+
const plng = `param${randomString()}`;
43+
const prad = `param${randomString()}`;
44+
return {
45+
sql: `ST_Distance("${alias}"."${field}", ST_MakePoint(:${plat},:${plng})) <= :${prad}`,
46+
params: { [plat]: val.point.lat, [plng]: val.point.lng, [prad]: val.radius },
47+
};
48+
},
49+
},
50+
);
51+
return customFilterRegistry;
52+
}

packages/query-typeorm/src/query/relation-query.builder.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { AggregateQuery, Class, Query } from '@nestjs-query/core';
33
import { Repository, SelectQueryBuilder, ObjectLiteral, Brackets } from 'typeorm';
44
import { RelationMetadata } from 'typeorm/metadata/RelationMetadata';
55
import { DriverUtils } from 'typeorm/driver/DriverUtils';
6+
import { CustomFilterRegistry } from './custom-filter.registry';
67
import { FilterQueryBuilder } from './filter-query.builder';
78
import { AggregateBuilder } from './aggregate.builder';
89

@@ -64,10 +65,17 @@ export class RelationQueryBuilder<Entity, Relation> {
6465

6566
private paramCount: number;
6667

67-
constructor(readonly repo: Repository<Entity>, readonly relation: string) {
68+
private readonly customFilterRegistry?: CustomFilterRegistry;
69+
70+
constructor(
71+
readonly repo: Repository<Entity>,
72+
readonly relation: string,
73+
readonly opts?: { customFilterRegistry?: CustomFilterRegistry },
74+
) {
6875
this.relationRepo = this.repo.manager.getRepository<Relation>(this.relationMeta.from);
6976
this.filterQueryBuilder = new FilterQueryBuilder<Relation>(this.relationRepo);
7077
this.paramCount = 0;
78+
this.customFilterRegistry = opts?.customFilterRegistry;
7179
}
7280

7381
select(entity: Entity, query: Query<Relation>): SelectQueryBuilder<Relation> {
@@ -83,7 +91,7 @@ export class RelationQueryBuilder<Entity, Relation> {
8391
relationBuilder = this.filterQueryBuilder.applyFilter(
8492
relationBuilder,
8593
klass,
86-
undefined,
94+
this.customFilterRegistry,
8795
query.filter,
8896
relationBuilder.alias,
8997
);
@@ -132,7 +140,7 @@ export class RelationQueryBuilder<Entity, Relation> {
132140
relationBuilder = this.filterQueryBuilder.applyFilter(
133141
relationBuilder,
134142
klass,
135-
undefined,
143+
this.customFilterRegistry,
136144
query.filter,
137145
relationBuilder.alias,
138146
);

0 commit comments

Comments
 (0)