Skip to content

Commit e84893e

Browse files
committed
fix(msteams): release failed Graph collection bodies
1 parent 61f036f commit e84893e

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

extensions/msteams/src/attachments.graph.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,27 @@ const createJsonResponse = (payload: unknown, status = 200) =>
148148
new Response(JSON.stringify(payload), { status });
149149
const createGraphCollectionResponse = (value: unknown[]) => createJsonResponse({ value });
150150
const createNotFoundResponse = () => new Response("not found", { status: 404 });
151+
function cancelTrackedResponse(
152+
text: string,
153+
init: ResponseInit,
154+
): {
155+
response: Response;
156+
wasCanceled: () => boolean;
157+
} {
158+
let canceled = false;
159+
const stream = new ReadableStream<Uint8Array>({
160+
start(controller) {
161+
controller.enqueue(new TextEncoder().encode(text));
162+
},
163+
cancel() {
164+
canceled = true;
165+
},
166+
});
167+
return {
168+
response: new Response(stream, init),
169+
wasCanceled: () => canceled,
170+
};
171+
}
151172
const createRedirectResponse = (location: string, status = 302) =>
152173
new Response(null, { status, headers: { location } });
153174
const asFetchFn = (fetchFn: unknown): FetchFn => fetchFn as FetchFn;
@@ -324,6 +345,32 @@ describe("msteams graph attachments", () => {
324345

325346
it.each<GraphMediaSuccessCase>(GRAPH_MEDIA_SUCCESS_CASES)("$label", runGraphMediaSuccessCase);
326347

348+
it("cancels non-OK Graph collection bodies before returning empty hosted content", async () => {
349+
const tracked = cancelTrackedResponse("missing hosted contents", { status: 404 });
350+
const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
351+
const url = resolveRequestUrl(input);
352+
if (url === DEFAULT_MESSAGE_URL) {
353+
return createJsonResponse({ attachments: [] });
354+
}
355+
if (url === `${DEFAULT_MESSAGE_URL}/hostedContents`) {
356+
return tracked.response;
357+
}
358+
return createNotFoundResponse();
359+
});
360+
361+
const media = await downloadMSTeamsGraphMedia({
362+
messageUrl: DEFAULT_MESSAGE_URL,
363+
tokenProvider: createTokenProvider(),
364+
maxBytes: DEFAULT_MAX_BYTES,
365+
fetchFn: asFetchFn(fetchMock),
366+
resolveFn: resolvePublicHost,
367+
});
368+
369+
expect(media.media).toEqual([]);
370+
expect(media.hostedStatus).toBe(404);
371+
expect(tracked.wasCanceled()).toBe(true);
372+
});
373+
327374
it("does not forward Authorization for SharePoint redirects outside auth allowlist", async () => {
328375
const tokenProvider = createTokenProvider("top-secret-token");
329376
const escapedUrl = "https://example.com/collect";

extensions/msteams/src/attachments/graph.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ async function fetchGraphCollection(params: {
120120
try {
121121
const status = response.status;
122122
if (!response.ok) {
123+
await response.body?.cancel().catch(() => undefined);
123124
return { status, items: [] };
124125
}
125126
try {

0 commit comments

Comments
 (0)