Skip to content

Commit

Permalink
feat(graphql): Enable filtering on ORM relations
Browse files Browse the repository at this point in the history
  • Loading branch information
doug-martin committed Jul 4, 2020
1 parent 80c69d6 commit 60229b8
Show file tree
Hide file tree
Showing 27 changed files with 1,368 additions and 103 deletions.
29 changes: 29 additions & 0 deletions examples/sequelize/e2e/sub-task.resolver.spec.ts
Expand Up @@ -245,6 +245,35 @@ describe('SubTaskResolver (sequelize - e2e)', () => {
});
});

it(`should allow querying on todoItem`, () => {
return request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: `{
subTasks(filter: { todoItem: { title: { like: "Create Entity%" } } }) {
${pageInfoField}
${edgeNodes(subTaskFields)}
totalCount
}
}`,
})
.expect(200)
.then(({ body }) => {
const { edges, pageInfo, totalCount }: CursorConnectionType<SubTaskDTO> = body.data.subTasks;
expect(pageInfo).toEqual({
endCursor: 'YXJyYXljb25uZWN0aW9uOjU=',
hasNextPage: false,
hasPreviousPage: false,
startCursor: 'YXJyYXljb25uZWN0aW9uOjA=',
});
expect(totalCount).toBe(6);
expect(edges).toHaveLength(6);
expect(edges.map((e) => e.node)).toEqual(subTasks.slice(3, 9));
});
});

it(`should allow sorting`, () => {
return request(app.getHttpServer())
.post('/graphql')
Expand Down
29 changes: 29 additions & 0 deletions examples/sequelize/e2e/tag.resolver.spec.ts
Expand Up @@ -168,6 +168,35 @@ describe('TagResolver (sequelize - e2e)', () => {
});
});

it(`should allow querying on todoItems`, () => {
return request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: `{
tags(filter: { todoItems: { title: { like: "Create Entity%" } } }, sorting: [{field: id, direction: ASC}]) {
${pageInfoField}
${edgeNodes(tagFields)}
totalCount
}
}`,
})
.expect(200)
.then(({ body }) => {
const { edges, pageInfo, totalCount }: CursorConnectionType<TagDTO> = body.data.tags;
expect(pageInfo).toEqual({
endCursor: 'YXJyYXljb25uZWN0aW9uOjI=',
hasNextPage: false,
hasPreviousPage: false,
startCursor: 'YXJyYXljb25uZWN0aW9uOjA=',
});
expect(totalCount).toBe(3);
expect(edges).toHaveLength(3);
expect(edges.map((e) => e.node)).toEqual([tags[0], tags[2], tags[4]]);
});
});

it(`should allow sorting`, () => {
return request(app.getHttpServer())
.post('/graphql')
Expand Down
66 changes: 66 additions & 0 deletions examples/sequelize/e2e/todo-item.resolver.spec.ts
Expand Up @@ -223,6 +223,72 @@ describe('TodoItemResolver (sequelize - e2e)', () => {
});
});

it(`should allow querying on subTasks`, () => {
return request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: `{
todoItems(filter: { subTasks: { title: { in: ["Create Nest App - Sub Task 1", "Create Entity - Sub Task 1"] } } }) {
${pageInfoField}
${edgeNodes(todoItemFields)}
totalCount
}
}`,
})
.expect(200)
.then(({ body }) => {
const { edges, totalCount, pageInfo }: CursorConnectionType<TodoItemDTO> = body.data.todoItems;
expect(pageInfo).toEqual({
endCursor: 'YXJyYXljb25uZWN0aW9uOjE=',
hasNextPage: false,
hasPreviousPage: false,
startCursor: 'YXJyYXljb25uZWN0aW9uOjA=',
});
expect(totalCount).toBe(2);
expect(edges).toHaveLength(2);

expect(edges.map((e) => e.node)).toEqual([
{ id: '1', title: 'Create Nest App', completed: true, description: null, age: expect.any(Number) },
{ id: '2', title: 'Create Entity', completed: false, description: null, age: expect.any(Number) },
]);
});
});

it(`should allow querying on tags`, () => {
return request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: `{
todoItems(filter: { tags: { name: { eq: "Home" } } }) {
${pageInfoField}
${edgeNodes(todoItemFields)}
totalCount
}
}`,
})
.expect(200)
.then(({ body }) => {
const { edges, totalCount, pageInfo }: CursorConnectionType<TodoItemDTO> = body.data.todoItems;
expect(pageInfo).toEqual({
endCursor: 'YXJyYXljb25uZWN0aW9uOjE=',
hasNextPage: false,
hasPreviousPage: false,
startCursor: 'YXJyYXljb25uZWN0aW9uOjA=',
});
expect(totalCount).toBe(2);
expect(edges).toHaveLength(2);

expect(edges.map((e) => e.node)).toEqual([
{ id: '1', title: 'Create Nest App', completed: true, description: null, age: expect.any(Number) },
{ id: '4', title: 'Add Todo Item Resolver', completed: false, description: null, age: expect.any(Number) },
]);
});
});

