fix: extra enhancements

This commit is contained in:
hexastack 2024-11-21 09:38:09 +01:00
parent 286beee5e6
commit f93d6c20ae
3 changed files with 81 additions and 66 deletions

View File

@ -9,7 +9,7 @@
import { EventEmitter2 } from '@nestjs/event-emitter';
import { MongooseModule, getModelToken } from '@nestjs/mongoose';
import { Test } from '@nestjs/testing';
import { Model } from 'mongoose';
import { Model, Types } from 'mongoose';
import {
blockFixtures,
@ -153,7 +153,7 @@ describe('BlockRepository', () => {
});
});
describe('updateBlocksInScope', () => {
describe('prepareBlocksInCategoryUpdateScope', () => {
it('should update blocks within the scope based on category and ids', async () => {
jest.spyOn(blockRepository, 'findOne').mockResolvedValue({
id: validIds[0],
@ -164,7 +164,10 @@ describe('BlockRepository', () => {
const mockUpdateOne = jest.spyOn(blockRepository, 'updateOne');
await blockRepository.updateBlocksInScope(validCategory, validIds);
await blockRepository.prepareBlocksInCategoryUpdateScope(
validCategory,
validIds,
);
expect(mockUpdateOne).toHaveBeenCalledWith(validIds[0], {
nextBlocks: [validIds[1]],
@ -182,13 +185,16 @@ describe('BlockRepository', () => {
const mockUpdateOne = jest.spyOn(blockRepository, 'updateOne');
await blockRepository.updateBlocksInScope(validCategory, validIds);
await blockRepository.prepareBlocksInCategoryUpdateScope(
validCategory,
validIds,
);
expect(mockUpdateOne).not.toHaveBeenCalled();
});
});
describe('updateExternalBlocks', () => {
describe('prepareBlocksOutOfCategoryUpdateScope', () => {
it('should update blocks outside the scope by removing references from attachedBlock', async () => {
const otherBlocks = [
{
@ -200,7 +206,10 @@ describe('BlockRepository', () => {
const mockUpdateOne = jest.spyOn(blockRepository, 'updateOne');
await blockRepository.updateExternalBlocks(otherBlocks, validIds);
await blockRepository.prepareBlocksOutOfCategoryUpdateScope(
otherBlocks,
validIds,
);
expect(mockUpdateOne).toHaveBeenCalledWith('64abc1234def567890fedcab', {
attachedBlock: null,
@ -218,10 +227,12 @@ describe('BlockRepository', () => {
const mockUpdateOne = jest.spyOn(blockRepository, 'updateOne');
await blockRepository.updateExternalBlocks(otherBlocks, [validIds[0]]);
await blockRepository.prepareBlocksOutOfCategoryUpdateScope(otherBlocks, [
validIds[0],
]);
expect(mockUpdateOne).toHaveBeenCalledWith('64abc1234def567890fedcab', {
$pull: { nextBlocks: [validIds[1]] },
nextBlocks: [new Types.ObjectId(validIds[1])],
});
});
});
@ -236,13 +247,13 @@ describe('BlockRepository', () => {
},
] as Block[]);
const mockUpdateBlocksInScope = jest.spyOn(
const prepareBlocksInCategoryUpdateScope = jest.spyOn(
blockRepository,
'updateBlocksInScope',
'prepareBlocksInCategoryUpdateScope',
);
const mockUpdateExternalBlocks = jest.spyOn(
const prepareBlocksOutOfCategoryUpdateScope = jest.spyOn(
blockRepository,
'updateExternalBlocks',
'prepareBlocksOutOfCategoryUpdateScope',
);
await blockRepository.preUpdateMany(
@ -252,11 +263,11 @@ describe('BlockRepository', () => {
);
expect(mockFind).toHaveBeenCalled();
expect(mockUpdateBlocksInScope).toHaveBeenCalledWith(
expect(prepareBlocksInCategoryUpdateScope).toHaveBeenCalledWith(
validCategory,
validIds,
['64abc1234def567890fedcab'],
);
expect(mockUpdateExternalBlocks).toHaveBeenCalledWith(
expect(prepareBlocksOutOfCategoryUpdateScope).toHaveBeenCalledWith(
[
{
id: '64abc1234def567890fedcab',
@ -264,26 +275,26 @@ describe('BlockRepository', () => {
nextBlocks: [validIds[0]],
},
],
validIds,
['64abc1234def567890fedcab'],
);
});
it('should not perform updates if no category is provided', async () => {
const mockFind = jest.spyOn(blockRepository, 'find');
const mockUpdateBlocksInScope = jest.spyOn(
const prepareBlocksInCategoryUpdateScope = jest.spyOn(
blockRepository,
'updateBlocksInScope',
'prepareBlocksInCategoryUpdateScope',
);
const mockUpdateExternalBlocks = jest.spyOn(
const prepareBlocksOutOfCategoryUpdateScope = jest.spyOn(
blockRepository,
'updateExternalBlocks',
'prepareBlocksOutOfCategoryUpdateScope',
);
await blockRepository.preUpdateMany({} as any, {}, {});
await blockRepository.preUpdateMany({} as any, {}, { $set: {} });
expect(mockFind).not.toHaveBeenCalled();
expect(mockUpdateBlocksInScope).not.toHaveBeenCalled();
expect(mockUpdateExternalBlocks).not.toHaveBeenCalled();
expect(prepareBlocksInCategoryUpdateScope).not.toHaveBeenCalled();
expect(prepareBlocksOutOfCategoryUpdateScope).not.toHaveBeenCalled();
});
});
});

View File

@ -9,7 +9,7 @@
import { Injectable, Optional } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { InjectModel } from '@nestjs/mongoose';
import mongoose, {
import {
Document,
Model,
Query,
@ -94,27 +94,27 @@ export class BlockRepository extends BaseRepository<
| UpdateWithAggregationPipeline
| UpdateQuery<Document<Block, any, any>>,
): Promise<void> {
const movedBlock = await this.findOne(criteria);
if (!movedBlock) {
return;
}
const update: BlockUpdateDto = updates?.['$set'];
if (update?.category) {
const movedBlockId = criteria._id;
const movedBlock: Block = await this.findOne(criteria);
if (!movedBlock) {
return;
}
// Find and update blocks that reference the moved block
await this.updateMany(
{ nextBlocks: movedBlockId },
{ $pull: { nextBlocks: movedBlockId } },
{ nextBlocks: movedBlock.id },
{ $pull: { nextBlocks: movedBlock.id } },
);
await this.updateMany(
{ attachedBlock: movedBlockId },
{ attachedBlock: movedBlock.id },
{ $set: { attachedBlock: null } },
);
this.checkDeprecatedAttachmentUrl(update);
}
this.checkDeprecatedAttachmentUrl(update);
}
/**
@ -136,29 +136,32 @@ export class BlockRepository extends BaseRepository<
criteria: TFilterQuery<Block>,
updates: UpdateQuery<Document<Block, any, any>>,
): Promise<void> {
if (criteria._id?.$in && updates?.$set?.category) {
const ids: string[] = criteria._id?.$in || [];
const category: string = updates.$set.category;
const categoryId: string = updates.$set.category;
if (categoryId) {
const movedBlocks = await this.find(criteria);
// Step 1: Map IDs and Category
const objIds = ids.map((id) => new mongoose.Types.ObjectId(id));
const objCategory = new mongoose.Types.ObjectId(category);
if (movedBlocks.length) {
const ids: string[] = movedBlocks.map(({ id }) => id);
// Step 2: Find other blocks
const otherBlocks = await this.find({
_id: { $nin: objIds },
category: { $ne: objCategory },
$or: [
{ attachedBlock: { $in: objIds } },
{ nextBlocks: { $in: objIds } },
],
});
// Step 1: Map IDs and Category
const objIds = ids.map((id) => new Types.ObjectId(id));
const objCategoryId = new Types.ObjectId(categoryId);
// Step 3: Update blocks in the provided scope
await this.updateBlocksInScope(category, ids);
// Step 2: Find other blocks
const otherBlocks = await this.find({
_id: { $nin: objIds },
category: { $ne: objCategoryId },
$or: [
{ attachedBlock: { $in: objIds } },
{ nextBlocks: { $in: objIds } },
],
});
// Step 3: Update blocks in the provided scope
await this.prepareBlocksInCategoryUpdateScope(categoryId, ids);
// Step 4: Update external blocks
await this.updateExternalBlocks(otherBlocks, ids);
// Step 4: Update external blocks
await this.prepareBlocksOutOfCategoryUpdateScope(otherBlocks, ids);
}
}
}
@ -170,9 +173,12 @@ export class BlockRepository extends BaseRepository<
* @param ids - IDs representing the blocks to update.
* @returns A promise that resolves once all updates within the scope are complete.
*/
async updateBlocksInScope(category: string, ids: string[]): Promise<void> {
async prepareBlocksInCategoryUpdateScope(
category: string,
ids: string[],
): Promise<void> {
for (const id of ids) {
const oldState = await this.findOne(id);
const oldState: Block = await this.findOne(id);
if (oldState.category !== category) {
const updatedNextBlocks = oldState.nextBlocks.filter((nextBlock) =>
ids.includes(nextBlock),
@ -198,7 +204,7 @@ export class BlockRepository extends BaseRepository<
* @param ids - An array of the Ids to disassociate.
* @returns A promise that resolves once all external block updates are complete.
*/
async updateExternalBlocks(
async prepareBlocksOutOfCategoryUpdateScope(
otherBlocks: Block[],
ids: string[],
): Promise<void> {
@ -207,14 +213,12 @@ export class BlockRepository extends BaseRepository<
await this.updateOne(block.id, { attachedBlock: null });
}
const updatedNextBlocks = block.nextBlocks.filter(
(nextBlock) => !ids.includes(nextBlock),
);
const nextBlocks = block.nextBlocks
.filter((nextBlock) => !ids.includes(nextBlock))
.map((id) => new Types.ObjectId(id));
if (updatedNextBlocks.length > 0) {
await this.updateOne(block.id, {
$pull: { nextBlocks: updatedNextBlocks },
});
if (nextBlocks.length > 0) {
await this.updateOne(block.id, { nextBlocks });
}
}
}

View File

@ -8,7 +8,7 @@
import { getModelToken } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import mongoose, { Model } from 'mongoose';
import { Model, Types } from 'mongoose';
import { DummyRepository } from '@/utils/test/dummy/repositories/dummy.repository';
import { closeInMongodConnection } from '@/utils/test/test';
@ -150,7 +150,7 @@ describe('BaseRepository', () => {
expect(spyBeforeUpdate).toHaveBeenCalledWith(
expect.objectContaining({ $useProjection: true }),
{
_id: new mongoose.Types.ObjectId(created.id),
_id: new Types.ObjectId(created.id),
},
expect.objectContaining({ $set: expect.objectContaining(mockUpdate) }),
);
@ -202,7 +202,7 @@ describe('BaseRepository', () => {
expect(spyBeforeDelete).toHaveBeenCalledWith(
expect.objectContaining({ $useProjection: true }),
{
_id: new mongoose.Types.ObjectId(createdId),
_id: new Types.ObjectId(createdId),
},
);
expect(spyAfterDelete).toHaveBeenCalledWith(