Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/neat-hornets-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@opennextjs/cloudflare": patch
---

Add a new `withFilter` tag cache to allow to filter the tag cache used
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Add a new `withFilter` tag cache to allow to filter the tag cache used
Add a new `withFilter` tag cache to allow to filter the tags used

Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { NextModeTagCache } from "@opennextjs/aws/types/overrides";
import { beforeEach, describe, expect, it, vi } from "vitest";

import { softTagFilter, withFilter } from "./tag-cache-filter";

const mockedTagCache = {
name: "mocked",
mode: "nextMode",
getPathsByTags: vi.fn(),
hasBeenRevalidated: vi.fn(),
writeTags: vi.fn(),
} satisfies NextModeTagCache;

const filterFn = (tag: string) => tag.startsWith("valid_");

describe("withFilter", () => {
beforeEach(() => {
vi.clearAllMocks();
});

it("should filter out tags based on writeTags", async () => {
const tagCache = withFilter({
originalTagCache: mockedTagCache,
filterFn,
});

const tags = ["valid_tag", "invalid_tag"];

await tagCache.writeTags(tags);
expect(mockedTagCache.writeTags).toHaveBeenCalledWith(["valid_tag"]);
});

it("should not call writeTags if no tags are valid", async () => {
const tagCache = withFilter({
originalTagCache: mockedTagCache,
filterFn,
});
const tags = ["invalid_tag"];
await tagCache.writeTags(tags);
expect(mockedTagCache.writeTags).not.toHaveBeenCalled();
});

it("should filter out tags based on hasBeenRevalidated", async () => {
const tagCache = withFilter({
originalTagCache: mockedTagCache,
filterFn,
});

const tags = ["valid_tag", "invalid_tag"];
const lastModified = Date.now();

await tagCache.hasBeenRevalidated(tags, lastModified);
expect(mockedTagCache.hasBeenRevalidated).toHaveBeenCalledWith(["valid_tag"], lastModified);
});

it("should not call hasBeenRevalidated if no tags are valid", async () => {
const tagCache = withFilter({
originalTagCache: mockedTagCache,
filterFn,
});
const tags = ["invalid_tag"];
const lastModified = Date.now();
await tagCache.hasBeenRevalidated(tags, lastModified);
expect(mockedTagCache.hasBeenRevalidated).not.toHaveBeenCalled();
});

it("should filter out tags based on getPathsByTags", async () => {
const tagCache = withFilter({
originalTagCache: mockedTagCache,
filterFn,
});

const tags = ["valid_tag", "invalid_tag"];

await tagCache.getPathsByTags?.(tags);
expect(mockedTagCache.getPathsByTags).toHaveBeenCalledWith(["valid_tag"]);
});

it("should not call getPathsByTags if no tags are valid", async () => {
const tagCache = withFilter({
originalTagCache: mockedTagCache,
filterFn,
});
const tags = ["invalid_tag"];
await tagCache.getPathsByTags?.(tags);
expect(mockedTagCache.getPathsByTags).not.toHaveBeenCalled();
});

it("should return the correct name", () => {
const tagCache = withFilter({
originalTagCache: mockedTagCache,
filterFn,
});

expect(tagCache.name).toBe("filtered-mocked");
});

it("should not create a function if getPathsByTags is not defined", async () => {
const tagCache = withFilter({
originalTagCache: {
...mockedTagCache,
getPathsByTags: undefined,
},
filterFn,
});

expect(tagCache.getPathsByTags).toBeUndefined();
});

it("should properly filter soft tags", () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it("should properly filter soft tags", () => {
it("should filter soft tags", () => {

const tagCache = withFilter({
originalTagCache: mockedTagCache,
filterFn: softTagFilter,
});

tagCache.writeTags(["valid_tag", "_N_T_/", "_N_T_/test", "_N_T_/layout"]);
expect(mockedTagCache.writeTags).toHaveBeenCalledWith(["valid_tag"]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { NextModeTagCache } from "@opennextjs/aws/types/overrides";

interface WithFilterOptions {
/**
* The original tag cache.
* Call to this will receive only the filtered tags.
*/
originalTagCache: NextModeTagCache;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe?

Suggested change
/**
* The original tag cache.
* Call to this will receive only the filtered tags.
*/
originalTagCache: NextModeTagCache;
/**
* The wrapped tag cache.
* Call to this will receive only the filtered tags.
*/
tagCache: NextModeTagCache;

/**
* The function to filter tags.
* @param tag The tag to filter.
* @returns true if the tag should be forwarde, false otherwise.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @returns true if the tag should be forwarde, false otherwise.
* @returns true if the tag should be forwarded, false otherwise.

*/
filterFn: (tag: string) => boolean;
}

/**
* Creates a new tag cache that filters tags based on the provided filter function.
* This is usefult to remove tags that are not used by the app, this could reduce the number of request to the underlying tag cache.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* This is usefult to remove tags that are not used by the app, this could reduce the number of request to the underlying tag cache.
* This is usefull to remove tags that are not used by the app, this could reduce the number of requests to the underlying tag cache.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useful

*/
export function withFilter({ originalTagCache, filterFn }: WithFilterOptions): NextModeTagCache {
return {
name: `filtered-${originalTagCache.name}`,
mode: "nextMode",
getPathsByTags: originalTagCache.getPathsByTags
? async (tags) => {
const filteredTags = tags.filter(filterFn);
if (filteredTags.length === 0) {
return [];
}
return originalTagCache.getPathsByTags!(filteredTags);
}
: undefined,
hasBeenRevalidated: async (tags, lastModified) => {
const filteredTags = tags.filter(filterFn);
if (filteredTags.length === 0) {
return false;
}
return originalTagCache.hasBeenRevalidated(filteredTags, lastModified);
},
writeTags: async (tags) => {
const filteredTags = tags.filter(filterFn);
if (filteredTags.length === 0) {
return;
}
return originalTagCache.writeTags(filteredTags);
},
};
}

/**
* Filter function to exclude tags that start with "_N_T_".
* This is used to filter out internal soft tags.
* Can be used if `revalidatePath` is not used.
*/
export function softTagFilter(tag: string): boolean {
return !tag.startsWith("_N_T_");
}