Skip to content

Add example that fails for Nested items wrongly returned as null #308

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions examples/typeorm-soft-delete/e2e/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { Connection } from 'typeorm'

import { executeTruncate } from '../../../examples/helpers'
import { SubTaskEntity } from '../src/sub-task/sub-task.entity'
import { TagEntity } from '../src/tag/tag.entity'
import { TodoItemEntity } from '../src/todo-item/todo-item.entity'
import { TodoToTagEntity } from '../src/todo-to-tag/todo-to-tag.entity'

const tables = ['todo_item']
export const truncate = async (connection: Connection): Promise<void> => executeTruncate(connection, tables)
Expand All @@ -13,7 +15,10 @@ export const refresh = async (connection: Connection): Promise<void> => {
const todoRepo = connection.getRepository(TodoItemEntity)
const subTaskRepo = connection.getRepository(SubTaskEntity)

const savedTodos = await todoRepo.save(
const tagRepo = connection.getRepository(TagEntity)
const todoToTagRepo = connection.getRepository(TodoToTagEntity)

const savedTodoItems = await todoRepo.save(
todoRepo.create([
{ title: 'Create Nest App', completed: true },
{ title: 'Create Entity', completed: false },
Expand All @@ -24,7 +29,7 @@ export const refresh = async (connection: Connection): Promise<void> => {
)

await subTaskRepo.save(
savedTodos.reduce(
savedTodoItems.reduce(
(subTasks, todo) => [
...subTasks,

Expand All @@ -35,4 +40,24 @@ export const refresh = async (connection: Connection): Promise<void> => {
[] as Partial<SubTaskEntity>[]
)
)

const tags = await tagRepo.save(tagRepo.create([{ name: 'To Review' }, { name: 'Reviewed' }]))

await todoToTagRepo.save(
todoToTagRepo.create([
{ todoID: savedTodoItems[0].id, tagID: tags[0].id },

{ todoID: savedTodoItems[1].id, tagID: tags[0].id },

{ todoID: savedTodoItems[2].id, tagID: tags[0].id },

// Mark one of the links as deleted.
{ todoID: savedTodoItems[3].id, tagID: tags[0].id, deleted: new Date() },
{ todoID: savedTodoItems[3].id, tagID: tags[1].id },

// Mark one of the links as deleted.
{ todoID: savedTodoItems[4].id, tagID: tags[0].id, deleted: new Date() },
{ todoID: savedTodoItems[4].id, tagID: tags[1].id }
])
)
}
11 changes: 11 additions & 0 deletions examples/typeorm-soft-delete/e2e/graphql-fragments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ export const todoItemFields = `
description
`

export const todoItemWithTagsFields = `
id
title
toTags {
tag {
id
name
}
}
`

export const pageInfoField = `
pageInfo{
hasNextPage
Expand Down
39 changes: 38 additions & 1 deletion examples/typeorm-soft-delete/e2e/todo-item.resolver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { AppModule } from '../src/app.module'
import { SubTaskEntity } from '../src/sub-task/sub-task.entity'
import { TodoItemDTO } from '../src/todo-item/dto/todo-item.dto'
import { refresh } from './fixtures'
import { edgeNodes, pageInfoField, todoItemFields } from './graphql-fragments'
import { edgeNodes, pageInfoField, todoItemFields, todoItemWithTagsFields } from './graphql-fragments'

describe('SoftDelete - TodoItemResolver (e2e)', () => {
let app: INestApplication
Expand Down Expand Up @@ -338,6 +338,43 @@ describe('SoftDelete - TodoItemResolver (e2e)', () => {
})
})

it(`should return the todos with their tags`, async () => {
await request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: `{
todoItems {
${pageInfoField}
${edgeNodes(todoItemWithTagsFields)}
}
}`
})
.expect(200)
.then(({ body }) => {
const { edges, pageInfo }: CursorConnectionType<TodoItemDTO> = body.data.todoItems
expect(pageInfo).toEqual({
endCursor: 'YXJyYXljb25uZWN0aW9uOjQ=',
hasNextPage: false,
hasPreviousPage: false,
startCursor: 'YXJyYXljb25uZWN0aW9uOjA='
})
expect(edges).toHaveLength(5)
expect(edges.map((e) => e.node)).toEqual([
{ id: '1', title: 'Create Nest App', toTags: [{ tag: { id: '1', name: 'To Review' } }] },
{ id: '2', title: 'Create Entity', toTags: [{ tag: { id: '1', name: 'To Review' } }] },
{ id: '3', title: 'Create Entity Service', toTags: [{ tag: { id: '1', name: 'To Review' } }] },
{
id: '4',
title: 'Add Todo Item Resolver',
toTags: [{ tag: { id: '2', name: 'Reviewed' } }]
},
{ id: '5', title: 'How to create item With Sub Tasks', toTags: [{ tag: { id: '2', name: 'Reviewed' } }] }
])
})
})

describe('paging', () => {
it(`should allow paging with the 'first' field`, () =>
request(app.getHttpServer())
Expand Down
36 changes: 36 additions & 0 deletions examples/typeorm-soft-delete/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------

type Tag {
id: ID!
name: String!
}

type TodoItemToTag {
todoID: ID!
tagID: ID!
tag: Tag
}

type TodoItem {
id: ID!
title: String!
Expand All @@ -17,6 +28,13 @@ type TodoItem {
"""Specify to sort results."""
sorting: [SubTaskSort!]! = []
): [SubTask!]!
toTags(
"""Specify to filter the records returned."""
filter: TodoItemToTagFilter! = {}

"""Specify to sort results."""
sorting: [TodoItemToTagSort!]! = []
): [TodoItemToTag!]!
}

"""
Expand Down Expand Up @@ -160,6 +178,24 @@ enum SortNulls {
NULLS_LAST
}

input TodoItemToTagFilter {
and: [TodoItemToTagFilter!]
or: [TodoItemToTagFilter!]
todoID: IDFilterComparison
tagID: IDFilterComparison
}

input TodoItemToTagSort {
field: TodoItemToTagSortFields!
direction: SortDirection!
nulls: SortNulls
}

enum TodoItemToTagSortFields {
todoID
tagID
}

type SubTask {
id: ID!
title: String!
Expand Down
11 changes: 11 additions & 0 deletions examples/typeorm-soft-delete/src/tag/dto/tag.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Field, ID, ObjectType } from '@nestjs/graphql'
import { FilterableField } from '@ptc-org/nestjs-query-graphql'

@ObjectType('Tag')
export class TagDTO {
@FilterableField(() => ID)
id!: number

@Field()
name!: string
}
24 changes: 24 additions & 0 deletions examples/typeorm-soft-delete/src/tag/tag.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Column, CreateDateColumn, DeleteDateColumn, Entity, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'

import { TodoToTagEntity } from '../todo-to-tag/todo-to-tag.entity'

@Entity({ name: 'tag' })
export class TagEntity {
@PrimaryGeneratedColumn()
id!: number

@Column()
name!: string

@OneToMany(() => TodoToTagEntity, (todoToTag) => todoToTag.tag)
toTodos!: TodoToTagEntity[]

@CreateDateColumn()
created!: Date

@UpdateDateColumn()
updated!: Date

@DeleteDateColumn()
deleted?: Date
}
13 changes: 13 additions & 0 deletions examples/typeorm-soft-delete/src/tag/tag.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { InjectRepository } from '@nestjs/typeorm'
import { QueryService } from '@ptc-org/nestjs-query-core'
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'
import { Repository } from 'typeorm'

import { TagEntity } from './tag.entity'

@QueryService(TagEntity)
export class TagService extends TypeOrmQueryService<TagEntity> {
constructor(@InjectRepository(TagEntity) repo: Repository<TagEntity>) {
super(repo, { useSoftDelete: true })
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { GraphQLISODateTime, ID, ObjectType } from '@nestjs/graphql'
import { FilterableField, FilterableUnPagedRelation } from '@ptc-org/nestjs-query-graphql'
import { FilterableField, FilterableUnPagedRelation, UnPagedRelation } from '@ptc-org/nestjs-query-graphql'

import { SubTaskDTO } from '../../sub-task/dto/sub-task.dto'
import { SubTaskEntity } from '../../sub-task/sub-task.entity'
import { TodoToTagDTO } from '../../todo-to-tag/dto/todo-to-tag.dto'
import { TodoToTagEntity } from '../../todo-to-tag/todo-to-tag.entity'

@ObjectType('TodoItem')
@FilterableUnPagedRelation('subTasks', () => SubTaskDTO, {
update: { enabled: true },
withDeleted: true
})
@UnPagedRelation('toTags', () => TodoToTagDTO)
export class TodoItemDTO {
@FilterableField(() => ID)
id!: number
Expand All @@ -22,6 +25,8 @@ export class TodoItemDTO {
@FilterableField()
completed!: boolean

toTags!: TodoToTagEntity[]

@FilterableField()
subTasksCount!: number

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from 'typeorm'

import { SubTaskEntity } from '../sub-task/sub-task.entity'
import { TodoToTagEntity } from '../todo-to-tag/todo-to-tag.entity'

@Entity({ name: 'todo_item' })
export class TodoItemEntity {
Expand All @@ -28,6 +29,9 @@ export class TodoItemEntity {
@OneToMany(() => SubTaskEntity, (subTask) => subTask.todoItem)
subTasks!: SubTaskEntity[]

@OneToMany(() => TodoToTagEntity, (todoToTag) => todoToTag.todoItem)
toTags!: TodoToTagEntity[]

@VirtualColumn({
query: (alias) => `SELECT COUNT(*)
FROM sub_task
Expand Down
32 changes: 29 additions & 3 deletions examples/typeorm-soft-delete/src/todo-item/todo-item.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Module } from '@nestjs/common'
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql'
import { NestjsQueryGraphQLModule, PagingStrategies } from '@ptc-org/nestjs-query-graphql'
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'

import { TagDTO } from '../tag/dto/tag.dto'
import { TagEntity } from '../tag/tag.entity'
import { TagService } from '../tag/tag.service'
import { TodoToTagDTO } from '../todo-to-tag/dto/todo-to-tag.dto'
import { TodoToTagEntity } from '../todo-to-tag/todo-to-tag.entity'
import { TodoToTagService } from '../todo-to-tag/todo-to-tag.service'
import { TodoItemDTO } from './dto/todo-item.dto'
import { TodoItemInputDTO } from './dto/todo-item-input.dto'
import { TodoItemUpdateDTO } from './dto/todo-item-update.dto'
Expand All @@ -13,14 +19,34 @@ import { TodoItemService } from './todo-item.service'
providers: [TodoItemResolver],
imports: [
NestjsQueryGraphQLModule.forFeature({
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],
services: [TodoItemService],
imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity, TodoToTagEntity, TagEntity])],
services: [TodoItemService, TodoToTagService, TagService],
resolvers: [
{
DTOClass: TodoItemDTO,
ServiceClass: TodoItemService,
CreateDTOClass: TodoItemInputDTO,
UpdateDTOClass: TodoItemUpdateDTO
},
{
DTOClass: TodoToTagDTO,
EntityClass: TodoToTagEntity,
ServiceClass: TodoToTagService,
pagingStrategy: PagingStrategies.NONE,
create: { disabled: true },
update: { disabled: true },
delete: { disabled: true },
read: { disabled: true }
},
{
DTOClass: TagDTO,
EntityClass: TagEntity,
ServiceClass: TagService,
pagingStrategy: PagingStrategies.NONE,
create: { disabled: true },
update: { disabled: true },
delete: { disabled: true },
read: { disabled: true }
}
]
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ID, ObjectType } from '@nestjs/graphql'
import { FilterableField, Relation } from '@ptc-org/nestjs-query-graphql'

import { TagDTO } from '../../tag/dto/tag.dto'
import { TodoItemEntity } from '../../todo-item/todo-item.entity'

@ObjectType('TodoItemToTag')
@Relation('tag', () => TagDTO, { nullable: true })
export class TodoToTagDTO {
@FilterableField(() => ID)
todoID: number

todoItem: TodoItemEntity

@FilterableField(() => ID)
tagID: number

tag!: TagDTO
}
24 changes: 24 additions & 0 deletions examples/typeorm-soft-delete/src/todo-to-tag/todo-to-tag.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { DeleteDateColumn, Entity, JoinColumn, ManyToOne, ObjectType, PrimaryColumn } from 'typeorm'

import { TagEntity } from '../tag/tag.entity'
import { TodoItemEntity } from '../todo-item/todo-item.entity'

@Entity({ name: 'todo_to_tag' })
export class TodoToTagEntity {
@PrimaryColumn({ name: 'todo_id' })
todoID!: number

@ManyToOne((): ObjectType<TodoItemEntity> => TodoItemEntity, (t) => t.toTags)
@JoinColumn({ name: 'todo_id' })
todoItem: TodoItemEntity

@PrimaryColumn({ name: 'tag_id' })
tagID!: number

@ManyToOne((): ObjectType<TagEntity> => TagEntity, (t) => t.toTodos)
@JoinColumn({ name: 'tag_id' })
tag: TagEntity

@DeleteDateColumn()
deleted?: Date
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { InjectRepository } from '@nestjs/typeorm'
import { QueryService } from '@ptc-org/nestjs-query-core'
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'
import { Repository } from 'typeorm'

import { TodoToTagEntity } from './todo-to-tag.entity'

@QueryService(TodoToTagEntity)
export class TodoToTagService extends TypeOrmQueryService<TodoToTagEntity> {
constructor(@InjectRepository(TodoToTagEntity) repo: Repository<TodoToTagEntity>) {
super(repo, { useSoftDelete: true })
}
}
Loading