diff --git a/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md b/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md index f70d480293c8..19eb90ee53dd 100644 --- a/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md +++ b/docs/site/tutorials/todo-list/todo-list-tutorial-repository.md @@ -87,52 +87,24 @@ export class TodoListRepository extends DefaultCrudRepository< ### Inclusion of Related Models -To get the related `Todo` objects for each `TodoList`, we can use register a -custom -[`InclusionResolver`](https://loopback.io/doc/en/lb4/apidocs.repository.inclusionresolver.html) -in the `TodoList` repository. +To get the related `Todo` objects for each `TodoList`, we can register the +inclusion resolver that comes with the +[`HasManyRepositoryFactory`](https://loopback.io/doc/en/lb4/apidocs.repository.hasmanyrepository.html). +We need to register this resolver to the repository class, which we can do as +follows: -First, add the following import: - -```ts -import {InclusionResolver} from '@loopback/repository'; -``` - -Next, in the constructor, add the following custom resolver: +{% include code-caption.html content="src/repositories/todo-list.repository.ts" %} ```ts -export class TodoListRepository extends DefaultCrudRepository { - // ... - constructor( - //db, relation factories setup - - // add the following code to build a custom resolver - const todosResolver: InclusionResolver = async todoLists => { - const todos: Todo[][] = []; - for (const todoList of todoLists) { - const todo = await this.todos(todoList.id).find(); - todos.push(todo); - } - - return todos; - }; - // the resolver needs to be registered before using - this.registerInclusionResolver('todos', todosResolver); - ) -} -``` +this.todos = this.createHasManyRepositoryFactoryFor( + 'todos', + todoRepositoryGetter, +); -After that, we need to register this resolver to the repository class, which we -can do as follows: - -```ts -this.registerInclusionResolver('todos', todosResolver); +// Add this line to register the resolver +this.registerInclusionResolver('todos', this.todos.inclusionResolver); ``` -{% include note.html content=" -This is a temporary implementation until we implement our relation resolvers. See [GitHub issue #3450](https://github.com/strongloop/loopback-next/issues/3450) for details. -" %} - Now when you get a `TodoList`, a `todos` property will be included that contains your related `Todo`s, for example: @@ -151,36 +123,22 @@ your related `Todo`s, for example: } ``` -Let's do the same on the `TodoRepository`: +On the other end, the +[`BelongsToAccessor`](https://loopback.io/doc/en/lb4/apidocs.repository.belongstoaccessor.html) +also comes with an inclusion resolver property that we can register on the +`TodoRepository`. So, let's register this resolver to the `TodoRepository` +similar to how we did it for the `TodoListRepository`: {% include code-caption.html content="src/repositories/todo.repository.ts" %} ```ts -// .. other imports -import {InclusionResolver} from '@loopback/repository'; -``` - -```ts -export class TodoRepository extends DefaultCrudRepository { - // ... - constructor( - //db, factories setup +this.todoList = this.createBelongsToAccessorFor( + 'todoList', + todoListRepositoryGetter, +); - // add the following code to build/register a custom resolver - const todoListResolver: InclusionResolver = async todos => { - const todoLists = []; - - for (const todo of todos) { - const todoList = await this.todoList(todo.id); - todoLists.push(todoList); - } - - return todoLists; - }; - - this.registerInclusionResolver('todoList', todoListResolver); - ) -} +// Add this line to register the resolver +this.registerInclusionResolver('todoList', this.todoList.inclusionResolver); ``` We're now ready to expose `TodoList` and its related `Todo` API through the diff --git a/examples/todo-list/src/__tests__/integration/todo-list-image.repository.integration.ts b/examples/todo-list/src/__tests__/integration/todo-list-image.repository.integration.ts index dfcfca861e8e..c4ad984de9b3 100644 --- a/examples/todo-list/src/__tests__/integration/todo-list-image.repository.integration.ts +++ b/examples/todo-list/src/__tests__/integration/todo-list-image.repository.integration.ts @@ -63,4 +63,20 @@ describe('TodoListImageRepository', () => { todoList: toJSON(list), }); }); + + it('includes TodoList in findOne result', async () => { + const list = await givenTodoListInstance(todoListRepo, {}); + const image = await givenTodoListImageInstance(todoListImageRepo, { + todoListId: list.id, + }); + + const response = await todoListImageRepo.findOne({ + include: [{relation: 'todoList'}], + }); + + expect(toJSON(response)).to.deepEqual({ + ...toJSON(image), + todoList: toJSON(list), + }); + }); }); diff --git a/examples/todo-list/src/__tests__/integration/todo-list.repository.integration.ts b/examples/todo-list/src/__tests__/integration/todo-list.repository.integration.ts index 471e06c13d23..0748fb555986 100644 --- a/examples/todo-list/src/__tests__/integration/todo-list.repository.integration.ts +++ b/examples/todo-list/src/__tests__/integration/todo-list.repository.integration.ts @@ -7,6 +7,7 @@ import { import { givenEmptyDatabase, givenTodoInstance, + givenTodoListImageInstance, givenTodoListInstance, testdb, } from '../helpers'; @@ -23,6 +24,10 @@ describe('TodoListRepository', () => { async () => todoListImageRepo, ); todoRepo = new TodoRepository(testdb, async () => todoListRepo); + todoListImageRepo = new TodoListImageRepository( + testdb, + async () => todoListRepo, + ); }); beforeEach(givenEmptyDatabase); @@ -56,4 +61,89 @@ describe('TodoListRepository', () => { todos: [toJSON(todo)], }); }); + + it('includes Todos in findOne method result', async () => { + const list = await givenTodoListInstance(todoListRepo); + const todo = await givenTodoInstance(todoRepo, {todoListId: list.id}); + + const response = await todoListRepo.findOne({ + where: {id: list.id}, + include: [{relation: 'todos'}], + }); + + expect(toJSON(response)).to.deepEqual({ + ...toJSON(list), + todos: [toJSON(todo)], + }); + }); + + it('includes TodoListImage in find method result', async () => { + const list = await givenTodoListInstance(todoListRepo); + const image = await givenTodoListImageInstance(todoListImageRepo, { + todoListId: list.id, + }); + + const response = await todoListRepo.find({ + include: [{relation: 'image'}], + }); + + expect(toJSON(response)).to.deepEqual([ + { + ...toJSON(list), + image: toJSON(image), + }, + ]); + }); + + it('includes TodoListImage in findById method result', async () => { + const list = await givenTodoListInstance(todoListRepo); + const image = await givenTodoListImageInstance(todoListImageRepo, { + todoListId: list.id, + }); + + const response = await todoListRepo.findById(list.id, { + include: [{relation: 'image'}], + }); + + expect(toJSON(response)).to.deepEqual({ + ...toJSON(list), + image: toJSON(image), + }); + }); + + it('includes TodoListImage in findOne method result', async () => { + const list = await givenTodoListInstance(todoListRepo); + const image = await givenTodoListImageInstance(todoListImageRepo, { + todoListId: list.id, + }); + + const response = await todoListRepo.findOne({ + include: [{relation: 'image'}], + }); + + expect(toJSON(response)).to.deepEqual({ + ...toJSON(list), + image: toJSON(image), + }); + }); + + it('includes both Todos and TodoListImage in find method result', async () => { + const list = await givenTodoListInstance(todoListRepo); + const todo = await givenTodoInstance(todoRepo, {todoListId: list.id}); + const image = await givenTodoListImageInstance(todoListImageRepo, { + todoListId: list.id, + }); + + const response = await todoListRepo.find({ + include: [{relation: 'image'}, {relation: 'todos'}], + }); + + expect(toJSON(response)).to.deepEqual([ + { + ...toJSON(list), + image: toJSON(image), + todos: [toJSON(todo)], + }, + ]); + }); }); diff --git a/examples/todo-list/src/__tests__/integration/todo.repository.integration.ts b/examples/todo-list/src/__tests__/integration/todo.repository.integration.ts index ca16f4965d0a..fbc0f1d05a27 100644 --- a/examples/todo-list/src/__tests__/integration/todo.repository.integration.ts +++ b/examples/todo-list/src/__tests__/integration/todo.repository.integration.ts @@ -56,4 +56,18 @@ describe('TodoRepository', () => { todoList: toJSON(list), }); }); + + it('includes TodoList in findOne method result', async () => { + const list = await givenTodoListInstance(todoListRepo); + const todo = await givenTodoInstance(todoRepo, {todoListId: list.id}); + + const response = await todoRepo.findOne({ + include: [{relation: 'todoList'}], + }); + + expect(toJSON(response)).to.deepEqual({ + ...toJSON(todo), + todoList: toJSON(list), + }); + }); }); diff --git a/examples/todo-list/src/repositories/todo-list-image.repository.ts b/examples/todo-list/src/repositories/todo-list-image.repository.ts index 32c38a197238..28c6c35bb904 100644 --- a/examples/todo-list/src/repositories/todo-list-image.repository.ts +++ b/examples/todo-list/src/repositories/todo-list-image.repository.ts @@ -7,7 +7,6 @@ import {Getter, inject} from '@loopback/core'; import { BelongsToAccessor, DefaultCrudRepository, - InclusionResolver, repository, } from '@loopback/repository'; import {DbDataSource} from '../datasources'; @@ -29,27 +28,12 @@ export class TodoListImageRepository extends DefaultCrudRepository< protected todoListRepositoryGetter: Getter, ) { super(TodoListImage, dataSource); + this.todoList = this.createBelongsToAccessorFor( 'todoList', todoListRepositoryGetter, ); - // this is a temporary implementation until - // https://github.com/strongloop/loopback-next/issues/3450 is landed - const todoListResolver: InclusionResolver< - TodoListImage, - TodoList - > = async images => { - const todoLists = []; - - for (const image of images) { - const todoList = await this.todoList(image.id); - todoLists.push(todoList); - } - - return todoLists; - }; - - this.registerInclusionResolver('todoList', todoListResolver); + this.registerInclusionResolver('todoList', this.todoList.inclusionResolver); } } diff --git a/examples/todo-list/src/repositories/todo-list.repository.ts b/examples/todo-list/src/repositories/todo-list.repository.ts index 50ed81841999..ecd80adfa7ee 100644 --- a/examples/todo-list/src/repositories/todo-list.repository.ts +++ b/examples/todo-list/src/repositories/todo-list.repository.ts @@ -8,7 +8,6 @@ import { DefaultCrudRepository, HasManyRepositoryFactory, HasOneRepositoryFactory, - InclusionResolver, juggler, repository, } from '@loopback/repository'; @@ -38,50 +37,20 @@ export class TodoListRepository extends DefaultCrudRepository< protected todoListImageRepositoryGetter: Getter, ) { super(TodoList, dataSource); + this.todos = this.createHasManyRepositoryFactoryFor( 'todos', todoRepositoryGetter, ); - // this is a temporary implementation until - // https://github.com/strongloop/loopback-next/issues/3450 is landed - const todosResolver: InclusionResolver< - TodoList, - Todo - > = async todoLists => { - const todos: Todo[][] = []; - for (const todoList of todoLists) { - const todo = await this.todos(todoList.id).find(); - todos.push(todo); - } - - return todos; - }; - - this.registerInclusionResolver('todos', todosResolver); + this.registerInclusionResolver('todos', this.todos.inclusionResolver); this.image = this.createHasOneRepositoryFactoryFor( 'image', todoListImageRepositoryGetter, ); - // this is a temporary implementation until - // https://github.com/strongloop/loopback-next/issues/3450 is landed - const imageResolver: InclusionResolver< - TodoList, - TodoListImage - > = async todoLists => { - const images = []; - - for (const todoList of todoLists) { - const image = await this.image(todoList.id).get(); - images.push(image); - } - - return images; - }; - - this.registerInclusionResolver('image', imageResolver); + this.registerInclusionResolver('image', this.image.inclusionResolver); } public findByTitle(title: string) { diff --git a/examples/todo-list/src/repositories/todo.repository.ts b/examples/todo-list/src/repositories/todo.repository.ts index 92720221a27f..0c7bf629c636 100644 --- a/examples/todo-list/src/repositories/todo.repository.ts +++ b/examples/todo-list/src/repositories/todo.repository.ts @@ -7,7 +7,6 @@ import {Getter, inject} from '@loopback/core'; import { BelongsToAccessor, DefaultCrudRepository, - InclusionResolver, juggler, repository, } from '@loopback/repository'; @@ -35,20 +34,6 @@ export class TodoRepository extends DefaultCrudRepository< 'todoList', todoListRepositoryGetter, ); - - // this is a temporary implementation until - // https://github.com/strongloop/loopback-next/issues/3450 is landed - const todoListResolver: InclusionResolver = async todos => { - const todoLists = []; - - for (const todo of todos) { - const todoList = await this.todoList(todo.id); - todoLists.push(todoList); - } - - return todoLists; - }; - - this.registerInclusionResolver('todoList', todoListResolver); + this.registerInclusionResolver('todoList', this.todoList.inclusionResolver); } }