diff options
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/api/routes/tags.ts | 12 | ||||
| -rw-r--r-- | packages/e2e_tests/tests/api/tags.test.ts | 27 | ||||
| -rw-r--r-- | packages/open-api/karakeep-openapi-spec.json | 69 | ||||
| -rw-r--r-- | packages/open-api/lib/tags.ts | 33 | ||||
| -rw-r--r-- | packages/sdk/src/karakeep-api.d.ts | 40 | ||||
| -rw-r--r-- | packages/shared/types/tags.ts | 4 | ||||
| -rw-r--r-- | packages/trpc/routers/tags.ts | 36 |
7 files changed, 172 insertions, 49 deletions
diff --git a/packages/api/routes/tags.ts b/packages/api/routes/tags.ts index 6d4cf39d..816e58b4 100644 --- a/packages/api/routes/tags.ts +++ b/packages/api/routes/tags.ts @@ -1,7 +1,10 @@ import { zValidator } from "@hono/zod-validator"; import { Hono } from "hono"; -import { zUpdateTagRequestSchema } from "@karakeep/shared/types/tags"; +import { + zCreateTagRequestSchema, + zUpdateTagRequestSchema, +} from "@karakeep/shared/types/tags"; import { authMiddleware } from "../middlewares/auth"; import { adaptPagination, zPagination } from "../utils/pagination"; @@ -16,6 +19,13 @@ const app = new Hono() return c.json(tags, 200); }) + // POST /tags + .post("/", zValidator("json", zCreateTagRequestSchema), async (c) => { + const body = c.req.valid("json"); + const tags = await c.var.api.tags.create(body); + return c.json(tags, 201); + }) + // GET /tags/[tagId] .get("/:tagId", async (c) => { const tagId = c.req.param("tagId"); diff --git a/packages/e2e_tests/tests/api/tags.test.ts b/packages/e2e_tests/tests/api/tags.test.ts index 3e3cacc0..6c387628 100644 --- a/packages/e2e_tests/tests/api/tags.test.ts +++ b/packages/e2e_tests/tests/api/tags.test.ts @@ -26,31 +26,16 @@ describe("Tags API", () => { }); it("should get, update and delete a tag", async () => { - // Create a bookmark first - const { data: createdBookmark } = await client.POST("/bookmarks", { + // Create a tag by attaching it to the bookmark + const { data: tag } = await client.POST("/tags", { body: { - type: "text", - title: "Test Bookmark", - text: "This is a test bookmark", + name: "Test Tag", }, }); + expect(tag).toBeDefined(); + expect(tag!.name).toBe("Test Tag"); - // Create a tag by attaching it to the bookmark - const { data: addTagResponse } = await client.POST( - "/bookmarks/{bookmarkId}/tags", - { - params: { - path: { - bookmarkId: createdBookmark!.id, - }, - }, - body: { - tags: [{ tagName: "Test Tag" }], - }, - }, - ); - - const tagId = addTagResponse!.attached[0]; + const tagId = tag!.id; // Get the tag const { data: retrievedTag, response: getResponse } = await client.GET( diff --git a/packages/open-api/karakeep-openapi-spec.json b/packages/open-api/karakeep-openapi-spec.json index dbc2e5d0..15fa246b 100644 --- a/packages/open-api/karakeep-openapi-spec.json +++ b/packages/open-api/karakeep-openapi-spec.json @@ -2240,6 +2240,61 @@ } } } + }, + "post": { + "description": "Create a new tag", + "summary": "Create a new tag", + "tags": [ + "Tags" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "description": "The data to create the tag with.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "201": { + "description": "The created tag", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + } + } + } + } + } } }, "/tags/{tagId}": { @@ -2375,7 +2430,19 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Tag" + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] } } } diff --git a/packages/open-api/lib/tags.ts b/packages/open-api/lib/tags.ts index b8136741..0a4f62cb 100644 --- a/packages/open-api/lib/tags.ts +++ b/packages/open-api/lib/tags.ts @@ -6,7 +6,9 @@ import { z } from "zod"; import { zSortOrder } from "@karakeep/shared/types/bookmarks"; import { + zCreateTagRequestSchema, zGetTagResponseSchema, + zTagBasicSchema, zUpdateTagRequestSchema, } from "@karakeep/shared/types/tags"; @@ -57,6 +59,35 @@ registry.registerPath({ }); registry.registerPath({ + method: "post", + path: "/tags", + description: "Create a new tag", + summary: "Create a new tag", + tags: ["Tags"], + security: [{ [BearerAuth.name]: [] }], + request: { + body: { + description: "The data to create the tag with.", + content: { + "application/json": { + schema: zCreateTagRequestSchema, + }, + }, + }, + }, + responses: { + 201: { + description: "The created tag", + content: { + "application/json": { + schema: zTagBasicSchema, + }, + }, + }, + }, +}); + +registry.registerPath({ method: "get", path: "/tags/{tagId}", description: "Get tag by its id", @@ -135,7 +166,7 @@ registry.registerPath({ description: "The updated tag", content: { "application/json": { - schema: TagSchema, + schema: zTagBasicSchema, }, }, }, diff --git a/packages/sdk/src/karakeep-api.d.ts b/packages/sdk/src/karakeep-api.d.ts index 1fa63dac..e8c048c4 100644 --- a/packages/sdk/src/karakeep-api.d.ts +++ b/packages/sdk/src/karakeep-api.d.ts @@ -1158,7 +1158,40 @@ export interface paths { }; }; put?: never; - post?: never; + /** + * Create a new tag + * @description Create a new tag + */ + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description The data to create the tag with. */ + requestBody?: { + content: { + "application/json": { + name: string; + }; + }; + }; + responses: { + /** @description The created tag */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + }; + }; + }; + }; + }; delete?: never; options?: never; head?: never; @@ -1278,7 +1311,10 @@ export interface paths { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["Tag"]; + "application/json": { + id: string; + name: string; + }; }; }; /** @description Tag not found */ diff --git a/packages/shared/types/tags.ts b/packages/shared/types/tags.ts index c2d8b28d..c7a0103e 100644 --- a/packages/shared/types/tags.ts +++ b/packages/shared/types/tags.ts @@ -1,5 +1,9 @@ import { z } from "zod"; +export const zCreateTagRequestSchema = z.object({ + name: z.string().min(1), +}); + export const zAttachedByEnumSchema = z.enum(["ai", "human"]); export type ZAttachedByEnum = z.infer<typeof zAttachedByEnumSchema>; export const zBookmarkTagSchema = z.object({ diff --git a/packages/trpc/routers/tags.ts b/packages/trpc/routers/tags.ts index 7f75c16e..cade4b45 100644 --- a/packages/trpc/routers/tags.ts +++ b/packages/trpc/routers/tags.ts @@ -7,7 +7,9 @@ import { SqliteError } from "@karakeep/db"; import { bookmarkTags, tagsOnBookmarks } from "@karakeep/db/schema"; import { triggerSearchReindex } from "@karakeep/shared/queues"; import { + zCreateTagRequestSchema, zGetTagResponseSchema, + zTagBasicSchema, zUpdateTagRequestSchema, } from "@karakeep/shared/types/tags"; @@ -53,19 +55,8 @@ export const ensureTagOwnership = experimental_trpcMiddleware<{ export const tagsAppRouter = router({ create: authedProcedure - .input( - z.object({ - name: z.string().min(1), // Ensure the name is provided and not empty - }), - ) - .output( - z.object({ - id: z.string(), - name: z.string(), - userId: z.string(), - createdAt: z.date(), - }), - ) + .input(zCreateTagRequestSchema) + .output(zTagBasicSchema) .mutation(async ({ input, ctx }) => { try { const [newTag] = await ctx.db @@ -76,7 +67,10 @@ export const tagsAppRouter = router({ }) .returning(); - return newTag; + return { + id: newTag.id, + name: newTag.name, + }; } catch (e) { if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_UNIQUE") { throw new TRPCError({ @@ -195,14 +189,7 @@ export const tagsAppRouter = router({ }), update: authedProcedure .input(zUpdateTagRequestSchema) - .output( - z.object({ - id: z.string(), - name: z.string(), - userId: z.string(), - createdAt: z.date(), - }), - ) + .output(zTagBasicSchema) .use(ensureTagOwnership) .mutation(async ({ input, ctx }) => { try { @@ -242,7 +229,10 @@ export const tagsAppRouter = router({ console.error("Failed to reindex affected bookmarks", e); } - return res[0]; + return { + id: res[0].id, + name: res[0].name, + }; } catch (e) { if (e instanceof SqliteError) { if (e.code == "SQLITE_CONSTRAINT_UNIQUE") { |
