-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(aggregations,relations): Add relation aggregation graphql enpoints
- Loading branch information
1 parent
db6ecb2
commit 56bb7e0
Showing
15 changed files
with
648 additions
and
5 deletions.
There are no files selected for viewing
169 changes: 169 additions & 0 deletions
169
packages/query-graphql/__tests__/loaders/aggregate-relations.loader.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import { AggregateQuery, QueryService } from '@nestjs-query/core'; | ||
import { mock, instance, when, deepEqual } from 'ts-mockito'; | ||
import { AggregateRelationsLoader } from '../../src/loader'; | ||
|
||
describe('AggregateRelationsLoader', () => { | ||
describe('createLoader', () => { | ||
class DTO { | ||
id!: string; | ||
} | ||
|
||
class RelationDTO { | ||
id!: string; | ||
} | ||
|
||
it('should return a function that accepts aggregate args', () => { | ||
const service = mock<QueryService<DTO>>(); | ||
const queryRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation'); | ||
expect(queryRelationsLoader.createLoader(instance(service))).toBeInstanceOf(Function); | ||
}); | ||
|
||
it('should try to load the relations with the query args', () => { | ||
const service = mock<QueryService<DTO>>(); | ||
const aggregateRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader( | ||
instance(service), | ||
); | ||
const filter = {}; | ||
const aggregate: AggregateQuery<RelationDTO> = { count: ['id'] }; | ||
const dtos = [{ id: 'dto-1' }, { id: 'dto-2' }]; | ||
const dto1Aggregate = { count: { id: 2 } }; | ||
const dto2Aggregate = { count: { id: 3 } }; | ||
when( | ||
service.aggregateRelations(RelationDTO, 'relation', deepEqual(dtos), deepEqual(filter), deepEqual(aggregate)), | ||
).thenResolve( | ||
new Map([ | ||
[dtos[0], dto1Aggregate], | ||
[dtos[1], dto2Aggregate], | ||
]), | ||
); | ||
return expect( | ||
aggregateRelationsLoader([ | ||
{ dto: dtos[0], filter, aggregate }, | ||
{ dto: dtos[1], filter, aggregate }, | ||
]), | ||
).resolves.toEqual([dto1Aggregate, dto2Aggregate]); | ||
}); | ||
|
||
it('should try return an empty aggregate result for each dto if no results are found', () => { | ||
const service = mock<QueryService<DTO>>(); | ||
const aggregateRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader( | ||
instance(service), | ||
); | ||
const filter = {}; | ||
const aggregate: AggregateQuery<RelationDTO> = { count: ['id'] }; | ||
const dtos = [{ id: 'dto-1' }, { id: 'dto-2' }]; | ||
const dto1Aggregate = { count: { id: 2 } }; | ||
when( | ||
service.aggregateRelations(RelationDTO, 'relation', deepEqual(dtos), deepEqual(filter), deepEqual(aggregate)), | ||
).thenResolve(new Map([[dtos[0], dto1Aggregate]])); | ||
return expect( | ||
aggregateRelationsLoader([ | ||
{ dto: dtos[0], filter, aggregate }, | ||
{ dto: dtos[1], filter, aggregate }, | ||
]), | ||
).resolves.toEqual([dto1Aggregate, {}]); | ||
}); | ||
|
||
it('should group queryRelations calls by filter and return in the correct order', () => { | ||
const service = mock<QueryService<DTO>>(); | ||
const queryRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader( | ||
instance(service), | ||
); | ||
const filter1 = { id: { gt: 'a' } }; | ||
const filter2 = {}; | ||
const aggregate: AggregateQuery<RelationDTO> = { count: ['id'] }; | ||
const dtos = [{ id: 'dto-1' }, { id: 'dto-2' }, { id: 'dto-3' }, { id: 'dto-4' }]; | ||
const dto1Aggregate = { count: { id: 2 } }; | ||
const dto2Aggregate = { count: { id: 3 } }; | ||
const dto3Aggregate = { count: { id: 4 } }; | ||
const dto4Aggregate = { count: { id: 5 } }; | ||
when( | ||
service.aggregateRelations( | ||
RelationDTO, | ||
'relation', | ||
deepEqual([dtos[0], dtos[2]]), | ||
deepEqual(filter1), | ||
deepEqual(aggregate), | ||
), | ||
).thenResolve( | ||
new Map([ | ||
[dtos[0], dto1Aggregate], | ||
[dtos[2], dto3Aggregate], | ||
]), | ||
); | ||
when( | ||
service.aggregateRelations( | ||
RelationDTO, | ||
'relation', | ||
deepEqual([dtos[1], dtos[3]]), | ||
deepEqual(filter2), | ||
deepEqual(aggregate), | ||
), | ||
).thenResolve( | ||
new Map([ | ||
[dtos[1], dto2Aggregate], | ||
[dtos[3], dto4Aggregate], | ||
]), | ||
); | ||
return expect( | ||
queryRelationsLoader([ | ||
{ dto: dtos[0], filter: filter1, aggregate }, | ||
{ dto: dtos[1], filter: filter2, aggregate }, | ||
{ dto: dtos[2], filter: filter1, aggregate }, | ||
{ dto: dtos[3], filter: filter2, aggregate }, | ||
]), | ||
).resolves.toEqual([dto1Aggregate, dto2Aggregate, dto3Aggregate, dto4Aggregate]); | ||
}); | ||
|
||
it('should group queryRelations calls by aggregate and return in the correct order', () => { | ||
const service = mock<QueryService<DTO>>(); | ||
const queryRelationsLoader = new AggregateRelationsLoader(RelationDTO, 'relation').createLoader( | ||
instance(service), | ||
); | ||
const filter = {}; | ||
const aggregate1: AggregateQuery<RelationDTO> = { count: ['id'] }; | ||
const aggregate2: AggregateQuery<RelationDTO> = { sum: ['id'] }; | ||
const dtos = [{ id: 'dto-1' }, { id: 'dto-2' }, { id: 'dto-3' }, { id: 'dto-4' }]; | ||
const dto1Aggregate = { count: { id: 2 } }; | ||
const dto2Aggregate = { sum: { id: 3 } }; | ||
const dto3Aggregate = { count: { id: 4 } }; | ||
const dto4Aggregate = { sum: { id: 5 } }; | ||
when( | ||
service.aggregateRelations( | ||
RelationDTO, | ||
'relation', | ||
deepEqual([dtos[0], dtos[2]]), | ||
deepEqual(filter), | ||
deepEqual(aggregate1), | ||
), | ||
).thenResolve( | ||
new Map([ | ||
[dtos[0], dto1Aggregate], | ||
[dtos[2], dto3Aggregate], | ||
]), | ||
); | ||
when( | ||
service.aggregateRelations( | ||
RelationDTO, | ||
'relation', | ||
deepEqual([dtos[1], dtos[3]]), | ||
deepEqual(filter), | ||
deepEqual(aggregate2), | ||
), | ||
).thenResolve( | ||
new Map([ | ||
[dtos[1], dto2Aggregate], | ||
[dtos[3], dto4Aggregate], | ||
]), | ||
); | ||
return expect( | ||
queryRelationsLoader([ | ||
{ dto: dtos[0], filter, aggregate: aggregate1 }, | ||
{ dto: dtos[1], filter, aggregate: aggregate2 }, | ||
{ dto: dtos[2], filter, aggregate: aggregate1 }, | ||
{ dto: dtos[3], filter, aggregate: aggregate2 }, | ||
]), | ||
).resolves.toEqual([dto1Aggregate, dto2Aggregate, dto3Aggregate, dto4Aggregate]); | ||
}); | ||
}); | ||
}); |
89 changes: 89 additions & 0 deletions
89
...esolvers/relations/__fixtures__/aggregate/aggregate-relation-custom-name.resolver.graphql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
type TestResolverDTO { | ||
id: ID! | ||
stringField: String! | ||
testsAggregate( | ||
"""Filter to find records to aggregate on""" | ||
filter: TestRelationDTOAggregateFilter | ||
): TestResolverDTOTestsAggregateResponse! | ||
} | ||
|
||
input TestRelationDTOAggregateFilter { | ||
and: [TestRelationDTOAggregateFilter!] | ||
or: [TestRelationDTOAggregateFilter!] | ||
id: IDFilterComparison | ||
testResolverId: StringFieldComparison | ||
} | ||
|
||
input IDFilterComparison { | ||
is: Boolean | ||
isNot: Boolean | ||
eq: ID | ||
neq: ID | ||
gt: ID | ||
gte: ID | ||
lt: ID | ||
lte: ID | ||
like: ID | ||
notLike: ID | ||
iLike: ID | ||
notILike: ID | ||
in: [ID!] | ||
notIn: [ID!] | ||
} | ||
|
||
input StringFieldComparison { | ||
is: Boolean | ||
isNot: Boolean | ||
eq: String | ||
neq: String | ||
gt: String | ||
gte: String | ||
lt: String | ||
lte: String | ||
like: String | ||
notLike: String | ||
iLike: String | ||
notILike: String | ||
in: [String!] | ||
notIn: [String!] | ||
} | ||
|
||
type TestResolverDTORelationsCountAggregate { | ||
id: Int | ||
testResolverId: Int | ||
} | ||
|
||
type TestResolverDTORelationsMinAggregate { | ||
id: ID | ||
testResolverId: String | ||
} | ||
|
||
type TestResolverDTORelationsMaxAggregate { | ||
id: ID | ||
testResolverId: String | ||
} | ||
|
||
type TestResolverDTOTestsCountAggregate { | ||
id: Int | ||
testResolverId: Int | ||
} | ||
|
||
type TestResolverDTOTestsMinAggregate { | ||
id: ID | ||
testResolverId: String | ||
} | ||
|
||
type TestResolverDTOTestsMaxAggregate { | ||
id: ID | ||
testResolverId: String | ||
} | ||
|
||
type TestResolverDTOTestsAggregateResponse { | ||
count: TestResolverDTOTestsCountAggregate | ||
min: TestResolverDTOTestsMinAggregate | ||
max: TestResolverDTOTestsMaxAggregate | ||
} | ||
|
||
type Query { | ||
test: TestResolverDTO! | ||
} |
38 changes: 38 additions & 0 deletions
38
..._/resolvers/relations/__fixtures__/aggregate/aggregate-relation-disabled.resolver.graphql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
type TestResolverDTO { | ||
id: ID! | ||
stringField: String! | ||
} | ||
|
||
type TestResolverDTORelationsCountAggregate { | ||
id: Int | ||
testResolverId: Int | ||
} | ||
|
||
type TestResolverDTORelationsMinAggregate { | ||
id: ID | ||
testResolverId: String | ||
} | ||
|
||
type TestResolverDTORelationsMaxAggregate { | ||
id: ID | ||
testResolverId: String | ||
} | ||
|
||
type TestResolverDTOTestsCountAggregate { | ||
id: Int | ||
testResolverId: Int | ||
} | ||
|
||
type TestResolverDTOTestsMinAggregate { | ||
id: ID | ||
testResolverId: String | ||
} | ||
|
||
type TestResolverDTOTestsMaxAggregate { | ||
id: ID | ||
testResolverId: String | ||
} | ||
|
||
type Query { | ||
test: TestResolverDTO! | ||
} |
8 changes: 8 additions & 0 deletions
8
...ts__/resolvers/relations/__fixtures__/aggregate/aggregate-relation-empty.resolver.graphql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
type TestResolverDTO { | ||
id: ID! | ||
stringField: String! | ||
} | ||
|
||
type Query { | ||
test: TestResolverDTO! | ||
} |
74 changes: 74 additions & 0 deletions
74
.../__tests__/resolvers/relations/__fixtures__/aggregate/aggregate-relation.resolver.graphql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
type TestResolverDTO { | ||
id: ID! | ||
stringField: String! | ||
relationsAggregate( | ||
"""Filter to find records to aggregate on""" | ||
filter: TestRelationDTOAggregateFilter | ||
): TestResolverDTORelationsAggregateResponse! | ||
} | ||
|
||
input TestRelationDTOAggregateFilter { | ||
and: [TestRelationDTOAggregateFilter!] | ||
or: [TestRelationDTOAggregateFilter!] | ||
id: IDFilterComparison | ||
testResolverId: StringFieldComparison | ||
} | ||
|
||
input IDFilterComparison { | ||
is: Boolean | ||
isNot: Boolean | ||
eq: ID | ||
neq: ID | ||
gt: ID | ||
gte: ID | ||
lt: ID | ||
lte: ID | ||
like: ID | ||
notLike: ID | ||
iLike: ID | ||
notILike: ID | ||
in: [ID!] | ||
notIn: [ID!] | ||
} | ||
|
||
input StringFieldComparison { | ||
is: Boolean | ||
isNot: Boolean | ||
eq: String | ||
neq: String | ||
gt: String | ||
gte: String | ||
lt: String | ||
lte: String | ||
like: String | ||
notLike: String | ||
iLike: String | ||
notILike: String | ||
in: [String!] | ||
notIn: [String!] | ||
} | ||
|
||
type TestResolverDTORelationsCountAggregate { | ||
id: Int | ||
testResolverId: Int | ||
} | ||
|
||
type TestResolverDTORelationsMinAggregate { | ||
id: ID | ||
testResolverId: String | ||
} | ||
|
||
type TestResolverDTORelationsMaxAggregate { | ||
id: ID | ||
testResolverId: String | ||
} | ||
|
||
type TestResolverDTORelationsAggregateResponse { | ||
count: TestResolverDTORelationsCountAggregate | ||
min: TestResolverDTORelationsMinAggregate | ||
max: TestResolverDTORelationsMaxAggregate | ||
} | ||
|
||
type Query { | ||
test: TestResolverDTO! | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.