Skip to content

Commit 4e11c29

Browse files
committed
fix api usage
1 parent 9286f99 commit 4e11c29

2 files changed

Lines changed: 36 additions & 1 deletion

File tree

‎runtime/src/api/client.ts‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,22 @@ export class FeedbackClient {
168168
url.searchParams.set("key", key);
169169
const init: RequestInit = { headers: { Accept: "application/json" } };
170170
if (signal) init.signal = signal;
171-
const response = await this.#fetch(url, init);
171+
let response = await this.#fetch(url, init);
172+
if (response.status === 400 && await errorCode(response.clone()) === "invalid_keys") {
173+
// Older deployed APIs use `keys`; send only this one key during rollout.
174+
url.searchParams.delete("key");
175+
url.searchParams.set("keys", key);
176+
response = await this.#fetch(url, init);
177+
if (!response.ok) throw new FeedbackError(response.status, await errorCode(response));
178+
const legacy: unknown = await response.json();
179+
const items = legacy && typeof legacy === "object" ? (legacy as { items?: unknown }).items : undefined;
180+
const discussion = items && typeof items === "object"
181+
? (items as Record<string, unknown>)[key] : undefined;
182+
if (!discussion || typeof discussion !== "object" || Array.isArray(discussion)) {
183+
throw new TypeError("Invalid discussion content response");
184+
}
185+
return { discussion: discussion as Record<string, unknown>, comments: [] };
186+
}
172187
if (!response.ok) throw new FeedbackError(response.status, await errorCode(response));
173188
const payload: unknown = await response.json();
174189
if (!payload || typeof payload !== "object" || (payload as { v?: unknown }).v !== 1) {

‎runtime/test/client.test.ts‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,26 @@ void test("discussion content requests exactly one key", async () => {
3737
assert.equal(content.discussion.id, "D_example");
3838
});
3939

40+
void test("discussion content uses one-key legacy request while an older API is deployed", async () => {
41+
const requested: string[] = [];
42+
const fetch = (input: URL | RequestInfo): Promise<Response> => {
43+
const url = input instanceof Request ? input.url : input.toString();
44+
requested.push(url);
45+
return Promise.resolve(url.includes("?key=")
46+
? Response.json({ v: 1, error: { code: "invalid_keys", message: "keys required" } }, { status: 400 })
47+
: Response.json({ v: 1, items: { "feedback/example": { id: "D_example" } } }));
48+
};
49+
const client = new FeedbackClient({ apiOrigin: "https://feedback-api.cpp.social", site: "cpp-social", fetch });
50+
51+
const content = await client.discussionContent("feedback/example");
52+
53+
assert.equal(content.discussion.id, "D_example");
54+
assert.deepEqual(requested, [
55+
"https://feedback-api.cpp.social/v1/sites/cpp-social/discussion?key=feedback%2Fexample",
56+
"https://feedback-api.cpp.social/v1/sites/cpp-social/discussion?keys=feedback%2Fexample",
57+
]);
58+
});
59+
4060
void test("OAuth transport sends JSON only to the configured site", async () => {
4161
const requests: Request[] = [];
4262
const fetch = (input: URL | RequestInfo, init?: RequestInit): Promise<Response> => {

0 commit comments

Comments
 (0)