aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorxuatz <xzlow10@gmail.com>2025-05-19 00:18:58 +0900
committerGitHub <noreply@github.com>2025-05-18 16:18:58 +0100
commit4e06ea7bdbaaa196da5c3e2a755aeefb25cf4228 (patch)
tree1f9aab52078eaa485430cfc6e514c655da122ea5
parent70d572096706cb2d7f5d3f29b51e6ba1d86578c6 (diff)
downloadkarakeep-4e06ea7bdbaaa196da5c3e2a755aeefb25cf4228.tar.zst
feat(api): enable ?sortOrder= for relevant resources (#1398)
* feat(api): enable `?sortOrder=` for relevant resources * fix tests
-rw-r--r--.prettierignore1
-rw-r--r--apps/web/app/api/v1/bookmarks/[bookmarkId]/route.ts4
-rw-r--r--apps/web/app/api/v1/bookmarks/route.ts9
-rw-r--r--apps/web/app/api/v1/bookmarks/search/route.ts1
-rw-r--r--apps/web/app/api/v1/lists/[listId]/bookmarks/route.ts4
-rw-r--r--apps/web/app/api/v1/tags/[tagId]/bookmarks/route.ts5
-rw-r--r--apps/web/app/api/v1/utils/types.ts12
-rw-r--r--packages/open-api/karakeep-openapi-spec.json61
-rw-r--r--packages/open-api/lib/bookmarks.ts6
-rw-r--r--packages/open-api/lib/lists.ts15
-rw-r--r--packages/open-api/lib/tags.ts15
-rw-r--r--packages/open-api/package.json1
-rw-r--r--packages/open-api/tsconfig.json2
-rw-r--r--packages/sdk/src/karakeep-api.d.ts24
14 files changed, 126 insertions, 34 deletions
diff --git a/.prettierignore b/.prettierignore
index 0acd8bfc..b9bf8b46 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -10,6 +10,7 @@ dist
.env
.env.*
**/migrations/**
+packages/open-api/karakeep-openapi-spec.json
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
diff --git a/apps/web/app/api/v1/bookmarks/[bookmarkId]/route.ts b/apps/web/app/api/v1/bookmarks/[bookmarkId]/route.ts
index fa551894..9ad18fd3 100644
--- a/apps/web/app/api/v1/bookmarks/[bookmarkId]/route.ts
+++ b/apps/web/app/api/v1/bookmarks/[bookmarkId]/route.ts
@@ -3,7 +3,7 @@ import { buildHandler } from "@/app/api/v1/utils/handler";
import { zUpdateBookmarksRequestSchema } from "@karakeep/shared/types/bookmarks";
-import { zGetBookmarkSearchParamsSchema } from "../../utils/types";
+import { zGetBookmarkQueryParamsSchema } from "../../utils/types";
export const dynamic = "force-dynamic";
@@ -13,7 +13,7 @@ export const GET = (
) =>
buildHandler({
req,
- searchParamsSchema: zGetBookmarkSearchParamsSchema,
+ searchParamsSchema: zGetBookmarkQueryParamsSchema,
handler: async ({ api, searchParams }) => {
const bookmark = await api.bookmarks.getBookmark({
bookmarkId: params.bookmarkId,
diff --git a/apps/web/app/api/v1/bookmarks/route.ts b/apps/web/app/api/v1/bookmarks/route.ts
index 1605d2b5..4df4f6ad 100644
--- a/apps/web/app/api/v1/bookmarks/route.ts
+++ b/apps/web/app/api/v1/bookmarks/route.ts
@@ -1,7 +1,10 @@
import { NextRequest } from "next/server";
import { z } from "zod";
-import { zNewBookmarkRequestSchema } from "@karakeep/shared/types/bookmarks";
+import {
+ zNewBookmarkRequestSchema,
+ zSortOrder,
+} from "@karakeep/shared/types/bookmarks";
import { buildHandler } from "../utils/handler";
import { adaptPagination, zPagination } from "../utils/pagination";
@@ -16,6 +19,10 @@ export const GET = (req: NextRequest) =>
.object({
favourited: zStringBool.optional(),
archived: zStringBool.optional(),
+ sortOrder: zSortOrder
+ .exclude([zSortOrder.Enum.relevance])
+ .optional()
+ .default(zSortOrder.Enum.desc),
// TODO: Change the default to false in a couple of releases.
includeContent: zStringBool.optional().default("true"),
})
diff --git a/apps/web/app/api/v1/bookmarks/search/route.ts b/apps/web/app/api/v1/bookmarks/search/route.ts
index 52081c7f..e85c7954 100644
--- a/apps/web/app/api/v1/bookmarks/search/route.ts
+++ b/apps/web/app/api/v1/bookmarks/search/route.ts
@@ -27,6 +27,7 @@ export const GET = (req: NextRequest) =>
const bookmarks = await api.bookmarks.searchBookmarks({
text: searchParams.q,
cursor: searchParams.cursor,
+ sortOrder: searchParams.sortOrder,
limit: searchParams.limit,
includeContent: searchParams.includeContent,
});
diff --git a/apps/web/app/api/v1/lists/[listId]/bookmarks/route.ts b/apps/web/app/api/v1/lists/[listId]/bookmarks/route.ts
index 3977413a..daf78449 100644
--- a/apps/web/app/api/v1/lists/[listId]/bookmarks/route.ts
+++ b/apps/web/app/api/v1/lists/[listId]/bookmarks/route.ts
@@ -1,14 +1,14 @@
import { NextRequest } from "next/server";
import { buildHandler } from "@/app/api/v1/utils/handler";
import { adaptPagination, zPagination } from "@/app/api/v1/utils/pagination";
-import { zGetBookmarkSearchParamsSchema } from "@/app/api/v1/utils/types";
+import { zGetBookmarkQueryParamsSchema } from "@/app/api/v1/utils/types";
export const dynamic = "force-dynamic";
export const GET = (req: NextRequest, params: { params: { listId: string } }) =>
buildHandler({
req,
- searchParamsSchema: zPagination.and(zGetBookmarkSearchParamsSchema),
+ searchParamsSchema: zPagination.and(zGetBookmarkQueryParamsSchema),
handler: async ({ api, searchParams }) => {
const bookmarks = await api.bookmarks.getBookmarks({
listId: params.params.listId,
diff --git a/apps/web/app/api/v1/tags/[tagId]/bookmarks/route.ts b/apps/web/app/api/v1/tags/[tagId]/bookmarks/route.ts
index cfc0af51..aaa5087b 100644
--- a/apps/web/app/api/v1/tags/[tagId]/bookmarks/route.ts
+++ b/apps/web/app/api/v1/tags/[tagId]/bookmarks/route.ts
@@ -1,7 +1,7 @@
import { NextRequest } from "next/server";
import { buildHandler } from "@/app/api/v1/utils/handler";
import { adaptPagination, zPagination } from "@/app/api/v1/utils/pagination";
-import { zGetBookmarkSearchParamsSchema } from "@/app/api/v1/utils/types";
+import { zGetBookmarkQueryParamsSchema } from "@/app/api/v1/utils/types";
export const dynamic = "force-dynamic";
@@ -11,10 +11,11 @@ export const GET = (
) =>
buildHandler({
req,
- searchParamsSchema: zPagination.and(zGetBookmarkSearchParamsSchema),
+ searchParamsSchema: zPagination.and(zGetBookmarkQueryParamsSchema),
handler: async ({ api, searchParams }) => {
const bookmarks = await api.bookmarks.getBookmarks({
tagId: params.tagId,
+ sortOrder: searchParams.sortOrder,
limit: searchParams.limit,
cursor: searchParams.cursor,
});
diff --git a/apps/web/app/api/v1/utils/types.ts b/apps/web/app/api/v1/utils/types.ts
index f0fe6231..bf181ce4 100644
--- a/apps/web/app/api/v1/utils/types.ts
+++ b/apps/web/app/api/v1/utils/types.ts
@@ -1,11 +1,23 @@
import { z } from "zod";
+import { zSortOrder } from "@karakeep/shared/types/bookmarks";
+
export const zStringBool = z
.string()
.refine((val) => val === "true" || val === "false", "Must be true or false")
.transform((val) => val === "true");
+export const zGetBookmarkQueryParamsSchema = z.object({
+ sortOrder: zSortOrder
+ .exclude([zSortOrder.Enum.relevance])
+ .optional()
+ .default(zSortOrder.Enum.desc),
+ // TODO: Change the default to false in a couple of releases.
+ includeContent: zStringBool.optional().default("true"),
+});
+
export const zGetBookmarkSearchParamsSchema = z.object({
+ sortOrder: zSortOrder.optional().default(zSortOrder.Enum.relevance),
// TODO: Change the default to false in a couple of releases.
includeContent: zStringBool.optional().default("true"),
});
diff --git a/packages/open-api/karakeep-openapi-spec.json b/packages/open-api/karakeep-openapi-spec.json
index 3bdaed54..dbc2e5d0 100644
--- a/packages/open-api/karakeep-openapi-spec.json
+++ b/packages/open-api/karakeep-openapi-spec.json
@@ -551,6 +551,19 @@
},
{
"schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc"
+ ],
+ "default": "desc"
+ },
+ "required": false,
+ "name": "sortOrder",
+ "in": "query"
+ },
+ {
+ "schema": {
"type": "number"
},
"required": false,
@@ -776,6 +789,20 @@
},
{
"schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc",
+ "relevance"
+ ],
+ "default": "relevance"
+ },
+ "required": false,
+ "name": "sortOrder",
+ "in": "query"
+ },
+ {
+ "schema": {
"type": "number"
},
"required": false,
@@ -1969,8 +1996,8 @@
},
"/lists/{listId}/bookmarks": {
"get": {
- "description": "Get the bookmarks in a list",
- "summary": "Get a bookmarks in a list",
+ "description": "Get bookmarks in the list",
+ "summary": "Get bookmarks in the list",
"tags": [
"Lists"
],
@@ -1985,6 +2012,19 @@
},
{
"schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc"
+ ],
+ "default": "desc"
+ },
+ "required": false,
+ "name": "sortOrder",
+ "in": "query"
+ },
+ {
+ "schema": {
"type": "number"
},
"required": false,
@@ -2367,8 +2407,8 @@
},
"/tags/{tagId}/bookmarks": {
"get": {
- "description": "Get the bookmarks with the tag",
- "summary": "Get a bookmarks with the tag",
+ "description": "Get bookmarks with the tag",
+ "summary": "Get bookmarks with the tag",
"tags": [
"Tags"
],
@@ -2383,6 +2423,19 @@
},
{
"schema": {
+ "type": "string",
+ "enum": [
+ "asc",
+ "desc"
+ ],
+ "default": "desc"
+ },
+ "required": false,
+ "name": "sortOrder",
+ "in": "query"
+ },
+ {
+ "schema": {
"type": "number"
},
"required": false,
diff --git a/packages/open-api/lib/bookmarks.ts b/packages/open-api/lib/bookmarks.ts
index e344d656..8fb0eb8c 100644
--- a/packages/open-api/lib/bookmarks.ts
+++ b/packages/open-api/lib/bookmarks.ts
@@ -9,6 +9,7 @@ import {
zBareBookmarkSchema,
zManipulatedTagSchema,
zNewBookmarkRequestSchema,
+ zSortOrder,
zUpdateBookmarksRequestSchema,
} from "@karakeep/shared/types/bookmarks";
@@ -60,6 +61,10 @@ registry.registerPath({
.object({
archived: z.boolean().optional(),
favourited: z.boolean().optional(),
+ sortOrder: zSortOrder
+ .exclude(["relevance"])
+ .optional()
+ .default(zSortOrder.Enum.desc),
})
.merge(PaginationSchema)
.merge(IncludeContentSearchParamSchema),
@@ -87,6 +92,7 @@ registry.registerPath({
query: z
.object({
q: z.string(),
+ sortOrder: zSortOrder.optional().default(zSortOrder.Enum.relevance),
})
.merge(PaginationSchema)
.merge(IncludeContentSearchParamSchema),
diff --git a/packages/open-api/lib/lists.ts b/packages/open-api/lib/lists.ts
index ab07e425..992b96c4 100644
--- a/packages/open-api/lib/lists.ts
+++ b/packages/open-api/lib/lists.ts
@@ -4,6 +4,7 @@ import {
} from "@asteasolutions/zod-to-openapi";
import { z } from "zod";
+import { zSortOrder } from "@karakeep/shared/types/bookmarks";
import {
zBookmarkListSchema,
zEditBookmarkListSchema,
@@ -190,13 +191,21 @@ registry.registerPath({
registry.registerPath({
method: "get",
path: "/lists/{listId}/bookmarks",
- description: "Get the bookmarks in a list",
- summary: "Get a bookmarks in a list",
+ description: "Get bookmarks in the list",
+ summary: "Get bookmarks in the list",
tags: ["Lists"],
security: [{ [BearerAuth.name]: [] }],
request: {
params: z.object({ listId: ListIdSchema }),
- query: PaginationSchema.merge(IncludeContentSearchParamSchema),
+ query: z
+ .object({
+ sortOrder: zSortOrder
+ .exclude(["relevance"])
+ .optional()
+ .default(zSortOrder.Enum.desc),
+ })
+ .merge(PaginationSchema)
+ .merge(IncludeContentSearchParamSchema),
},
responses: {
200: {
diff --git a/packages/open-api/lib/tags.ts b/packages/open-api/lib/tags.ts
index c51e3b84..b8136741 100644
--- a/packages/open-api/lib/tags.ts
+++ b/packages/open-api/lib/tags.ts
@@ -4,6 +4,7 @@ import {
} from "@asteasolutions/zod-to-openapi";
import { z } from "zod";
+import { zSortOrder } from "@karakeep/shared/types/bookmarks";
import {
zGetTagResponseSchema,
zUpdateTagRequestSchema,
@@ -152,13 +153,21 @@ registry.registerPath({
registry.registerPath({
method: "get",
path: "/tags/{tagId}/bookmarks",
- description: "Get the bookmarks with the tag",
- summary: "Get a bookmarks with the tag",
+ description: "Get bookmarks with the tag",
+ summary: "Get bookmarks with the tag",
tags: ["Tags"],
security: [{ [BearerAuth.name]: [] }],
request: {
params: z.object({ tagId: TagIdSchema }),
- query: PaginationSchema.merge(IncludeContentSearchParamSchema),
+ query: z
+ .object({
+ sortOrder: zSortOrder
+ .exclude(["relevance"])
+ .optional()
+ .default(zSortOrder.Enum.desc),
+ })
+ .merge(PaginationSchema)
+ .merge(IncludeContentSearchParamSchema),
},
responses: {
200: {
diff --git a/packages/open-api/package.json b/packages/open-api/package.json
index dfbb0bb6..900af481 100644
--- a/packages/open-api/package.json
+++ b/packages/open-api/package.json
@@ -19,6 +19,7 @@
"typecheck": "tsc --noEmit",
"generate": "tsx index.ts",
"format": "prettier . --ignore-path ../../.prettierignore",
+ "format:fix": "prettier . --write --ignore-path ../../.prettierignore",
"lint": "eslint ."
},
"main": "index.ts",
diff --git a/packages/open-api/tsconfig.json b/packages/open-api/tsconfig.json
index d97c8ef4..a795b96a 100644
--- a/packages/open-api/tsconfig.json
+++ b/packages/open-api/tsconfig.json
@@ -5,5 +5,5 @@
"exclude": ["node_modules"],
"compilerOptions": {
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
- },
+ }
}
diff --git a/packages/sdk/src/karakeep-api.d.ts b/packages/sdk/src/karakeep-api.d.ts
index 0b434b3d..1fa63dac 100644
--- a/packages/sdk/src/karakeep-api.d.ts
+++ b/packages/sdk/src/karakeep-api.d.ts
@@ -20,6 +20,7 @@ export interface paths {
query?: {
archived?: boolean;
favourited?: boolean;
+ sortOrder?: "asc" | "desc";
limit?: number;
cursor?: components["schemas"]["Cursor"];
/** @description If set to true, bookmark's content will be included in the response. Note, this content can be large for some bookmarks. */
@@ -135,6 +136,7 @@ export interface paths {
parameters: {
query: {
q: string;
+ sortOrder?: "asc" | "desc" | "relevance";
limit?: number;
cursor?: components["schemas"]["Cursor"];
/** @description If set to true, bookmark's content will be included in the response. Note, this content can be large for some bookmarks. */
@@ -971,12 +973,13 @@ export interface paths {
cookie?: never;
};
/**
- * Get a bookmarks in a list
- * @description Get the bookmarks in a list
+ * Get bookmarks in the list
+ * @description Get bookmarks in the list
*/
get: {
parameters: {
query?: {
+ sortOrder?: "asc" | "desc";
limit?: number;
cursor?: components["schemas"]["Cursor"];
/** @description If set to true, bookmark's content will be included in the response. Note, this content can be large for some bookmarks. */
@@ -1052,18 +1055,6 @@ export interface paths {
};
content?: never;
};
- /** @description Bookmark already in list */
- 400: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- code: string;
- message: string;
- };
- };
- };
/** @description List or bookmark not found */
404: {
headers: {
@@ -1314,12 +1305,13 @@ export interface paths {
cookie?: never;
};
/**
- * Get a bookmarks with the tag
- * @description Get the bookmarks with the tag
+ * Get bookmarks with the tag
+ * @description Get bookmarks with the tag
*/
get: {
parameters: {
query?: {
+ sortOrder?: "asc" | "desc";
limit?: number;
cursor?: components["schemas"]["Cursor"];
/** @description If set to true, bookmark's content will be included in the response. Note, this content can be large for some bookmarks. */