@flusys/nestjs-task-manager 6.3.0 → 6.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -142,11 +142,13 @@ let TaskBoardService = class TaskBoardService extends _classes.ApiService {
142
142
  }, {
143
143
  role: dto.role ?? _entities.BoardMemberRole.MEMBER
144
144
  });
145
- return memberRepo.findOne({
145
+ const restored = await memberRepo.findOne({
146
146
  where: {
147
147
  id: softDeleted.id
148
148
  }
149
149
  });
150
+ await this.invalidateBoardCache(dto.boardId);
151
+ return restored;
150
152
  }
151
153
  const member = memberRepo.create({
152
154
  boardId: dto.boardId,
@@ -154,7 +156,9 @@ let TaskBoardService = class TaskBoardService extends _classes.ApiService {
154
156
  role: dto.role ?? _entities.BoardMemberRole.MEMBER,
155
157
  createdById: user.id
156
158
  });
157
- return memberRepo.save(member);
159
+ const saved = await memberRepo.save(member);
160
+ await this.invalidateBoardCache(dto.boardId);
161
+ return saved;
158
162
  }
159
163
  async removeMember(dto) {
160
164
  const memberRepo = await this.ensureMemberRepository();
@@ -174,6 +178,7 @@ let TaskBoardService = class TaskBoardService extends _classes.ApiService {
174
178
  }, {
175
179
  deletedAt: new Date()
176
180
  });
181
+ await this.invalidateBoardCache(dto.boardId);
177
182
  }
178
183
  async getMembers(boardId) {
179
184
  const memberRepo = await this.ensureMemberRepository();
@@ -207,6 +212,16 @@ let TaskBoardService = class TaskBoardService extends _classes.ApiService {
207
212
  createdById: user.id
208
213
  })));
209
214
  }
