mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-08 01:57:31 +01:00
Chunk external object summary requests
This commit is contained in:
1 parent
f6999839c1
commit
517d5522db
2 files changed
+64
-1
No files matched your search
@@ -0,0 +1,48 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ExternalObjectSummary } from "@paperclipai/shared";
|
||||
import { externalObjectsApi } from "../api/externalObjects";
|
||||
import {
|
||||
EXTERNAL_OBJECT_SUMMARY_BATCH_SIZE,
|
||||
fetchIssueExternalObjectSummariesInBatches,
|
||||
} from "./useIssueExternalObjects";
|
||||
|
||||
vi.mock("../api/externalObjects", () => ({
|
||||
externalObjectsApi: {
|
||||
getIssueSummaries: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
const emptySummary: ExternalObjectSummary = {
|
||||
total: 0,
|
||||
byStatusCategory: {},
|
||||
byLiveness: {},
|
||||
highestSeverity: "neutral",
|
||||
objects: [],
|
||||
};
|
||||
|
||||
describe("fetchIssueExternalObjectSummariesInBatches", () => {
|
||||
afterEach(() => {
|
||||
vi.mocked(externalObjectsApi.getIssueSummaries).mockReset();
|
||||
});
|
||||
|
||||
it("chunks bulk summary requests below the server validation cap and merges results", async () => {
|
||||
const issueIds = Array.from(
|
||||
{ length: EXTERNAL_OBJECT_SUMMARY_BATCH_SIZE * 2 + 3 },
|
||||
(_entry, index) => `issue-${index}`,
|
||||
);
|
||||
vi.mocked(externalObjectsApi.getIssueSummaries).mockImplementation(async (_companyId, ids) => ({
|
||||
summaries: Object.fromEntries(ids.map((id) => [id, { ...emptySummary, total: id.endsWith("-0") ? 1 : 0 }])),
|
||||
}));
|
||||
|
||||
const result = await fetchIssueExternalObjectSummariesInBatches("company-1", issueIds);
|
||||
|
||||
expect(externalObjectsApi.getIssueSummaries).toHaveBeenCalledTimes(3);
|
||||
expect(vi.mocked(externalObjectsApi.getIssueSummaries).mock.calls.map((call) => call[1].length)).toEqual([
|
||||
EXTERNAL_OBJECT_SUMMARY_BATCH_SIZE,
|
||||
EXTERNAL_OBJECT_SUMMARY_BATCH_SIZE,
|
||||
3,
|
||||
]);
|
||||
expect(Object.keys(result.summaries)).toHaveLength(issueIds.length);
|
||||
expect(result.summaries["issue-0"]?.total).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,21 @@ import type { MarkdownExternalReferenceMap } from "../components/MarkdownBody";
|
||||
import type { ExternalObjectPillData } from "../components/ExternalObjectPill";
|
||||
import { instanceSettingsApi } from "../api/instanceSettings";
|
||||
|
||||
export const EXTERNAL_OBJECT_SUMMARY_BATCH_SIZE = 500;
|
||||
|
||||
export async function fetchIssueExternalObjectSummariesInBatches(
|
||||
companyId: string,
|
||||
issueIds: readonly string[],
|
||||
) {
|
||||
const summaries: Record<string, ExternalObjectSummary> = {};
|
||||
for (let index = 0; index < issueIds.length; index += EXTERNAL_OBJECT_SUMMARY_BATCH_SIZE) {
|
||||
const batch = issueIds.slice(index, index + EXTERNAL_OBJECT_SUMMARY_BATCH_SIZE);
|
||||
const response = await externalObjectsApi.getIssueSummaries(companyId, batch);
|
||||
Object.assign(summaries, response.summaries);
|
||||
}
|
||||
return { summaries };
|
||||
}
|
||||
|
||||
/**
|
||||
* Browser-side mention-source label. Keep in sync with the shared formatter
|
||||
* without coupling this hook to the server-only URL canonicalization helpers.
|
||||
@@ -201,7 +216,7 @@ export function useIssueExternalObjectSummaries(
|
||||
const enabled = externalObjectsFeature.isEnabled && Boolean(companyId) && normalizedIssueIds.length > 0;
|
||||
const query = useQuery({
|
||||
queryKey: queryKeys.externalObjects.issueSummaries(companyId ?? "__none__", normalizedIssueIds),
|
||||
queryFn: () => externalObjectsApi.getIssueSummaries(companyId!, normalizedIssueIds),
|
||||
queryFn: () => fetchIssueExternalObjectSummariesInBatches(companyId!, normalizedIssueIds),
|
||||
enabled,
|
||||
staleTime: 60_000,
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user