Skip to content

Commit

Permalink
fix(sequelize): use keyFrom as foreign key for belongsto relation def…
Browse files Browse the repository at this point in the history
…inition

Co-author: https://github.com/KalleV

Signed-off-by: Samarpan Bhattacharya <[email protected]>
  • Loading branch information
samarpanB committed Sep 27, 2023
1 parent 984ecac commit 08c06ca
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 10 deletions.
12 changes: 11 additions & 1 deletion extensions/sequelize/src/__tests__/fixtures/models/todo.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@ export class Todo extends Entity {
})
isComplete?: boolean;

@belongsTo(() => TodoList, {name: 'todoList'}, {name: 'todo_list_id'})
@belongsTo(
() => TodoList,
{
keyTo: 'id',
keyFrom: 'todoListId',
name: 'todoList',
},
{
name: 'todo_list_id',
},
)
todoListId: number;

constructor(data?: Partial<Todo>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,18 +499,103 @@ describe('Sequelize CRUD Repository (integration)', () => {
});

it('supports @belongsTo', async () => {
await migrateSchema(['books']);

const categories = [
{name: 'Programming'},
{name: 'Cooking'},
{name: 'Self Help'},
];

const categoryResponse = await client
.post('/categories-bulk')
.send(categories);

type Category = {name: string; id: number};

const books = [
{
title: 'The Art of Cooking',
categoryId: categoryResponse.body.find(
(cat: Category) => cat.name === 'Cooking',
).id,
},
{
title: 'JavaScript the Art of Web',
categoryId: categoryResponse.body.find(
(cat: Category) => cat.name === 'Programming',
).id,
},
{
title: '7 Rules of life',
categoryId: categoryResponse.body.find(
(cat: Category) => cat.name === 'Self Help',
).id,
},
];

await client.post('/books-bulk').send(books);

const filter = {
where: {title: {like: '%Art%'}},
include: [
{
relation: 'category',
scope: {where: {name: 'Programming'}},
required: true,
},
],
};

const relationRes = await client
.get(`/books?filter=${encodeURIComponent(JSON.stringify(filter))}`)
.send();

// If only 1 entry is returned it ensures that the cooking entry is not envolved.
// Confirming the fact that it used inner join behind the scenes
expect(relationRes.body.length).to.be.equal(1);
expect(relationRes.body[0].category).to.be.deepEqual(
categoryResponse.body.find(
(cat: Category) => cat.name === 'Programming',
),
);
});

it('supports @belongsTo using keyfrom and keyto', async () => {
await migrateSchema(['users', 'todos', 'todo-lists']);

const userRes = await client.post('/users').send(getDummyUser());

const todoListRes = await client
.post('/todo-lists')
.send(getDummyTodoList({user: userRes.body.id}));
const todoOne = await client.post('/todo-lists').send(
getDummyTodoList({
title: 'Todo list one',
user: userRes.body.id,
}),
);
const todoListRes = await client.post('/todo-lists').send(
getDummyTodoList({
title: 'Another todo list',
user: userRes.body.id,
}),
);

const todo = getDummyTodo({todoListId: todoListRes.body.id});
let todo = getDummyTodo({
title: 'Todo one',
todoListId: todoListRes.body.id,
});
const todoRes = await client.post('/todos').send(todo);
todo = getDummyTodo({
title: 'Another todo',
todoListId: todoOne.body.id,
});
await client.post('/todos').send(todo);

const filter = {include: ['todoList']};
const filter = {
where: {
title: {like: '%one%'},
},
include: ['todoList'],
};
const relationRes = await client.get(
`/todos?filter=${encodeURIComponent(JSON.stringify(filter))}`,
);
Expand Down
10 changes: 6 additions & 4 deletions extensions/sequelize/src/sequelize/sequelize.repository.base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -700,12 +700,14 @@ export class SequelizeCrudRepository<
entityClass.definition.relations[key].type ===
LoopbackRelationType.belongsTo
) {
const foreignKey = (
entityClass.definition.relations[key] as BelongsToDefinition
).keyTo;
const relationDefinition = entityClass.definition.relations[
key
] as BelongsToDefinition;
const foreignKey = relationDefinition.keyFrom;
const targetKey = relationDefinition.keyTo;
sourceModel.belongsTo(targetModel, {
foreignKey: {name: foreignKey},

targetKey,
// Which client will pass on in loopback style include filter, eg. `include: ["thisName"]`
as: entityClass.definition.relations[key].name,
});
Expand Down

0 comments on commit 08c06ca

Please sign in to comment.