diff --git a/web/src/api/queries.fields.test.tsx b/web/src/api/queries.fields.test.tsx new file mode 100644 index 0000000..6ed0f4c --- /dev/null +++ b/web/src/api/queries.fields.test.tsx @@ -0,0 +1,40 @@ +import { expect, test } from "vitest"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { renderHook, waitFor } from "@testing-library/react"; +import { http, HttpResponse } from "msw"; +import { server } from "../test/server"; +import { useCreateFieldDefinition } from "./queries"; + +function wrapper({ children }: { children: React.ReactNode }) { + const qc = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false } } }); + + return {children}; +} + +test("useCreateFieldDefinition POSTs the request body", async () => { + let body: unknown; + + server.use( + http.post("/api/admin/field-definitions", async ({ request }) => { + body = await request.json(); + + return HttpResponse.json({ key: "technique" }, { status: 201 }); + }), + ); + + const { result } = renderHook(() => useCreateFieldDefinition(), { wrapper }); + + result.current.mutate({ + key: "technique", + data_type: "term", + vocabulary_id: "v-technique", + authority_kind: null, + required: false, + group: "Provenance", + labels: [{ lang: "en", label: "Technique" }], + }); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + expect((body as { key: string }).key).toBe("technique"); + expect((body as { data_type: string }).data_type).toBe("term"); +}); diff --git a/web/src/api/queries.ts b/web/src/api/queries.ts index 668f6f2..0825ec9 100644 --- a/web/src/api/queries.ts +++ b/web/src/api/queries.ts @@ -315,6 +315,23 @@ export function useSearch(q: string, visibility: string | null) { }); } +type NewFieldDefinitionRequest = components["schemas"]["NewFieldDefinitionRequest"]; + +export function useCreateFieldDefinition() { + const qc = useQueryClient(); + + return useMutation({ + mutationFn: async (body: NewFieldDefinitionRequest) => { + const { data, response } = await api.POST("/api/admin/field-definitions", { body }); + + if (response.status !== 201 || !data) throw new Error("failed to create field definition"); + + return data; + }, + onSuccess: () => qc.invalidateQueries({ queryKey: ["field-definitions"] }), + }); +} + type Visibility = "draft" | "internal" | "public"; /** Error carrying the HTTP status so callers can branch 422-gate vs 409-illegal. */ diff --git a/web/src/test/handlers.ts b/web/src/test/handlers.ts index 81d0d89..f6dcfba 100644 --- a/web/src/test/handlers.ts +++ b/web/src/test/handlers.ts @@ -68,4 +68,8 @@ export const handlers = [ http.post("/api/admin/logout", () => new HttpResponse(null, { status: 204 })), http.post("/api/admin/objects/:id/visibility", () => new HttpResponse(null, { status: 204 })), + + http.post("/api/admin/field-definitions", () => + HttpResponse.json({ key: "new_field" }, { status: 201 }), + ), ];