feat: add batch submit client
This commit is contained in:
parent
ff2755e218
commit
b75755e6a6
65
src/shared/batch-submit-client.ts
Normal file
65
src/shared/batch-submit-client.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import type { BatchPayload } from "../content/market/batch-payload";
|
||||||
|
import { isAuthResponseMessage } from "./auth-messages";
|
||||||
|
|
||||||
|
interface FetchResponseLike {
|
||||||
|
json(): Promise<unknown>;
|
||||||
|
ok: boolean;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type FetchLike = (
|
||||||
|
input: string,
|
||||||
|
init?: RequestInit
|
||||||
|
) => Promise<FetchResponseLike>;
|
||||||
|
|
||||||
|
type SendMessageLike = (message: unknown) => Promise<unknown>;
|
||||||
|
|
||||||
|
export function createBatchSubmitClient(options: {
|
||||||
|
baseUrl: string;
|
||||||
|
fetchImpl?: FetchLike;
|
||||||
|
sendMessage: SendMessageLike;
|
||||||
|
}) {
|
||||||
|
const fetchImpl = options.fetchImpl ?? fetch;
|
||||||
|
|
||||||
|
return {
|
||||||
|
async submitBatch(payload: BatchPayload) {
|
||||||
|
const token = await readAccessToken(options.sendMessage);
|
||||||
|
const response = await fetchImpl(
|
||||||
|
new URL("/api/mock/batches", options.baseUrl).toString(),
|
||||||
|
{
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
method: "POST"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.status === 401 || response.status === 403) {
|
||||||
|
throw new Error("batch submit unauthorized");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`batch submit failed: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readAccessToken(sendMessage: SendMessageLike): Promise<string> {
|
||||||
|
const response = await sendMessage({ type: "auth:get-access-token" });
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isAuthResponseMessage(response) ||
|
||||||
|
!response.ok ||
|
||||||
|
response.type !== "auth:token" ||
|
||||||
|
!response.value.accessToken.trim()
|
||||||
|
) {
|
||||||
|
throw new Error("batch submit token unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.value.accessToken;
|
||||||
|
}
|
||||||
82
tests/batch-submit-client.test.ts
Normal file
82
tests/batch-submit-client.test.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { describe, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
|
import { createBatchSubmitClient } from "../src/shared/batch-submit-client";
|
||||||
|
|
||||||
|
describe("batch-submit-client", () => {
|
||||||
|
test("posts the batch payload with a Bearer token", async () => {
|
||||||
|
const sendMessage = vi.fn(async () => ({
|
||||||
|
ok: true,
|
||||||
|
type: "auth:token",
|
||||||
|
value: { accessToken: "abc123" }
|
||||||
|
}));
|
||||||
|
const fetchImpl = vi.fn(async () => ({
|
||||||
|
ok: true,
|
||||||
|
status: 200,
|
||||||
|
json: async () => ({ acceptedCount: 2, ok: true })
|
||||||
|
}));
|
||||||
|
|
||||||
|
const client = createBatchSubmitClient({
|
||||||
|
baseUrl: "http://127.0.0.1:4319",
|
||||||
|
fetchImpl,
|
||||||
|
sendMessage
|
||||||
|
});
|
||||||
|
|
||||||
|
await client.submitBatch({
|
||||||
|
authors: [{ authorId: "111", authorName: "达人A" }],
|
||||||
|
batchId: "批次A-2026-04-22T12:30:00.000Z",
|
||||||
|
batchName: "批次A",
|
||||||
|
createdAt: "2026-04-22T12:30:00.000Z",
|
||||||
|
creatorName: "王少卿",
|
||||||
|
logtoUserId: "p7pdhhtde8kj",
|
||||||
|
resource: "https://talent-search.intelligrow.cn"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fetchImpl).toHaveBeenCalledWith(
|
||||||
|
"http://127.0.0.1:4319/api/mock/batches",
|
||||||
|
expect.objectContaining({
|
||||||
|
body: JSON.stringify({
|
||||||
|
authors: [{ authorId: "111", authorName: "达人A" }],
|
||||||
|
batchId: "批次A-2026-04-22T12:30:00.000Z",
|
||||||
|
batchName: "批次A",
|
||||||
|
createdAt: "2026-04-22T12:30:00.000Z",
|
||||||
|
creatorName: "王少卿",
|
||||||
|
logtoUserId: "p7pdhhtde8kj",
|
||||||
|
resource: "https://talent-search.intelligrow.cn"
|
||||||
|
}),
|
||||||
|
headers: expect.objectContaining({
|
||||||
|
Authorization: "Bearer abc123",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}),
|
||||||
|
method: "POST"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("throws on unauthorized responses", async () => {
|
||||||
|
const client = createBatchSubmitClient({
|
||||||
|
baseUrl: "http://127.0.0.1:4319",
|
||||||
|
fetchImpl: vi.fn(async () => ({
|
||||||
|
ok: false,
|
||||||
|
status: 401,
|
||||||
|
json: async () => ({ error: "unauthorized", ok: false })
|
||||||
|
})),
|
||||||
|
sendMessage: vi.fn(async () => ({
|
||||||
|
ok: true,
|
||||||
|
type: "auth:token",
|
||||||
|
value: { accessToken: "abc123" }
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
client.submitBatch({
|
||||||
|
authors: [],
|
||||||
|
batchId: "批次A-2026-04-22T12:30:00.000Z",
|
||||||
|
batchName: "批次A",
|
||||||
|
createdAt: "2026-04-22T12:30:00.000Z",
|
||||||
|
creatorName: "王少卿",
|
||||||
|
logtoUserId: "p7pdhhtde8kj",
|
||||||
|
resource: "https://talent-search.intelligrow.cn"
|
||||||
|
})
|
||||||
|
).rejects.toThrow(/unauthorized/i);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user