mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-07 18:42:25 +00:00
refactor(msteams): share response release wrapper
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { responseWithRelease } from "../response-with-release.js";
|
||||
import type { MSTeamsAttachmentLike } from "./types.js";
|
||||
|
||||
type InlineImageCandidate =
|
||||
@@ -576,52 +577,6 @@ export async function resolveAndValidateIP(
|
||||
|
||||
/** Maximum number of redirects to follow in safeFetch. */
|
||||
const MAX_SAFE_REDIRECTS = 5;
|
||||
const NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);
|
||||
|
||||
function responseWithRelease(response: Response, release: () => Promise<void>): Response {
|
||||
let released = false;
|
||||
const releaseOnce = async () => {
|
||||
if (released) {
|
||||
return;
|
||||
}
|
||||
released = true;
|
||||
await release();
|
||||
};
|
||||
|
||||
if (!response.body || NULL_BODY_STATUSES.has(response.status)) {
|
||||
void releaseOnce();
|
||||
return response;
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const body = new ReadableStream<Uint8Array>({
|
||||
async pull(controller) {
|
||||
try {
|
||||
const next = await reader.read();
|
||||
if (next.done) {
|
||||
controller.close();
|
||||
await releaseOnce();
|
||||
return;
|
||||
}
|
||||
controller.enqueue(next.value);
|
||||
} catch (err) {
|
||||
await releaseOnce();
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
async cancel(reason) {
|
||||
void reader.cancel(reason).catch(() => {});
|
||||
await releaseOnce();
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: response.headers,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a URL with redirect: "manual", validating each redirect target
|
||||
* against the hostname allowlist and optional DNS-resolved IP (anti-SSRF).
|
||||
|
||||
@@ -4,13 +4,13 @@ import { fetchWithSsrFGuard, type MSTeamsConfig } from "../runtime-api.js";
|
||||
import { GRAPH_ROOT } from "./attachments/shared.js";
|
||||
import { resolveMSTeamsSdkCloudOptions } from "./cloud.js";
|
||||
import { createMSTeamsHttpError } from "./http-error.js";
|
||||
import { responseWithRelease } from "./response-with-release.js";
|
||||
import { createMSTeamsTokenProvider, loadMSTeamsSdkWithAuth } from "./sdk.js";
|
||||
import { readAccessToken } from "./token-response.js";
|
||||
import { resolveDelegatedAccessToken, resolveMSTeamsCredentials } from "./token.js";
|
||||
import { buildUserAgent } from "./user-agent.js";
|
||||
|
||||
const GRAPH_BETA = "https://graph.microsoft.com/beta";
|
||||
const NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);
|
||||
|
||||
export type GraphUser = {
|
||||
id?: string;
|
||||
@@ -31,50 +31,6 @@ type GraphChannel = {
|
||||
|
||||
export type GraphResponse<T> = { value?: T[] };
|
||||
|
||||
function responseWithRelease(response: Response, release: () => Promise<void>): Response {
|
||||
let released = false;
|
||||
const releaseOnce = async () => {
|
||||
if (released) {
|
||||
return;
|
||||
}
|
||||
released = true;
|
||||
await release();
|
||||
};
|
||||
|
||||
if (!response.body || NULL_BODY_STATUSES.has(response.status)) {
|
||||
void releaseOnce();
|
||||
return response;
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const body = new ReadableStream<Uint8Array>({
|
||||
async pull(controller) {
|
||||
try {
|
||||
const next = await reader.read();
|
||||
if (next.done) {
|
||||
controller.close();
|
||||
await releaseOnce();
|
||||
return;
|
||||
}
|
||||
controller.enqueue(next.value);
|
||||
} catch (error) {
|
||||
await releaseOnce();
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
async cancel(reason) {
|
||||
void reader.cancel(reason).catch(() => undefined);
|
||||
await releaseOnce();
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: response.headers,
|
||||
});
|
||||
}
|
||||
|
||||
export function normalizeQuery(value?: string | null): string {
|
||||
return value?.trim() ?? "";
|
||||
}
|
||||
|
||||
45
extensions/msteams/src/response-with-release.ts
Normal file
45
extensions/msteams/src/response-with-release.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
const NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);
|
||||
|
||||
export function responseWithRelease(response: Response, release: () => Promise<void>): Response {
|
||||
let released = false;
|
||||
const releaseOnce = async () => {
|
||||
if (released) {
|
||||
return;
|
||||
}
|
||||
released = true;
|
||||
await release();
|
||||
};
|
||||
|
||||
if (!response.body || NULL_BODY_STATUSES.has(response.status)) {
|
||||
void releaseOnce();
|
||||
return response;
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const body = new ReadableStream<Uint8Array>({
|
||||
async pull(controller) {
|
||||
try {
|
||||
const next = await reader.read();
|
||||
if (next.done) {
|
||||
controller.close();
|
||||
await releaseOnce();
|
||||
return;
|
||||
}
|
||||
controller.enqueue(next.value);
|
||||
} catch (error) {
|
||||
await releaseOnce();
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
async cancel(reason) {
|
||||
void reader.cancel(reason).catch(() => undefined);
|
||||
await releaseOnce();
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: response.headers,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user