it(`should allow sorting`, () => {
return request(app.getHttpServer())
.post('/graphql')
Expand Down
4 changes: 2 additions & 2 deletions examples/sequelize/src/sub-task/dto/sub-task.dto.ts
@@ -1,9 +1,9 @@
import { Relation, FilterableField } from '@nestjs-query/query-graphql';
import { FilterableField, FilterableRelation } from '@nestjs-query/query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { TodoItemDTO } from '../../todo-item/dto/todo-item.dto';

@ObjectType('SubTask')
@Relation('todoItem', () => TodoItemDTO, { disableRemove: true })
@FilterableRelation('todoItem', () => TodoItemDTO, { disableRemove: true })
export class SubTaskDTO {
@FilterableField(() => ID)
id!: number;
Expand Down
4 changes: 2 additions & 2 deletions examples/sequelize/src/tag/dto/tag.dto.ts
@@ -1,9 +1,9 @@
import { FilterableField, Connection } from '@nestjs-query/query-graphql';
import { FilterableField, FilterableConnection } from '@nestjs-query/query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { TodoItemDTO } from '../../todo-item/dto/todo-item.dto';

@ObjectType('Tag')
@Connection('todoItems', () => TodoItemDTO)
@FilterableConnection('todoItems', () => TodoItemDTO)
export class TagDTO {
@FilterableField(() => ID)
id!: number;
Expand Down
6 changes: 3 additions & 3 deletions examples/sequelize/src/todo-item/dto/todo-item.dto.ts
@@ -1,12 +1,12 @@
import { FilterableField, Connection } from '@nestjs-query/query-graphql';
import { FilterableField, FilterableConnection } from '@nestjs-query/query-graphql';
import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
import { AuthGuard } from '../../auth.guard';
import { SubTaskDTO } from '../../sub-task/dto/sub-task.dto';
import { TagDTO } from '../../tag/dto/tag.dto';

@ObjectType('TodoItem')
@Connection('subTasks', () => SubTaskDTO, { guards: [AuthGuard] })
@Connection('tags', () => TagDTO, { guards: [AuthGuard] })
@FilterableConnection('subTasks', () => SubTaskDTO, { guards: [AuthGuard] })
@FilterableConnection('tags', () => TagDTO, { guards: [AuthGuard] })
export class TodoItemDTO {
@FilterableField(() => ID)
id!: number;
Expand Down
29 changes: 29 additions & 0 deletions examples/typeorm/e2e/sub-task.resolver.spec.ts
Expand Up @@ -245,6 +245,35 @@ describe('SubTaskResolver (typeorm - e2e)', () => {
});
});

it(`should allow querying on todoItem`, () => {
return request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: `{
subTasks(filter: { todoItem: { title: { like: "Create Entity%" } } }) {
${pageInfoField}
${edgeNodes(subTaskFields)}
totalCount
}
}`,
})
.expect(200)
.then(({ body }) => {
const { edges, pageInfo, totalCount }: CursorConnectionType<SubTaskDTO> = body.data.subTasks;
expect(pageInfo).toEqual({
endCursor: 'YXJyYXljb25uZWN0aW9uOjU=',
hasNextPage: false,
hasPreviousPage: false,
startCursor: 'YXJyYXljb25uZWN0aW9uOjA=',
});
expect(totalCount).toBe(6);
expect(edges).toHaveLength(6);
expect(edges.map((e) => e.node)).toEqual(subTasks.slice(3, 9));
});
});

it(`should allow sorting`, () => {
return request(app.getHttpServer())
.post('/graphql')
Expand Down
29 changes: 29 additions & 0 deletions examples/typeorm/e2e/tag.resolver.spec.ts
Expand Up @@ -168,6 +168,35 @@ describe('TagResolver (typeorm - e2e)', () => {
});
});

it(`should allow querying on todoItems`, () => {
return request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: `{
tags(filter: { todoItems: { title: { like: "Create Entity%" } } }, sorting: [{field: id, direction: ASC}]) {
${pageInfoField}
${edgeNodes(tagFields)}
totalCount
}
}`,
})
.expect(200)
.then(({ body }) => {
const { edges, pageInfo, totalCount }: CursorConnectionType<TagDTO> = body.data.tags;
expect(pageInfo).toEqual({
endCursor: 'YXJyYXljb25uZWN0aW9uOjI=',
hasNextPage: false,
hasPreviousPage: false,
startCursor: 'YXJyYXljb25uZWN0aW9uOjA=',
});
expect(totalCount).toBe(3);
expect(edges).toHaveLength(3);
expect(edges.map((e) => e.node)).toEqual([tags[0], tags[2], tags[4]]);
});
});

