Skip to content

Commit 7720cdb

Browse files
Merge pull request #72 from bloomuporg/main
fix: allow revalidateTags to also be called with an array of tags
2 parents 9aadc2d + 59aaf09 commit 7720cdb

File tree

2 files changed

+109
-30
lines changed

2 files changed

+109
-30
lines changed

packages/cache-core/__tests__/cacheHandler.spec.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,22 @@ describe('CacheHandler', () => {
372372
expect(mockLogger.info).toHaveBeenNthCalledWith(1, `Revalidate by path ${mockCacheKey}`)
373373
})
374374

375+
it('should revalidate multiple paths when passed as array', async () => {
376+
Cache.setConfig({
377+
logger: mockLogger,
378+
cache: new FileSystemCache()
379+
})
380+
const cacheHandler = new Cache(mockNextHandlerContext)
381+
const paths = [`${NEXT_CACHE_IMPLICIT_TAG_ID}path1`, `${NEXT_CACHE_IMPLICIT_TAG_ID}path2`]
382+
383+
await cacheHandler.revalidateTag(paths)
384+
385+
expect(mockLogger.info).toHaveBeenCalledTimes(2)
386+
expect(mockLogger.info).toHaveBeenNthCalledWith(1, 'Revalidate by path path1')
387+
expect(mockLogger.info).toHaveBeenNthCalledWith(2, 'Revalidate by path path2')
388+
expect(mockDeleteAllByKeyMatchData).toHaveBeenCalledTimes(2)
389+
})
390+
375391
it('should log when revalidate tag', async () => {
376392
Cache.setConfig({
377393
logger: mockLogger,
@@ -385,6 +401,41 @@ describe('CacheHandler', () => {
385401
expect(mockLogger.info).toHaveBeenNthCalledWith(1, `Revalidate by tag ${mockCacheKey}`)
386402
})
387403

404+
it('should revalidate multiple tags when passed as array', async () => {
405+
Cache.setConfig({
406+
logger: mockLogger,
407+
cache: new FileSystemCache()
408+
})
409+
const cacheHandler = new Cache(mockNextHandlerContext)
410+
const tags = ['tag1', 'tag2', 'tag3']
411+
412+
await cacheHandler.revalidateTag(tags)
413+
414+
expect(mockLogger.info).toHaveBeenCalledTimes(3)
415+
expect(mockLogger.info).toHaveBeenNthCalledWith(1, 'Revalidate by tag tag1')
416+
expect(mockLogger.info).toHaveBeenNthCalledWith(2, 'Revalidate by tag tag2')
417+
expect(mockLogger.info).toHaveBeenNthCalledWith(3, 'Revalidate by tag tag3')
418+
expect(mockRevalidateTagData).toHaveBeenCalledTimes(3)
419+
})
420+
421+
it('should handle mixed array of paths and regular tags', async () => {
422+
Cache.setConfig({
423+
logger: mockLogger,
424+
cache: new FileSystemCache()
425+
})
426+
const cacheHandler = new Cache(mockNextHandlerContext)
427+
const tags = [`${NEXT_CACHE_IMPLICIT_TAG_ID}path1`, 'regularTag', `${NEXT_CACHE_IMPLICIT_TAG_ID}path2`]
428+
429+
await cacheHandler.revalidateTag(tags)
430+
431+
expect(mockLogger.info).toHaveBeenCalledTimes(3)
432+
expect(mockLogger.info).toHaveBeenNthCalledWith(1, 'Revalidate by path path1')
433+
expect(mockLogger.info).toHaveBeenNthCalledWith(2, 'Revalidate by tag regularTag')
434+
expect(mockLogger.info).toHaveBeenNthCalledWith(3, 'Revalidate by path path2')
435+
expect(mockDeleteAllByKeyMatchData).toHaveBeenCalledTimes(2)
436+
expect(mockRevalidateTagData).toHaveBeenCalledTimes(1)
437+
})
438+
388439
it('should log when failed to revalidate tag', async () => {
389440
const errorMessage = 'error revalidate'
390441
mockRevalidateTagData.mockRejectedValueOnce(errorMessage)
@@ -404,6 +455,31 @@ describe('CacheHandler', () => {
404455
expect(mockLogger.error).toHaveBeenCalledWith(`Failed revalidate by ${mockCacheKey}`, errorMessage)
405456
})
406457

458+
it('should continue processing tags in array if one fails', async () => {
459+
const errorMessage = 'error revalidate'
460+
mockRevalidateTagData.mockRejectedValueOnce(errorMessage)
461+
462+
Cache.setConfig({
463+
logger: mockLogger,
464+
cache: new FileSystemCache()
465+
})
466+
const cacheHandler = new Cache(mockNextHandlerContext)
467+
const tags = ['tag1', 'tag2', 'tag3']
468+
469+
await cacheHandler.revalidateTag(tags)
470+
471+
expect(mockLogger.info).toHaveBeenCalledTimes(3)
472+
expect(mockLogger.info).toHaveBeenNthCalledWith(1, 'Revalidate by tag tag1')
473+
expect(mockLogger.info).toHaveBeenNthCalledWith(2, 'Revalidate by tag tag2')
474+
expect(mockLogger.info).toHaveBeenNthCalledWith(3, 'Revalidate by tag tag3')
475+
476+
expect(mockLogger.error).toHaveBeenCalledTimes(1)
477+
expect(mockLogger.error).toHaveBeenCalledWith('Failed revalidate by tag1', errorMessage)
478+
479+
// Should continue with other tags despite the error
480+
expect(mockRevalidateTagData).toHaveBeenCalledTimes(3)
481+
})
482+
407483
it('should log when failed to revalidate path', async () => {
408484
const errorMessage = 'error revalidate'
409485
mockDeleteAllByKeyMatchData.mockRejectedValueOnce(errorMessage)

packages/cache-core/src/cacheHandler.ts

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -241,39 +241,42 @@ export class Cache implements CacheHandler {
241241
}
242242
}
243243

244-
async revalidateTag(tag: string) {
245-
try {
246-
if (tag.startsWith(NEXT_CACHE_IMPLICIT_TAG_ID)) {
247-
const [path, query] = tag.slice(NEXT_CACHE_IMPLICIT_TAG_ID.length).split('?')
248-
Cache.config.logger.info(`Revalidate by path ${path}`)
249-
250-
if (query) {
251-
try {
252-
this.queryCacheKey = this.buildCacheKey(
253-
Cache.config.cacheQueries.toSorted(),
254-
Object.fromEntries(new URLSearchParams(query)),
255-
'query'
256-
)
257-
} catch (err) {
258-
Cache.config.logger.error('Error building cache key from path', err)
244+
async revalidateTag(tags: string | string[]) {
245+
const tagArray = Array.isArray(tags) ? tags : [tags]
246+
247+
for (const tag of tagArray) {
248+
try {
249+
if (tag.startsWith(NEXT_CACHE_IMPLICIT_TAG_ID)) {
250+
const [path, query] = tag.slice(NEXT_CACHE_IMPLICIT_TAG_ID.length).split('?')
251+
Cache.config.logger.info(`Revalidate by path ${path}`)
252+
253+
if (query) {
254+
try {
255+
this.queryCacheKey = this.buildCacheKey(
256+
Cache.config.cacheQueries.toSorted(),
257+
Object.fromEntries(new URLSearchParams(query)),
258+
'query'
259+
)
260+
} catch (err) {
261+
Cache.config.logger.error('Error building cache key from path', err)
262+
}
259263
}
260-
}
261-
262-
const pageKey = this.removeSlashFromStart(path)
263264

264-
await Cache.config.cache.deleteAllByKeyMatch(!pageKey.length ? 'index' : pageKey, this.buildPageCacheKey(), {
265-
serverCacheDirPath: this.serverCacheDirPath
266-
})
267-
} else {
268-
Cache.config.logger.info(`Revalidate by tag ${tag}`)
269-
// TODO: (ISSUE-1650)
270-
await Cache.config.cache.revalidateTag(tag, [], {
271-
serverCacheDirPath: this.serverCacheDirPath
272-
})
265+
const pageKey = this.removeSlashFromStart(path)
266+
267+
await Cache.config.cache.deleteAllByKeyMatch(!pageKey.length ? 'index' : pageKey, this.buildPageCacheKey(), {
268+
serverCacheDirPath: this.serverCacheDirPath
269+
})
270+
} else {
271+
Cache.config.logger.info(`Revalidate by tag ${tag}`)
272+
// TODO: (ISSUE-1650)
273+
await Cache.config.cache.revalidateTag(tag, [], {
274+
serverCacheDirPath: this.serverCacheDirPath
275+
})
276+
}
277+
} catch (err) {
278+
Cache.config.logger.error(`Failed revalidate by ${tag}`, err)
273279
}
274-
} catch (err) {
275-
Cache.config.logger.error(`Failed revalidate by ${tag}`, err)
276-
return
277280
}
278281
}
279282

0 commit comments

Comments
 (0)