215
+ async invalidateBoardCache(boardId) {
216
+ await Promise.all([
217
+ this.clearCacheForAll(),
218
+ this.clearCacheForId([
219
+ {
220
+ id: boardId
221
+ }
222
+ ])
223
+ ]);
224
+ }
210
225
  async ensureMemberRepository() {
211
226
  if (!this.memberRepository) {
212
227
  this.memberRepository = await this.dataSourceProvider.getRepository(_entities.TaskBoardMember);
@@ -57,6 +57,15 @@ function mockQueryRunner(repo) {
57
57
  return qr;
58
58
  }
59
59
  describe('TaskBoardService', ()=>{
60
+ const mockUtilsService = {
61
+ clearCache: jest.fn(),
62
+ getCacheKey: jest.fn().mockReturnValue('cache-key'),
63
+ trackCacheKey: jest.fn()
64
+ };
65
+ // Call history is shared across tests in this file; reset it so each cache assertion is exact.
66
+ beforeEach(()=>{
67
+ mockUtilsService.clearCache.mockClear();
68
+ });
60
69
  let service;
61
70
  let mockRepo;
62
71
  let mockMemberRepo;
@@ -84,11 +93,7 @@ describe('TaskBoardService', ()=>{
84
93
  },
85
94
  {
86
95
  provide: _modules.UtilsService,
87
- useValue: {
88
- clearCache: jest.fn(),
89
- getCacheKey: jest.fn(),
90
- trackCacheKey: jest.fn()
91
- }
96
+ useValue: mockUtilsService
92
97
  },
93
98
  {
94
99
  provide: _taskmanagerconfigservice.TaskManagerConfigService,
@@ -264,6 +269,23 @@ describe('TaskBoardService', ()=>{
264
269
  }));
265
270
  expect(result).toEqual(created);
266
271
  });
272
+ it('invalidates the board list cache and the board entry (membership drives visibility and total_members)', async ()=>{
273
+ mockRepo.findOne.mockResolvedValue(buildBoard({
274
+ id: 'board-1'
275
+ }));
276
+ mockMemberRepo.findOne.mockResolvedValueOnce(null);
277
+ mockMemberRepo.findOne.mockResolvedValueOnce(null);
278
+ mockMemberRepo.create.mockImplementation((data)=>data);
279
+ mockMemberRepo.save.mockResolvedValue(buildMember({
280
+ boardId: 'board-1'
281
+ }));
282
+ await service.addMember({
283
+ boardId: 'board-1',
284
+ userId: 'user-2'
285
+ }, (0, _loggedusermock.buildMockUser)());
286
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything());
287
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything(), 'board-1');
288
+ });
267
289
  it('defaults the role to MEMBER when none is given', async ()=>{
268
290
  mockRepo.findOne.mockResolvedValue(buildBoard({
269
291
  id: 'board-1'
@@ -347,6 +369,17 @@ describe('TaskBoardService', ()=>{
347
369
  deletedAt: expect.any(Date)
348
370
  });
349
371
  });
372
+ it('invalidates the board list cache and the board entry', async ()=>{
373
+ mockMemberRepo.findOne.mockResolvedValue(buildMember({
374
+ id: 'member-1'
375
+ }));
376
+ await service.removeMember({
377
+ boardId: 'board-1',
378
+ userId: 'user-uuid-1'
379
+ });
380
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything());
381
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything(), 'board-1');
382
+ });
350
383
  it('throws NotFoundException when the member is not found', async ()=>{
351
384
  mockMemberRepo.findOne.mockResolvedValue(null);
352
385
  await expect(service.removeMember({
@@ -42,6 +42,10 @@ function _ts_param(paramIndex, decorator) {
42
42
  };
43
43
  }
44
44
  let TaskListService = class TaskListService extends _classes.ApiService {
45
+ /** Boards cache denormalized `total_tasks` / `total_lists`, so board caches go stale on any write here. */ async clearCacheForAll() {
46
+ await super.clearCacheForAll();
47
+ await this.utilsService.clearCache('task_board', this.cacheManager);
48
+ }
45
49
  async convertSingleDtoToEntity(dto, _user) {
46
50
  await this.ensureDataSourceRepository();
47
51
  let entity = {};
@@ -93,7 +97,12 @@ let TaskListService = class TaskListService extends _classes.ApiService {
93
97
  }, {
94
98
  position: i * 100
95
99
  })));
96
- await this.clearCacheForAll();
100
+ await Promise.all([
101
+ this.clearCacheForAll(),
102
+ this.clearCacheForId(dto.orderedIds.map((id)=>({
103
+ id
104
+ })))
105
+ ]);
97
106
  }
98
107
  constructor(cacheManager, utilsService, dataSourceProvider){
99
108
  super('task_list', cacheManager, utilsService, TaskListService.name, true, 'task-manager', _entities.TaskList, dataSourceProvider), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider;
@@ -41,6 +41,15 @@ function mockQueryRunner(repo) {
41
41
  return qr;
42
42
  }
43
43
  describe('TaskListService', ()=>{
44
+ const mockUtilsService = {
45
+ clearCache: jest.fn(),
46
+ getCacheKey: jest.fn().mockReturnValue('cache-key'),
47
+ trackCacheKey: jest.fn()
48
+ };
49
+ // Call history is shared across tests in this file; reset it so each cache assertion is exact.
50
+ beforeEach(()=>{
51
+ mockUtilsService.clearCache.mockClear();
52
+ });
44
53
  let service;
45
54
  let mockRepo;
46
55
  beforeEach(async ()=>{
@@ -55,11 +64,7 @@ describe('TaskListService', ()=>{
55
64
  },
56
65
  {
57
66
  provide: _modules.UtilsService,
58
- useValue: {
59
- clearCache: jest.fn(),
60
- getCacheKey: jest.fn(),
61
- trackCacheKey: jest.fn()
62
- }
67
+ useValue: mockUtilsService
63
68
  },
64
69
  {
65
70
  provide: _taskmanagerdatasourceprovider.TaskManagerDataSourceProvider,
@@ -188,6 +193,19 @@ describe('TaskListService', ()=>{
188
193
  position: 200
189
194
  });
190
195
  });
196
+ it('invalidates the list cache, each reordered entry, and the parent board cache', async ()=>{
197
+ await service.reorderLists({
198
+ boardId: 'board-1',
199
+ orderedIds: [
200
+ 'list-a',
201
+ 'list-b'
202
+ ]
203
+ });
204
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_list', expect.anything());
205
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_list', expect.anything(), 'list-a');
206
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_list', expect.anything(), 'list-b');
207
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything());
208
+ });
191
209
  it('handles reordering a single-item list (position 0)', async ()=>{
192
210
  await service.reorderLists({
193
211
  boardId: 'board-1',
@@ -45,6 +45,10 @@ function _ts_param(paramIndex, decorator) {
45
45
  };
46
46
  }
47
47
  let TaskService = class TaskService extends _classes.ApiService {
48
+ /** Boards cache denormalized `total_tasks` / `total_lists`, so board caches go stale on any write here. */ async clearCacheForAll() {
49
+ await super.clearCacheForAll();
50
+ await this.utilsService.clearCache('task_board', this.cacheManager);
51
+ }
48
52
  async convertSingleDtoToEntity(dto, user) {
49
53
  await this.ensureDataSourceRepository();
50
54
  let entity = {};
@@ -185,6 +189,12 @@ let TaskService = class TaskService extends _classes.ApiService {
185
189
  }, {
186
190
  listId: dto.targetListId
187
191
  });
192
+ await Promise.all([
193
+ this.clearCacheForAll(),
194
+ this.clearCacheForId([
195
+ saved
196
+ ])
197
+ ]);
188
198
  return saved;
189
199
  }
190
200
  async assignTask(dto, user) {
@@ -221,6 +231,12 @@ let TaskService = class TaskService extends _classes.ApiService {
221
231
  }, {
222
232
  assigneeIds: dto.assigneeIds
223
233
  });
234
+ await Promise.all([
235
+ this.clearCacheForAll(),
236
+ this.clearCacheForId([
237
+ saved
238
+ ])
239
+ ]);
224
240
  saved.assignees = await this.loadAssigneesForTask(saved.id);
225
241
  return saved;
226
242
  }
@@ -49,6 +49,15 @@ function mockQueryRunner(repo) {
49
49
  return qr;
50
50
  }
51
51
  describe('TaskService', ()=>{
52
+ const mockUtilsService = {
53
+ clearCache: jest.fn(),
54
+ getCacheKey: jest.fn().mockReturnValue('cache-key'),
55
+ trackCacheKey: jest.fn()
56
+ };
57
+ // Call history is shared across tests in this file; reset it so each cache assertion is exact.
58
+ beforeEach(()=>{
59
+ mockUtilsService.clearCache.mockClear();
60
+ });
52
61
  let service;
53
62
  let mockRepo;
54
63
  let mockLabelJunctionRepo;
@@ -96,11 +105,7 @@ describe('TaskService', ()=>{
96
105
  },
97
106
  {
98
107
  provide: _modules.UtilsService,
99
- useValue: {
100
- clearCache: jest.fn(),
101
- getCacheKey: jest.fn(),
102
- trackCacheKey: jest.fn()
103
- }
108
+ useValue: mockUtilsService
104
109
  },
105
110
  {
106
111
  provide: _taskmanagerdatasourceprovider.TaskManagerDataSourceProvider,
@@ -368,6 +373,25 @@ describe('TaskService', ()=>{
368
373
  listId: 'list-new'
369
374
  });
370
375
  });
376
+ it('invalidates the task list cache and the moved task entry', async ()=>{
377
+ const task = buildTask({
378
+ id: 'task-1',
379
+ listId: 'list-old',
380
+ boardId: 'board-1'
381
+ });
382
+ mockRepo.findOne.mockResolvedValue(task);
383
+ mockRepo.save.mockImplementation((t)=>Promise.resolve(t));
384
+ await service.moveTask({
385
+ taskId: 'task-1',
386
+ targetListId: 'list-new',
387
+ position: 1
388
+ }, (0, _loggedusermock.buildMockUser)({
389
+ id: 'mover-1'
390
+ }));
391
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task', expect.anything());
392
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task', expect.anything(), 'task-1');
393
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything());
394
+ });
371
395
  it('defaults position to 0 (first position) when no position is given', async ()=>{
372
396
  const task = buildTask({
373
397
  id: 'task-1',
@@ -132,11 +132,13 @@ export class TaskBoardService extends ApiService {
132
132
  }, {
133
133
  role: dto.role ?? BoardMemberRole.MEMBER
134
134
  });
135
- return memberRepo.findOne({
135
+ const restored = await memberRepo.findOne({
136
136
  where: {
137
137
  id: softDeleted.id
138
138
  }
139
139
  });
140
+ await this.invalidateBoardCache(dto.boardId);
141
+ return restored;
140
142
  }
141
143
  const member = memberRepo.create({
142
144
  boardId: dto.boardId,
@@ -144,7 +146,9 @@ export class TaskBoardService extends ApiService {
144
146
  role: dto.role ?? BoardMemberRole.MEMBER,
145
147
  createdById: user.id
146
148
  });
147
- return memberRepo.save(member);
149
+ const saved = await memberRepo.save(member);
150
+ await this.invalidateBoardCache(dto.boardId);
151
+ return saved;
148
152
  }
149
153
  async removeMember(dto) {
150
154
  const memberRepo = await this.ensureMemberRepository();
@@ -164,6 +168,7 @@ export class TaskBoardService extends ApiService {
164
168
  }, {
165
169
  deletedAt: new Date()
166
170
  });
171
+ await this.invalidateBoardCache(dto.boardId);
167
172
  }
168
173
  async getMembers(boardId) {
169
174
  const memberRepo = await this.ensureMemberRepository();
@@ -197,6 +202,16 @@ export class TaskBoardService extends ApiService {
197
202
  createdById: user.id
198
203
  })));
199
204
  }
205
+ async invalidateBoardCache(boardId) {
206
+ await Promise.all([
207
+ this.clearCacheForAll(),
208
+ this.clearCacheForId([
209
+ {
210
+ id: boardId
211
+ }
212
+ ])
213
+ ]);
214
+ }
200
215
  async ensureMemberRepository() {
201
216
  if (!this.memberRepository) {
202
217
  this.memberRepository = await this.dataSourceProvider.getRepository(TaskBoardMember);
@@ -53,6 +53,15 @@ function mockQueryRunner(repo) {
53
53
  return qr;
54
54
  }
55
55
  describe('TaskBoardService', ()=>{
56
+ const mockUtilsService = {
57
+ clearCache: jest.fn(),
58
+ getCacheKey: jest.fn().mockReturnValue('cache-key'),
59
+ trackCacheKey: jest.fn()
60
+ };
61
+ // Call history is shared across tests in this file; reset it so each cache assertion is exact.
62
+ beforeEach(()=>{
63
+ mockUtilsService.clearCache.mockClear();
64
+ });
56
65
  let service;
57
66
  let mockRepo;
58
67
  let mockMemberRepo;
@@ -80,11 +89,7 @@ describe('TaskBoardService', ()=>{
80
89
  },
81
90
  {
82
91
  provide: UtilsService,
83
- useValue: {
84
- clearCache: jest.fn(),
85
- getCacheKey: jest.fn(),
86
- trackCacheKey: jest.fn()
87
- }
92
+ useValue: mockUtilsService
88
93
  },
89
94
  {
90
95
  provide: TaskManagerConfigService,
@@ -260,6 +265,23 @@ describe('TaskBoardService', ()=>{
260
265
  }));
261
266
  expect(result).toEqual(created);
262
267
  });
268
+ it('invalidates the board list cache and the board entry (membership drives visibility and total_members)', async ()=>{
269
+ mockRepo.findOne.mockResolvedValue(buildBoard({
270
+ id: 'board-1'
271
+ }));
272
+ mockMemberRepo.findOne.mockResolvedValueOnce(null);
273
+ mockMemberRepo.findOne.mockResolvedValueOnce(null);
274
+ mockMemberRepo.create.mockImplementation((data)=>data);
275
+ mockMemberRepo.save.mockResolvedValue(buildMember({
276
+ boardId: 'board-1'
277
+ }));
278
+ await service.addMember({
279
+ boardId: 'board-1',
280
+ userId: 'user-2'
281
+ }, buildMockUser());
282
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything());
283
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything(), 'board-1');
284
+ });
263
285
  it('defaults the role to MEMBER when none is given', async ()=>{
264
286
  mockRepo.findOne.mockResolvedValue(buildBoard({
265
287
  id: 'board-1'
@@ -343,6 +365,17 @@ describe('TaskBoardService', ()=>{
343
365
  deletedAt: expect.any(Date)
344
366
  });
345
367
  });
368
+ it('invalidates the board list cache and the board entry', async ()=>{
369
+ mockMemberRepo.findOne.mockResolvedValue(buildMember({
370
+ id: 'member-1'
371
+ }));
372
+ await service.removeMember({
373
+ boardId: 'board-1',
374
+ userId: 'user-uuid-1'
375
+ });
376
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything());
377
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything(), 'board-1');
378
+ });
346
379
  it('throws NotFoundException when the member is not found', async ()=>{
347
380
  mockMemberRepo.findOne.mockResolvedValue(null);
348
381
  await expect(service.removeMember({
@@ -32,6 +32,10 @@ import { TASK_LIST_MESSAGES } from '../config';
32
32
  import { TaskList } from '../entities';
33
33
  import { TaskManagerDataSourceProvider } from './task-manager-datasource.provider';
34
34
  export class TaskListService extends ApiService {
35
+ /** Boards cache denormalized `total_tasks` / `total_lists`, so board caches go stale on any write here. */ async clearCacheForAll() {
36
+ await super.clearCacheForAll();
37
+ await this.utilsService.clearCache('task_board', this.cacheManager);
38
+ }
35
39
  async convertSingleDtoToEntity(dto, _user) {
36
40
  await this.ensureDataSourceRepository();
37
41
  let entity = {};
@@ -83,7 +87,12 @@ export class TaskListService extends ApiService {
83
87
  }, {
84
88
  position: i * 100
85
89
  })));
86
- await this.clearCacheForAll();
90
+ await Promise.all([
91
+ this.clearCacheForAll(),
92
+ this.clearCacheForId(dto.orderedIds.map((id)=>({
93
+ id
94
+ })))
95
+ ]);
87
96
  }
88
97
  constructor(cacheManager, utilsService, dataSourceProvider){
89
98
  super('task_list', cacheManager, utilsService, TaskListService.name, true, 'task-manager', TaskList, dataSourceProvider), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider;
@@ -37,6 +37,15 @@ function mockQueryRunner(repo) {
37
37
  return qr;
38
38
  }
39
39
  describe('TaskListService', ()=>{
40
+ const mockUtilsService = {
41
+ clearCache: jest.fn(),
42
+ getCacheKey: jest.fn().mockReturnValue('cache-key'),
43
+ trackCacheKey: jest.fn()
44
+ };
45
+ // Call history is shared across tests in this file; reset it so each cache assertion is exact.
46
+ beforeEach(()=>{
47
+ mockUtilsService.clearCache.mockClear();
48
+ });
40
49
  let service;
41
50
  let mockRepo;
42
51
  beforeEach(async ()=>{
@@ -51,11 +60,7 @@ describe('TaskListService', ()=>{
51
60
  },
52
61
  {
53
62
  provide: UtilsService,
54
- useValue: {
55
- clearCache: jest.fn(),
56
- getCacheKey: jest.fn(),
57
- trackCacheKey: jest.fn()
58
- }
63
+ useValue: mockUtilsService
59
64
  },
60
65
  {
61
66
  provide: TaskManagerDataSourceProvider,
@@ -184,6 +189,19 @@ describe('TaskListService', ()=>{
184
189
  position: 200
185
190
  });
186
191
  });
192
+ it('invalidates the list cache, each reordered entry, and the parent board cache', async ()=>{
193
+ await service.reorderLists({
194
+ boardId: 'board-1',
195
+ orderedIds: [
196
+ 'list-a',
197
+ 'list-b'
198
+ ]
199
+ });
200
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_list', expect.anything());
201
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_list', expect.anything(), 'list-a');
202
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_list', expect.anything(), 'list-b');
203
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything());
204
+ });
187
205
  it('handles reordering a single-item list (position 0)', async ()=>{
188
206
  await service.reorderLists({
189
207
  boardId: 'board-1',
@@ -35,6 +35,10 @@ import { TaskActivityService } from './task-activity.service';
35
35
  import { TaskBoardService } from './task-board.service';
36
36
  import { TaskManagerDataSourceProvider } from './task-manager-datasource.provider';
37
37
  export class TaskService extends ApiService {
38
+ /** Boards cache denormalized `total_tasks` / `total_lists`, so board caches go stale on any write here. */ async clearCacheForAll() {
39
+ await super.clearCacheForAll();
40
+ await this.utilsService.clearCache('task_board', this.cacheManager);
41
+ }
38
42
  async convertSingleDtoToEntity(dto, user) {
39
43
  await this.ensureDataSourceRepository();
40
44
  let entity = {};
@@ -175,6 +179,12 @@ export class TaskService extends ApiService {
175
179
  }, {
176
180
  listId: dto.targetListId
177
181
  });
182
+ await Promise.all([
183
+ this.clearCacheForAll(),
184
+ this.clearCacheForId([
185
+ saved
186
+ ])
187
+ ]);
178
188
  return saved;
179
189
  }
180
190
  async assignTask(dto, user) {
@@ -211,6 +221,12 @@ export class TaskService extends ApiService {
211
221
  }, {
212
222
  assigneeIds: dto.assigneeIds
213
223
  });
224
+ await Promise.all([
225
+ this.clearCacheForAll(),
226
+ this.clearCacheForId([
227
+ saved
228
+ ])
229
+ ]);
214
230
  saved.assignees = await this.loadAssigneesForTask(saved.id);
215
231
  return saved;
216
232
  }
@@ -45,6 +45,15 @@ function mockQueryRunner(repo) {
45
45
  return qr;
46
46
  }
47
47
  describe('TaskService', ()=>{
48
+ const mockUtilsService = {
49
+ clearCache: jest.fn(),
50
+ getCacheKey: jest.fn().mockReturnValue('cache-key'),
51
+ trackCacheKey: jest.fn()
52
+ };
53
+ // Call history is shared across tests in this file; reset it so each cache assertion is exact.
54
+ beforeEach(()=>{
55
+ mockUtilsService.clearCache.mockClear();
56
+ });
48
57
  let service;
49
58
  let mockRepo;
50
59
  let mockLabelJunctionRepo;
@@ -92,11 +101,7 @@ describe('TaskService', ()=>{
92
101
  },
93
102
  {
94
103
  provide: UtilsService,
95
- useValue: {
96
- clearCache: jest.fn(),
97
- getCacheKey: jest.fn(),
98
- trackCacheKey: jest.fn()
99
- }
104
+ useValue: mockUtilsService
100
105
  },
101
106
  {
102
107
  provide: TaskManagerDataSourceProvider,
@@ -364,6 +369,25 @@ describe('TaskService', ()=>{
364
369
  listId: 'list-new'
365
370
  });
366
371
  });
372
+ it('invalidates the task list cache and the moved task entry', async ()=>{
373
+ const task = buildTask({
374
+ id: 'task-1',
375
+ listId: 'list-old',
376
+ boardId: 'board-1'
377
+ });
378
+ mockRepo.findOne.mockResolvedValue(task);
379
+ mockRepo.save.mockImplementation((t)=>Promise.resolve(t));
380
+ await service.moveTask({
381
+ taskId: 'task-1',
382
+ targetListId: 'list-new',
383
+ position: 1
384
+ }, buildMockUser({
385
+ id: 'mover-1'
386
+ }));
387
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task', expect.anything());
388
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task', expect.anything(), 'task-1');
389
+ expect(mockUtilsService.clearCache).toHaveBeenCalledWith('task_board', expect.anything());
390
+ });
367
391
  it('defaults position to 0 (first position) when no position is given', async ()=>{
368
392
  const task = buildTask({
369
393
  id: 'task-1',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/nestjs-task-manager",
3
- "version": "6.3.0",
3
+ "version": "6.4.1",
4
4
  "description": "Task management module with dynamic boards and real-time collaboration",
5
5
  "main": "cjs/index.js",
6
6
  "module": "fesm/index.js",
@@ -85,7 +85,7 @@
85
85
  "socket.io": "^4.8.0"
86
86
  },
87
87
  "dependencies": {
88
- "@flusys/nestjs-core": "6.3.0",
89
- "@flusys/nestjs-shared": "6.3.0"
88
+ "@flusys/nestjs-core": "6.4.1",
89
+ "@flusys/nestjs-shared": "6.4.1"
90
90
  }
91
91
  }
@@ -28,5 +28,6 @@ export declare class TaskBoardService extends ApiService<CreateTaskBoardDto, Upd
28
28
  })[]>;
29
29
  isBoardMember(boardId: string, userId: string): Promise<boolean>;
30
30
  protected afterInsertOperation(entities: TaskBoardBase[], _dto: CreateTaskBoardDto | Array<CreateTaskBoardDto>, user: ILoggedUserInfo | null, _qr: any): Promise<void>;
31
+ private invalidateBoardCache;
31
32
  private ensureMemberRepository;
32
33
  }
@@ -10,6 +10,7 @@ export declare class TaskListService extends ApiService<CreateTaskListDto, Updat
10
10
  protected utilsService: UtilsService;
11
11
  private readonly dataSourceProvider;
12
12
  constructor(cacheManager: HybridCache, utilsService: UtilsService, dataSourceProvider: TaskManagerDataSourceProvider);
13
+ clearCacheForAll(): Promise<void>;
13
14
  convertSingleDtoToEntity(dto: CreateTaskListDto | UpdateTaskListDto, _user: ILoggedUserInfo | null): Promise<TaskList>;
14
15
  protected getFilterQuery(query: SelectQueryBuilder<TaskList>, filter: Record<string, unknown>, _user: ILoggedUserInfo | null): Promise<{
15
16
  query: SelectQueryBuilder<TaskList>;
@@ -20,6 +20,7 @@ export declare class TaskService extends ApiService<CreateTaskDto, UpdateTaskDto
20
20
  private pendingLabelIds;
21
21
  private pendingAssigneeIds;
22
22
  constructor(cacheManager: HybridCache, utilsService: UtilsService, dataSourceProvider: TaskManagerDataSourceProvider, boardService: TaskBoardService, activityService: TaskActivityService);
23
+ clearCacheForAll(): Promise<void>;
23
24
  convertSingleDtoToEntity(dto: CreateTaskDto | UpdateTaskDto, user: ILoggedUserInfo | null): Promise<Task>;
24
25
  protected afterInsertOperation(entities: Task[], _dto: CreateTaskDto | Array<CreateTaskDto>, user: ILoggedUserInfo | null, _qr: QueryRunner): Promise<void>;
25
26
  protected afterUpdateOperation(entities: Task[], _dto: UpdateTaskDto | Array<UpdateTaskDto>, _user: ILoggedUserInfo | null, _qr: QueryRunner): Promise<void>;