it(`should allow sorting`, () => {
return request(app.getHttpServer())
.post('/graphql')
Expand Down
66 changes: 66 additions & 0 deletions examples/typeorm/e2e/todo-item.resolver.spec.ts
Expand Up @@ -223,6 +223,72 @@ describe('TodoItemResolver (typeorm - e2e)', () => {
});
});

it(`should allow querying on subTasks`, () => {
return request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: `{
todoItems(filter: { subTasks: { title: { in: ["Create Nest App - Sub Task 1", "Create Entity - Sub Task 1"] } } }) {
${pageInfoField}
${edgeNodes(todoItemFields)}
totalCount
}
}`,
})
.expect(200)
.then(({ body }) => {
const { edges, totalCount, pageInfo }: CursorConnectionType<TodoItemDTO> = body.data.todoItems;
expect(pageInfo).toEqual({
endCursor: 'YXJyYXljb25uZWN0aW9uOjE=',
hasNextPage: false,
hasPreviousPage: false,
startCursor: 'YXJyYXljb25uZWN0aW9uOjA=',
});
expect(totalCount).toBe(2);
expect(edges).toHaveLength(2);

expect(edges.map((e) => e.node)).toEqual([
{ id: '1', title: 'Create Nest App', completed: true, description: null, age: expect.any(Number) },
{ id: '2', title: 'Create Entity', completed: false, description: null, age: expect.any(Number) },
]);
});
});

it(`should allow querying on tags`, () => {
return request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: `{
todoItems(filter: { tags: { name: { eq: "Home" } } }) {
${pageInfoField}
${edgeNodes(todoItemFields)}
totalCount
}
}`,
})
.expect(200)
.then(({ body }) => {
const { edges, totalCount, pageInfo }: CursorConnectionType<TodoItemDTO> = body.data.todoItems;
expect(pageInfo).toEqual({
endCursor: 'YXJyYXljb25uZWN0aW9uOjE=',
hasNextPage: false,
hasPreviousPage: false,
startCursor: 'YXJyYXljb25uZWN0aW9uOjA=',
});
expect(totalCount).toBe(2);
expect(edges).toHaveLength(2);

expect(edges.map((e) => e.node)).toEqual([
{ id: '1', title: 'Create Nest App', completed: true, description: null, age: expect.any(Number) },
{ id: '4', title: 'Add Todo Item Resolver', completed: false, description: null, age: expect.any(Number) },
]);
});
});

it(`should allow sorting`, () => {
return request(app.getHttpServer())
.post('/graphql')
Expand Down
4 changes: 2 additions & 2 deletions examples/typeorm/src/sub-task/dto/sub-task.dto.ts
@@ -1,9 +1,9 @@
import { FilterableField, Relation } from '@nestjs-query/query-graphql';
import { FilterableField, FilterableRelation } from '@nestjs-query/query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { TodoItemDTO } from '../../todo-item/dto/todo-item.dto';

@ObjectType('SubTask')
@Relation('todoItem', () => TodoItemDTO, { disableRemove: true })
@FilterableRelation('todoItem', () => TodoItemDTO, { disableRemove: true })
export class SubTaskDTO {
@FilterableField(() => ID)
id!: number;
Expand Down
4 changes: 2 additions & 2 deletions examples/typeorm/src/tag/dto/tag.dto.ts
@@ -1,9 +1,9 @@
import { FilterableField, Connection } from '@nestjs-query/query-graphql';
import { FilterableField, FilterableConnection } from '@nestjs-query/query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { TodoItemDTO } from '../../todo-item/dto/todo-item.dto';

@ObjectType('Tag')
@Connection('todoItems', () => TodoItemDTO)
@FilterableConnection('todoItems', () => TodoItemDTO)
export class TagDTO {
@FilterableField(() => ID)
id!: number;
Expand Down
6 changes: 3 additions & 3 deletions examples/typeorm/src/todo-item/dto/todo-item.dto.ts
@@ -1,12 +1,12 @@
import { FilterableField, Connection } from '@nestjs-query/query-graphql';
import { FilterableField, FilterableConnection } from '@nestjs-query/query-graphql';
import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
import { AuthGuard } from '../../auth.guard';
import { SubTaskDTO } from '../../sub-task/dto/sub-task.dto';
import { TagDTO } from '../../tag/dto/tag.dto';

@ObjectType('TodoItem')
@Connection('subTasks', () => SubTaskDTO, { disableRemove: true, guards: [AuthGuard] })
@Connection('tags', () => TagDTO, { guards: [AuthGuard] })
@FilterableConnection('subTasks', () => SubTaskDTO, { disableRemove: true, guards: [AuthGuard] })
@FilterableConnection('tags', () => TagDTO, { guards: [AuthGuard] })
export class TodoItemDTO {
@FilterableField(() => ID)
id!: number;
Expand Down

0 comments on commit 60229b8

Please sign in to comment.