This commit is contained in:
Tom Moor
2025-12-21 09:46:26 -05:00
parent f9f22be4c5
commit e0033b2288
3 changed files with 118 additions and 1 deletions
@@ -1007,6 +1007,102 @@ describe("#documents.list", () => {
expect(body.data.length).toEqual(1);
});
it("should allow advanced filtering", async () => {
const user = await buildUser();
await buildDocument({
title: "First document",
userId: user.id,
teamId: user.teamId,
});
await buildDocument({
title: "Second document",
userId: user.id,
teamId: user.teamId,
});
const res = await server.post("/api/documents.list", {
body: {
token: user.getJwtToken(),
filter: {
field: "title",
operator: "contains",
value: "First",
},
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.data.length).toEqual(1);
expect(body.data[0].title).toEqual("First document");
});
it("should allow advanced filtering with logical operators", async () => {
const user = await buildUser();
await buildDocument({
title: "First document",
userId: user.id,
teamId: user.teamId,
});
await buildDocument({
title: "Second document",
userId: user.id,
teamId: user.teamId,
});
const res = await server.post("/api/documents.list", {
body: {
token: user.getJwtToken(),
filter: {
operator: "OR",
filters: [
{
field: "title",
operator: "eq",
value: "First document",
},
{
field: "title",
operator: "eq",
value: "Second document",
},
],
},
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.data.length).toEqual(2);
});
it("should allow filtering to include archived", async () => {
const user = await buildUser();
await buildDocument({
title: "First document",
userId: user.id,
teamId: user.teamId,
});
await buildDocument({
title: "Second document",
userId: user.id,
teamId: user.teamId,
archivedAt: new Date(),
});
const res = await server.post("/api/documents.list", {
body: {
token: user.getJwtToken(),
filter: {
field: "archivedAt",
operator: "isNotNull",
value: true,
},
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.data.length).toEqual(1);
});
it("should allow filtering to private collection", async () => {
const user = await buildUser();
const collection = await buildCollection({
+7 -1
View File
@@ -58,6 +58,7 @@ import { DocumentHelper } from "@server/models/helpers/DocumentHelper";
import { ProsemirrorHelper } from "@server/models/helpers/ProsemirrorHelper";
import SearchHelper from "@server/models/helpers/SearchHelper";
import { TextHelper } from "@server/models/helpers/TextHelper";
import { buildWhere } from "@server/models/filters/Filters";
import { authorize, can, cannot } from "@server/policies";
import {
presentDocument,
@@ -97,6 +98,7 @@ router.post(
parentDocumentId,
userId: createdById,
statusFilter,
filter,
} = ctx.input.body;
// always filter by the current team
@@ -114,8 +116,12 @@ router.post(
],
};
if (filter) {
where[Op.and].push(buildWhere(filter));
}
// Exclude archived docs by default
if (!statusFilter) {
if (!statusFilter && !filter) {
where[Op.and].push({ archivedAt: { [Op.eq]: null } });
}
+15
View File
@@ -5,6 +5,7 @@ import { DocumentPermission, StatusFilter } from "@shared/types";
import { BaseSchema } from "@server/routes/api/schema";
import { zodIconType, zodIdType, zodShareIdType } from "@server/utils/zod";
import { ValidateColor } from "@server/validation";
import { builderFilterSchema } from "@shared/helpers/FilterHelper";
const DocumentsSortParamsSchema = z.object({
/** Specifies the attributes by which documents will be sorted in the list */
@@ -69,6 +70,17 @@ const BaseIdSchema = z.object({
id: zodIdType(),
});
const filter = builderFilterSchema(
z.enum([
"createdAt",
"updatedAt",
"archivedAt",
"publishedAt",
"title",
"templateId",
])
);
export const DocumentsListSchema = BaseSchema.extend({
body: DocumentsSortParamsSchema.extend({
/** Id of the user who created the doc */
@@ -94,6 +106,9 @@ export const DocumentsListSchema = BaseSchema.extend({
/** Document statuses to include in results */
statusFilter: z.nativeEnum(StatusFilter).array().optional(),
/** Advanced filters */
filter: filter.FilterSchema.optional(),
}),
// Maintains backwards compatibility
}).transform((req) => {